Merge "Avoid per task app icons in Taskbar for desktop mode" into main
diff --git a/aconfig/launcher_overview.aconfig b/aconfig/launcher_overview.aconfig
index b299edf..e0a597c 100644
--- a/aconfig/launcher_overview.aconfig
+++ b/aconfig/launcher_overview.aconfig
@@ -71,4 +71,11 @@
metadata {
purpose: PURPOSE_BUGFIX
}
-}
\ No newline at end of file
+}
+
+flag {
+ name: "enable_expressive_dismiss_task_motion"
+ namespace: "launcher_overview"
+ description: "Enables expressive motion and animations for dismissing a task in Overview."
+ bug: "381239462"
+}
diff --git a/go/quickstep/res/values-iw/strings.xml b/go/quickstep/res/values-iw/strings.xml
index ddb8ddd..db66106 100644
--- a/go/quickstep/res/values-iw/strings.xml
+++ b/go/quickstep/res/values-iw/strings.xml
@@ -14,7 +14,7 @@
<string name="assistant_not_selected_text" msgid="3244613673884359276">"כדי להאזין לטקסט שבמסך או לתרגם אותו, צריך לבחור אפליקציית עוזר דיגיטלי ב\'הגדרות\'"</string>
<string name="assistant_not_supported_title" msgid="1675788067597484142">"צריך לשנות את העוזר הדיגיטלי כדי להשתמש בתכונה הזו"</string>
<string name="assistant_not_supported_text" msgid="1708031078549268884">"כדי להאזין לטקסט שבמסך או לתרגם אותו, צריך לשנות את אפליקציית העוזר הדיגיטלי ב\'הגדרות\'"</string>
- <string name="tooltip_listen" msgid="7634466447860989102">"צריך להקיש כאן כדי להאזין לטקסט שבמסך הזה"</string>
- <string name="tooltip_translate" msgid="4184845868901542567">"צריך להקיש כאן כדי לתרגם את הטקסט שבמסך הזה"</string>
+ <string name="tooltip_listen" msgid="7634466447860989102">"צריך ללחוץ כאן כדי להאזין לטקסט שבמסך הזה"</string>
+ <string name="tooltip_translate" msgid="4184845868901542567">"צריך ללחוץ כאן כדי לתרגם את הטקסט שבמסך הזה"</string>
<string name="toast_p2p_app_not_shareable" msgid="7229739094132131536">"אי אפשר לשתף את האפליקציה הזו"</string>
</resources>
diff --git a/quickstep/res/drawable/task_header_close_button.xml b/quickstep/res/drawable/task_header_close_button.xml
new file mode 100644
index 0000000..b409158
--- /dev/null
+++ b/quickstep/res/drawable/task_header_close_button.xml
@@ -0,0 +1,24 @@
+<!-- Copyright (C) 2025 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="18dp"
+ android:height="18dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M256,760L200,704L424,480L200,256L256,200L480,424L704,200L760,256L536,480L760,704L704,760L480,536L256,760Z"/>
+</vector>
diff --git a/quickstep/res/drawable/task_thumbnail_header_bg.xml b/quickstep/res/drawable/task_thumbnail_header_bg.xml
new file mode 100644
index 0000000..52ac1ae
--- /dev/null
+++ b/quickstep/res/drawable/task_thumbnail_header_bg.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2025 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="@color/materialColorSurfaceBright" />
+ <corners android:topLeftRadius="@dimen/task_thumbnail_header_round_corner_radius"
+ android:topRightRadius="@dimen/task_thumbnail_header_round_corner_radius"/>
+</shape>
diff --git a/quickstep/res/layout/redesigned_gesture_tutorial_fragment.xml b/quickstep/res/layout/redesigned_gesture_tutorial_fragment.xml
index b004dfd..7530c28 100644
--- a/quickstep/res/layout/redesigned_gesture_tutorial_fragment.xml
+++ b/quickstep/res/layout/redesigned_gesture_tutorial_fragment.xml
@@ -147,6 +147,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="104dp"
android:accessibilityHeading="true"
+ android:accessibilityTraversalBefore="@id/gesture_tutorial_fragment_feedback_subtitle"
android:gravity="top"
android:lineSpacingExtra="-1sp"
android:textAppearance="@style/TextAppearance.GestureTutorial.MainTitle"
@@ -161,6 +162,8 @@
android:layout_marginTop="24dp"
android:lineSpacingExtra="4sp"
android:textAppearance="@style/TextAppearance.GestureTutorial.MainSubtitle"
+ android:accessibilityTraversalAfter="@id/gesture_tutorial_fragment_feedback_title"
+ android:accessibilityTraversalBefore="@id/gesture_tutorial_fragment_action_button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/gesture_tutorial_fragment_feedback_title" />
@@ -224,6 +227,10 @@
android:layout_marginBottom="@dimen/gesture_tutorial_done_button_bottom_margin"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
+ android:clickable="true"
+ android:focusableInTouchMode="true"
+ android:accessibilityTraversalAfter="@id/gesture_tutorial_fragment_feedback_subtitle"
+ android:contentDescription="@string/gesture_tutorial_action_button_label"
android:background="@drawable/gesture_tutorial_action_button_background"
android:stateListAnimator="@null"
android:text="@string/gesture_tutorial_action_button_label"
diff --git a/quickstep/res/layout/split_instructions_view.xml b/quickstep/res/layout/split_instructions_view.xml
index a11974c..b433a59 100644
--- a/quickstep/res/layout/split_instructions_view.xml
+++ b/quickstep/res/layout/split_instructions_view.xml
@@ -29,9 +29,9 @@
android:id="@+id/split_instructions_text"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
- android:maxWidth="@dimen/split_instructions_view_max_width"
android:textColor="?androidprv:attr/textColorOnAccent"
- android:text="@string/toast_split_select_app" />
+ android:text="@string/toast_split_select_app"
+ android:layout_weight="1" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/split_instructions_text_cancel"
diff --git a/quickstep/res/layout/task_thumbnail_view_header.xml b/quickstep/res/layout/task_thumbnail_view_header.xml
new file mode 100644
index 0000000..ecc1559
--- /dev/null
+++ b/quickstep/res/layout/task_thumbnail_view_header.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright (C) 2025 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.quickstep.views.TaskThumbnailViewHeader
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@drawable/task_thumbnail_header_bg">
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/task_thumbnail_header_height"
+ android:layout_marginStart="@dimen/task_thumbnail_header_margin_edge"
+ android:layout_marginEnd="@dimen/task_thumbnail_header_margin_edge"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent">
+ <ImageView
+ android:id="@+id/header_app_icon"
+ android:contentDescription="@string/header_app_icon_description"
+ android:layout_width="@dimen/task_thumbnail_header_icon_size"
+ android:layout_height="@dimen/task_thumbnail_header_icon_size"
+ android:layout_marginEnd="@dimen/task_thumbnail_header_margin_between_views"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/header_app_title"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintHorizontal_bias="0"
+ app:layout_constraintVertical_bias="0.5"
+ app:layout_constraintHorizontal_chainStyle="spread_inside" />
+ <TextView
+ android:id="@+id/header_app_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/task_thumbnail_header_margin_between_views"
+ android:layout_marginEnd="@dimen/task_thumbnail_header_margin_between_views"
+ android:text="@string/header_default_app_title"
+ app:layout_constraintStart_toEndOf="@id/header_app_icon"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintHorizontal_bias="0"
+ app:layout_constraintVertical_bias="0.5" />
+ <ImageButton
+ android:id="@+id/header_close_button"
+ android:contentDescription="@string/header_close_icon_description"
+ android:layout_width="@dimen/task_thumbnail_header_icon_size"
+ android:layout_height="@dimen/task_thumbnail_header_icon_size"
+ android:layout_marginStart="@dimen/task_thumbnail_header_margin_between_views"
+ android:src="@drawable/task_header_close_button"
+ android:tint="@android:color/darker_gray"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintHorizontal_bias="1"
+ app:layout_constraintVertical_bias="0.5" />
+ </androidx.constraintlayout.widget.ConstraintLayout>
+</com.android.quickstep.views.TaskThumbnailViewHeader>
diff --git a/quickstep/res/values-af/strings.xml b/quickstep/res/values-af/strings.xml
index 7f8e210..cf7b938 100644
--- a/quickstep/res/values-af/strings.xml
+++ b/quickstep/res/values-af/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Programgebruikinstellings"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Vee alles uit"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Onlangse programme"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Taak is toegemaak"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 minuut"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> oor vandag"</string>
diff --git a/quickstep/res/values-am/strings.xml b/quickstep/res/values-am/strings.xml
index 3114fe0..60c04dc 100644
--- a/quickstep/res/values-am/strings.xml
+++ b/quickstep/res/values-am/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"የመተግበሪያ አጠቃቀም ቅንብሮች"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"ሁሉንም አጽዳ"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"የቅርብ ጊዜ መተግበሪያዎች"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"ተግባር ተዘግቷል"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>፣ <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 ደቂቃ"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"ዛሬ <xliff:g id="TIME">%1$s</xliff:g> ቀርቷል"</string>
diff --git a/quickstep/res/values-ar/strings.xml b/quickstep/res/values-ar/strings.xml
index 5bfdd06..dc756d4 100644
--- a/quickstep/res/values-ar/strings.xml
+++ b/quickstep/res/values-ar/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"إعدادات استخدام التطبيق"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"محو الكل"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"التطبيقات المستخدمة مؤخرًا"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"تم إغلاق المهمة."</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>، <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"أقل من دقيقة"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"يتبقى اليوم <xliff:g id="TIME">%1$s</xliff:g>."</string>
diff --git a/quickstep/res/values-as/strings.xml b/quickstep/res/values-as/strings.xml
index bce8075..ae16f96 100644
--- a/quickstep/res/values-as/strings.xml
+++ b/quickstep/res/values-as/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"এপে ব্যৱহাৰ কৰা ডেটাৰ ছেটিং"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"আটাইবোৰ মচক"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"শেহতীয়া এপ্সমূহ"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"কাৰ্য বন্ধ কৰা হ’ল"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< ১ মিনিট"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"আজি <xliff:g id="TIME">%1$s</xliff:g> বাকী আছ"</string>
diff --git a/quickstep/res/values-az/strings.xml b/quickstep/res/values-az/strings.xml
index 715c2c6..df1b828 100644
--- a/quickstep/res/values-az/strings.xml
+++ b/quickstep/res/values-az/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Tətbiq istifadə ayarları"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Hamısını silin"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Son tətbiqlər"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Tapşırıq bağlanıb"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 dəq"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Bu gün <xliff:g id="TIME">%1$s</xliff:g> qaldı"</string>
diff --git a/quickstep/res/values-b+sr+Latn/strings.xml b/quickstep/res/values-b+sr+Latn/strings.xml
index 9089e9a..bad8811 100644
--- a/quickstep/res/values-b+sr+Latn/strings.xml
+++ b/quickstep/res/values-b+sr+Latn/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Podešavanja korišćenja aplikacije"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Obriši sve"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Nedavne aplikacije"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Zadatak je zatvoren"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 min"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Još <xliff:g id="TIME">%1$s</xliff:g> danas"</string>
diff --git a/quickstep/res/values-be/strings.xml b/quickstep/res/values-be/strings.xml
index 68fe66c..f61b337 100644
--- a/quickstep/res/values-be/strings.xml
+++ b/quickstep/res/values-be/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Налады выкарыстання праграмы"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Ачысціць усё"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Нядаўнія праграмы"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Задача закрыта"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 хв"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Сёння засталося <xliff:g id="TIME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-bg/strings.xml b/quickstep/res/values-bg/strings.xml
index 046f564..12339d6 100644
--- a/quickstep/res/values-bg/strings.xml
+++ b/quickstep/res/values-bg/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Настройки за използването на приложенията"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Изчистване на всички"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Скорошни приложения"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Задачата е затворена"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 мин"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Оставащо време днес: <xliff:g id="TIME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-bn/strings.xml b/quickstep/res/values-bn/strings.xml
index 956f66d..70cd5d1 100644
--- a/quickstep/res/values-bn/strings.xml
+++ b/quickstep/res/values-bn/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"অ্যাপ ব্যবহারের সেটিংস"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"সবকিছু খালি করুন"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"সম্প্রতি ব্যবহৃত অ্যাপ"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"টাস্ক বন্ধ করা হয়েছে"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< ১ মি."</string>
<string name="time_left_for_app" msgid="3111996412933644358">"আজকে <xliff:g id="TIME">%1$s</xliff:g> বাকি আছে"</string>
diff --git a/quickstep/res/values-bs/strings.xml b/quickstep/res/values-bs/strings.xml
index d61220c..430dd9d 100644
--- a/quickstep/res/values-bs/strings.xml
+++ b/quickstep/res/values-bs/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Postavke korištenja aplikacije"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Obriši sve"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Nedavne aplikacije"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Zadatak je zatvoren"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 min"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Preostalo vrijeme: <xliff:g id="TIME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-ca/strings.xml b/quickstep/res/values-ca/strings.xml
index 4c240c5..5226ddc 100644
--- a/quickstep/res/values-ca/strings.xml
+++ b/quickstep/res/values-ca/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Configuració d\'ús d\'aplicacions"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Esborra-ho tot"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplicacions recents"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Tasca tancada"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>; <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 minut"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"temps restant avui: <xliff:g id="TIME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-cs/strings.xml b/quickstep/res/values-cs/strings.xml
index 0fbe82a..a381ac6 100644
--- a/quickstep/res/values-cs/strings.xml
+++ b/quickstep/res/values-cs/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Nastavení využití aplikací"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Vymazat vše"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Poslední aplikace"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Úkol byl zavřen"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 minuta"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"dnes zbývá: <xliff:g id="TIME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-da/strings.xml b/quickstep/res/values-da/strings.xml
index 7b9aa92..fbe81f7 100644
--- a/quickstep/res/values-da/strings.xml
+++ b/quickstep/res/values-da/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Indstillinger for appforbrug"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Ryd alt"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Seneste apps"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Opgaven er lukket"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 min"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> tilbage i dag"</string>
diff --git a/quickstep/res/values-de/strings.xml b/quickstep/res/values-de/strings.xml
index d8f4ceb..30f2cfb 100644
--- a/quickstep/res/values-de/strings.xml
+++ b/quickstep/res/values-de/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Einstellungen zur App-Nutzung"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Alle Apps schließen"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Kürzlich geöffnete Apps"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Aufgabe geschlossen"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 Min."</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Heute noch <xliff:g id="TIME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-el/strings.xml b/quickstep/res/values-el/strings.xml
index 43f2c23..ecd8604 100644
--- a/quickstep/res/values-el/strings.xml
+++ b/quickstep/res/values-el/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Ρυθμίσεις χρήσης εφαρμογής"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Διαγραφή όλων"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Πρόσφατες εφαρμογές"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Η εργασία έκλεισε"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 λ."</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Απομένουν <xliff:g id="TIME">%1$s</xliff:g> σήμερα"</string>
diff --git a/quickstep/res/values-en-rAU/strings.xml b/quickstep/res/values-en-rAU/strings.xml
index e2314b3..a799d0a 100644
--- a/quickstep/res/values-en-rAU/strings.xml
+++ b/quickstep/res/values-en-rAU/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"App usage settings"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Clear all"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Recent apps"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Task closed"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 minute"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> left today"</string>
diff --git a/quickstep/res/values-en-rCA/strings.xml b/quickstep/res/values-en-rCA/strings.xml
index de8b73a..3528cd6 100644
--- a/quickstep/res/values-en-rCA/strings.xml
+++ b/quickstep/res/values-en-rCA/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"App usage settings"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Clear all"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Recent apps"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Task Closed"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 minute"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> left today"</string>
diff --git a/quickstep/res/values-en-rGB/strings.xml b/quickstep/res/values-en-rGB/strings.xml
index e2314b3..a799d0a 100644
--- a/quickstep/res/values-en-rGB/strings.xml
+++ b/quickstep/res/values-en-rGB/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"App usage settings"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Clear all"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Recent apps"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Task closed"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 minute"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> left today"</string>
diff --git a/quickstep/res/values-en-rIN/strings.xml b/quickstep/res/values-en-rIN/strings.xml
index e2314b3..a799d0a 100644
--- a/quickstep/res/values-en-rIN/strings.xml
+++ b/quickstep/res/values-en-rIN/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"App usage settings"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Clear all"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Recent apps"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Task closed"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 minute"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> left today"</string>
diff --git a/quickstep/res/values-es-rUS/strings.xml b/quickstep/res/values-es-rUS/strings.xml
index cd8530a..f51cdea 100644
--- a/quickstep/res/values-es-rUS/strings.xml
+++ b/quickstep/res/values-es-rUS/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Configuración de uso de la app"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Cerrar todo"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Apps recientes"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Se cerró la tarea"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> (<xliff:g id="REMAINING_TIME">%2$s</xliff:g>)"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 minuto"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Tiempo restante: <xliff:g id="TIME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-es/strings.xml b/quickstep/res/values-es/strings.xml
index 662f60b..06c2d50 100644
--- a/quickstep/res/values-es/strings.xml
+++ b/quickstep/res/values-es/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Ajustes de uso de la aplicación"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Borrar todo"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplicaciones recientes"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Tarea cerrada"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> (<xliff:g id="REMAINING_TIME">%2$s</xliff:g>)"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"<1 minuto"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"tiempo restante: <xliff:g id="TIME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-et/strings.xml b/quickstep/res/values-et/strings.xml
index 0e360ec..d394c2a 100644
--- a/quickstep/res/values-et/strings.xml
+++ b/quickstep/res/values-et/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Rakenduse kasutuse seaded"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Sule kõik"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Hiljutised rakendused"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Ülesanne suleti"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 minut"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Tääna jäänud <xliff:g id="TIME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-eu/strings.xml b/quickstep/res/values-eu/strings.xml
index 4d59ed7..02ea865 100644
--- a/quickstep/res/values-eu/strings.xml
+++ b/quickstep/res/values-eu/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Aplikazioen erabileraren ezarpenak"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Garbitu guztiak"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Azkenaldiko aplikazioak"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Itxi da zeregina"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> (<xliff:g id="REMAINING_TIME">%2$s</xliff:g>)"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 min"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> gelditzen dira gaur"</string>
@@ -55,7 +54,7 @@
<string name="back_gesture_feedback_complete_with_follow_up" msgid="8653374779579748392">"Ikasi duzu atzera egiteko keinua. Jarraian, lortu aplikazioz aldatzeko argibideak."</string>
<string name="back_gesture_feedback_complete_without_follow_up" msgid="197189945858268342">"Ikasi duzu atzera egiteko keinua"</string>
<string name="back_gesture_feedback_swipe_in_nav_bar" msgid="9157480023651452969">"Ziurtatu hatza ez duzula pasatzen pantailaren behealdetik gertuegi"</string>
- <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Keinuaren sentikortasuna aldatzeko, joan ezarpenetara"</string>
+ <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Keinuaren sentikortasuna aldatzeko, joan Ezarpenak atalera"</string>
<string name="back_gesture_intro_title" msgid="19551256430224428">"Pasatu hatza atzera egiteko"</string>
<string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Aurreko pantailara itzultzeko, pasatu hatza pantailaren ezkerreko edo eskuineko ertzetik erdialdera."</string>
<string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Egin atzera"</string>
@@ -73,7 +72,7 @@
<string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Ziurtatu hatza pantailaren beheko ertzetik gora pasatzen duzula"</string>
<string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Eduki sakatuta leihoa luzaroago hatza jaso aurretik"</string>
<string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Ziurtatu hatza zuzen pasatzen duzula gora; ondoren, gelditu"</string>
- <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Ikasi duzu keinuak erabiltzen. Keinuak desaktibatzeko, joan ezarpenetara."</string>
+ <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Ikasi duzu keinuak erabiltzen. Keinuak desaktibatzeko, joan Ezarpenak atalera."</string>
<string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Ikasi duzu aplikazioz aldatzeko keinua"</string>
<string name="overview_gesture_intro_title" msgid="2902054412868489378">"Pasatu hatza aplikazioa aldatzeko"</string>
<string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Aplikazio batetik bestera joateko, pasatu hatza pantailaren behealdetik gora, eduki pantaila sakatuta eta altxatu hatza."</string>
diff --git a/quickstep/res/values-fa/strings.xml b/quickstep/res/values-fa/strings.xml
index b521e92..f2f58ce 100644
--- a/quickstep/res/values-fa/strings.xml
+++ b/quickstep/res/values-fa/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"تنظیمات استفاده از برنامه"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"پاک کردن همه"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"برنامههای اخیر"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"تکلیف بسته شد"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>، <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< ۱ دقیقه"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> باقیمانده برای امروز"</string>
diff --git a/quickstep/res/values-fi/strings.xml b/quickstep/res/values-fi/strings.xml
index 6c7ec0f..d5540f4 100644
--- a/quickstep/res/values-fi/strings.xml
+++ b/quickstep/res/values-fi/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Sovelluksen käyttöasetukset"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Poista kaikki"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Viimeisimmät sovellukset"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Tehtävä suljettu"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 min"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> jäljellä tänään"</string>
diff --git a/quickstep/res/values-fr-rCA/strings.xml b/quickstep/res/values-fr-rCA/strings.xml
index 447ac9c..c087fae 100644
--- a/quickstep/res/values-fr-rCA/strings.xml
+++ b/quickstep/res/values-fr-rCA/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Paramètres d\'utilisation de l\'appli"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Tout effacer"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Applis récentes"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Tâche fermée"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> : <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 min"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Il reste <xliff:g id="TIME">%1$s</xliff:g> aujourd\'hui"</string>
diff --git a/quickstep/res/values-fr/strings.xml b/quickstep/res/values-fr/strings.xml
index 212a4ca..ddda716 100644
--- a/quickstep/res/values-fr/strings.xml
+++ b/quickstep/res/values-fr/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Paramètres de consommation de l\'application"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Tout effacer"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Applications récentes"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Tâche fermée"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 min"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Encore <xliff:g id="TIME">%1$s</xliff:g> aujourd\'hui"</string>
diff --git a/quickstep/res/values-gl/strings.xml b/quickstep/res/values-gl/strings.xml
index d685997..ac02be0 100644
--- a/quickstep/res/values-gl/strings.xml
+++ b/quickstep/res/values-gl/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Configuración do uso de aplicacións"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Borrar todo"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Apps recentes"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Tarefa pechada"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> (<xliff:g id="REMAINING_TIME">%2$s</xliff:g>)"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"<1 min"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Tempo restante hoxe <xliff:g id="TIME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-gu/strings.xml b/quickstep/res/values-gu/strings.xml
index f815524..57551d6 100644
--- a/quickstep/res/values-gu/strings.xml
+++ b/quickstep/res/values-gu/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ઍપ વપરાશનું સેટિંગ"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"બધું સાફ કરો"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"તાજેતરની ઍપ"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"કાર્ય બંધ કર્યું"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 મિનિટ"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> આજે બાકી"</string>
diff --git a/quickstep/res/values-hi/strings.xml b/quickstep/res/values-hi/strings.xml
index 49fe0cd..4d6a91b 100644
--- a/quickstep/res/values-hi/strings.xml
+++ b/quickstep/res/values-hi/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ऐप्लिकेशन इस्तेमाल की सेटिंग"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"सभी हटाएं"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"हाल ही में इस्तेमाल किए गए ऐप्लिकेशन"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"टास्क बंद किया गया"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"<1 मिनट"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"आज <xliff:g id="TIME">%1$s</xliff:g> और चलेगा"</string>
diff --git a/quickstep/res/values-hr/strings.xml b/quickstep/res/values-hr/strings.xml
index 83dde25..dc66c49 100644
--- a/quickstep/res/values-hr/strings.xml
+++ b/quickstep/res/values-hr/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Postavke upotrebe aplikacija"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Izbriši sve"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Nedavne aplikacije"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Zadatak je zatvoren"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 min"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Još <xliff:g id="TIME">%1$s</xliff:g> danas"</string>
diff --git a/quickstep/res/values-hu/strings.xml b/quickstep/res/values-hu/strings.xml
index 95e153b..4a2b9d2 100644
--- a/quickstep/res/values-hu/strings.xml
+++ b/quickstep/res/values-hu/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Alkalmazáshasználati beállítások"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Összes törlése"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Legutóbbi alkalmazások"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"A feladat bezárult"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 perc"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Ma még <xliff:g id="TIME">%1$s</xliff:g> van hátra"</string>
diff --git a/quickstep/res/values-hy/strings.xml b/quickstep/res/values-hy/strings.xml
index 519d9d4..b7e2800 100644
--- a/quickstep/res/values-hy/strings.xml
+++ b/quickstep/res/values-hy/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Հավելվածի օգտագործման կարգավորումներ"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Փակել բոլորը"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Վերջին հավելվածներ"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Առաջադրանքը փակված է"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 ր"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Այսօր մնացել է՝ <xliff:g id="TIME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-in/strings.xml b/quickstep/res/values-in/strings.xml
index b8722c3..243b428 100644
--- a/quickstep/res/values-in/strings.xml
+++ b/quickstep/res/values-in/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Setelan penggunaan aplikasi"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Hapus semua"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplikasi terbaru"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Tugas Ditutup"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 menit"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> tersisa hari ini"</string>
diff --git a/quickstep/res/values-is/strings.xml b/quickstep/res/values-is/strings.xml
index c1ab22b..8265027 100644
--- a/quickstep/res/values-is/strings.xml
+++ b/quickstep/res/values-is/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Notkunarstillingar forrits"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Hreinsa allt"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Nýleg forrit"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Verkefni lokað"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 mín."</string>
<string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> eftir í dag"</string>
diff --git a/quickstep/res/values-it/strings.xml b/quickstep/res/values-it/strings.xml
index cda024b..e7771d9 100644
--- a/quickstep/res/values-it/strings.xml
+++ b/quickstep/res/values-it/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Impostazioni di utilizzo delle app"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Cancella tutto"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"App recenti"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Attività chiusa"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 min"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Rimanente oggi: <xliff:g id="TIME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-iw/strings.xml b/quickstep/res/values-iw/strings.xml
index 664f8e8..ae84699 100644
--- a/quickstep/res/values-iw/strings.xml
+++ b/quickstep/res/values-iw/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"הגדרות שימוש באפליקציה"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"ניקוי הכול"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"אפליקציות אחרונות"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"המשימה סגורה"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< דקה"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"הזמן שנותר להיום: <xliff:g id="TIME">%1$s</xliff:g>"</string>
@@ -88,7 +87,7 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"מדריך <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"הכול מוכן!"</string>
<string name="allset_hint" msgid="459504134589971527">"כדי לחזור לדף הבית, צריך להחליק למעלה"</string>
- <string name="allset_button_hint" msgid="2395219947744706291">"כדי לעבור אל מסך הבית צריך להקיש על הלחצן הראשי"</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"כדי לעבור אל מסך הבית צריך ללחוץ על הכפתור הראשי"</string>
<string name="allset_description_generic" msgid="5385500062202019855">"הכול מוכן ואפשר להתחיל להשתמש ב<xliff:g id="DEVICE">%1$s</xliff:g>"</string>
<string name="default_device_name" msgid="6660656727127422487">"מכשיר"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"הגדרות הניווט במערכת"</annotation></string>
@@ -96,7 +95,7 @@
<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="toast_split_select_app" msgid="8464310533320556058">"צריך להקיש על אפליקציה אחרת כדי להשתמש במסך מפוצל"</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="1939025102486630426">"ביטול"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"יציאה מתצוגת מסך מפוצל"</string>
diff --git a/quickstep/res/values-ja/strings.xml b/quickstep/res/values-ja/strings.xml
index 3df5c00..3be39b9 100644
--- a/quickstep/res/values-ja/strings.xml
+++ b/quickstep/res/values-ja/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"アプリの使用状況の設定"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"すべてクリア"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"最近使ったアプリ"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"タスクを閉じました"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>、<xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"1 分未満"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"今日はあと <xliff:g id="TIME">%1$s</xliff:g>です"</string>
diff --git a/quickstep/res/values-ka/strings.xml b/quickstep/res/values-ka/strings.xml
index d0db915..d6797d0 100644
--- a/quickstep/res/values-ka/strings.xml
+++ b/quickstep/res/values-ka/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"აპების გამოყენების პარამეტრები"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"ყველას გასუფთავება"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"ბოლოდროინდელი აპები"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"ამოცანა დაიხურა"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 წუთი"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"დღეს დარჩენილია <xliff:g id="TIME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-kk/strings.xml b/quickstep/res/values-kk/strings.xml
index 850ed46..632ed16 100644
--- a/quickstep/res/values-kk/strings.xml
+++ b/quickstep/res/values-kk/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Қолданбаны пайдалану параметрлері"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Барлығын өшіру"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Соңғы қолданбалар"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Тапсырма жабылды."</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 мин"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Бүгін <xliff:g id="TIME">%1$s</xliff:g> қалды"</string>
diff --git a/quickstep/res/values-km/strings.xml b/quickstep/res/values-km/strings.xml
index 6da129d..b5158db 100644
--- a/quickstep/res/values-km/strings.xml
+++ b/quickstep/res/values-km/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ការកំណត់ការប្រើប្រាស់កម្មវិធី"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"សម្អាតទាំងអស់"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"កម្មវិធីថ្មីៗ"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"បានបិទកិច្ចការ"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 នាទី"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"នៅសល់ <xliff:g id="TIME">%1$s</xliff:g> ទៀតនៅថ្ងៃនេះ"</string>
diff --git a/quickstep/res/values-kn/strings.xml b/quickstep/res/values-kn/strings.xml
index aa38af2..438bbf0 100644
--- a/quickstep/res/values-kn/strings.xml
+++ b/quickstep/res/values-kn/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ಆ್ಯಪ್ ಬಳಕೆಯ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"ಎಲ್ಲವನ್ನೂ ತೆರವುಗೊಳಿಸಿ"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"ಇತ್ತೀಚಿನ ಅಪ್ಲಿಕೇಶನ್ಗಳು"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"ಕಾರ್ಯವನ್ನು ಮುಚ್ಚಲಾಗಿದೆ"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 ನಿ"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"ಇಂದು <xliff:g id="TIME">%1$s</xliff:g> ಸಮಯ ಉಳಿದಿದೆ"</string>
diff --git a/quickstep/res/values-ko/strings.xml b/quickstep/res/values-ko/strings.xml
index 29c9478..b50bd03 100644
--- a/quickstep/res/values-ko/strings.xml
+++ b/quickstep/res/values-ko/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"앱 사용 설정"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"모두 삭제"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"최근 앱"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"작업 종료됨"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1분"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"오늘 <xliff:g id="TIME">%1$s</xliff:g> 남음"</string>
diff --git a/quickstep/res/values-ky/strings.xml b/quickstep/res/values-ky/strings.xml
index b59cefa..f9ac3ca 100644
--- a/quickstep/res/values-ky/strings.xml
+++ b/quickstep/res/values-ky/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Колдонмону пайдалануу параметрлери"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Баарын тазалоо"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Акыркы колдонмолор"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Тапшырма жабылды"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 мүнөт"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Бүгүн <xliff:g id="TIME">%1$s</xliff:g> мүнөт калды"</string>
diff --git a/quickstep/res/values-lo/strings.xml b/quickstep/res/values-lo/strings.xml
index 985f353..ec1622f 100644
--- a/quickstep/res/values-lo/strings.xml
+++ b/quickstep/res/values-lo/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ການຕັ້ງຄ່າການນຳໃຊ້ແອັບ"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"ລຶບລ້າງທັງໝົດ"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"ແອັບຫຼ້າສຸດ"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"ປິດໜ້າວຽກແລ້ວ"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 ນາທີ"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"ເຫຼືອ <xliff:g id="TIME">%1$s</xliff:g> ມື້ນີ້"</string>
diff --git a/quickstep/res/values-lt/strings.xml b/quickstep/res/values-lt/strings.xml
index e69c2dc..cc44886 100644
--- a/quickstep/res/values-lt/strings.xml
+++ b/quickstep/res/values-lt/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Programos naudojimo nustatymai"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Išvalyti viską"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Naujausios programos"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Užduotis uždaryta"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 min."</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Šiandien liko: <xliff:g id="TIME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-lv/strings.xml b/quickstep/res/values-lv/strings.xml
index f9401ee..c57db9c 100644
--- a/quickstep/res/values-lv/strings.xml
+++ b/quickstep/res/values-lv/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Lietotņu izmantošanas iestatījumi"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Notīrīt visu"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Pēdējās izmantotās lietotnes"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Uzdevums ir aizvērts"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"<1 minūte"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Šodien atlicis: <xliff:g id="TIME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-mk/strings.xml b/quickstep/res/values-mk/strings.xml
index d9b6143..186153d 100644
--- a/quickstep/res/values-mk/strings.xml
+++ b/quickstep/res/values-mk/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Поставки за користење на апликациите"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Избриши ги сите"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Неодамнешни апликации"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Задачата е затворена"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 минута"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Уште <xliff:g id="TIME">%1$s</xliff:g> за денес"</string>
diff --git a/quickstep/res/values-ml/strings.xml b/quickstep/res/values-ml/strings.xml
index 9a2fa6e..b25ce07 100644
--- a/quickstep/res/values-ml/strings.xml
+++ b/quickstep/res/values-ml/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ആപ്പ് ഉപയോഗ ക്രമീകരണം"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"എല്ലാം മായ്ക്കുക"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"സമീപകാല ആപ്പുകൾ"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"ടാസ്ക്ക് അടച്ചു"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 മിനിറ്റ്"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"ഇന്ന് <xliff:g id="TIME">%1$s</xliff:g> ശേഷിക്കുന്നു"</string>
diff --git a/quickstep/res/values-mn/strings.xml b/quickstep/res/values-mn/strings.xml
index dda82a9..b45bfa4 100644
--- a/quickstep/res/values-mn/strings.xml
+++ b/quickstep/res/values-mn/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Апп ашиглалтын тохиргоо"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Бүгдийг арилгах"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Саяхны аппууд"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Ажлыг хаасан"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 минут"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Өнөөдөр <xliff:g id="TIME">%1$s</xliff:g> үлдсэн"</string>
diff --git a/quickstep/res/values-mr/strings.xml b/quickstep/res/values-mr/strings.xml
index 00c3be0..06b8bd8 100644
--- a/quickstep/res/values-mr/strings.xml
+++ b/quickstep/res/values-mr/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"अॅप वापर सेटिंग्ज"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"सर्व साफ करा"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"अलीकडील अॅप्स"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"टास्क बंद केली"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"१मिहून कमी"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"आज <xliff:g id="TIME">%1$s</xliff:g>शिल्लक आहे"</string>
diff --git a/quickstep/res/values-ms/strings.xml b/quickstep/res/values-ms/strings.xml
index 6d67b97..82839e2 100644
--- a/quickstep/res/values-ms/strings.xml
+++ b/quickstep/res/values-ms/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Tetapan penggunaan apl"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Kosongkan semua"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Apl terbaharu"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Tugas Ditutup"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 minit"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> lagi hari ini"</string>
diff --git a/quickstep/res/values-my/strings.xml b/quickstep/res/values-my/strings.xml
index a477c3b..2951a09 100644
--- a/quickstep/res/values-my/strings.xml
+++ b/quickstep/res/values-my/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"အက်ပ်အသုံးပြုမှု ဆက်တင်များ"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"အားလုံးရှင်းရန်"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"မကြာသေးမီက အက်ပ်များ"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"လုပ်ဆောင်စရာ ပိတ်ထားသည်"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>၊ <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< ၁ မိနစ်"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"ယနေ့ <xliff:g id="TIME">%1$s</xliff:g> ခု ကျန်သည်"</string>
diff --git a/quickstep/res/values-nb/strings.xml b/quickstep/res/values-nb/strings.xml
index 763a6b7..c485ecf 100644
--- a/quickstep/res/values-nb/strings.xml
+++ b/quickstep/res/values-nb/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Innstillinger for appbruk"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Fjern alt"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Nylige apper"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Oppgaven er lukket"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 minutt"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> gjenstår i dag"</string>
diff --git a/quickstep/res/values-ne/strings.xml b/quickstep/res/values-ne/strings.xml
index c23ba58..4d0e82a 100644
--- a/quickstep/res/values-ne/strings.xml
+++ b/quickstep/res/values-ne/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"एपको उपयोगका सेटिङहरू"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"सबै मेटाउनुहोस्"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"हालसालैका एपहरू"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"कार्य बन्द गरियो"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< १ मिनेट"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"आज: <xliff:g id="TIME">%1$s</xliff:g> बाँकी"</string>
diff --git a/quickstep/res/values-nl/strings.xml b/quickstep/res/values-nl/strings.xml
index dd4a148..20b132f 100644
--- a/quickstep/res/values-nl/strings.xml
+++ b/quickstep/res/values-nl/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Instellingen voor app-gebruik"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Alles wissen"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Recente apps"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Taak gesloten"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 minuut"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Nog <xliff:g id="TIME">%1$s</xliff:g> vandaag"</string>
diff --git a/quickstep/res/values-or/strings.xml b/quickstep/res/values-or/strings.xml
index 0802a8b..fe71252 100644
--- a/quickstep/res/values-or/strings.xml
+++ b/quickstep/res/values-or/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ଆପ ବ୍ୟବହାର ସେଟିଂସ"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"ସବୁ ଖାଲି କରନ୍ତୁ"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"ବର୍ତ୍ତମାନର ଆପ୍"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"ଟାସ୍କ ବନ୍ଦ ହୋଇଯାଇଛି"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 ମିନିଟ୍"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"ଆଜି <xliff:g id="TIME">%1$s</xliff:g> ବାକି ଅଛି"</string>
diff --git a/quickstep/res/values-pa/strings.xml b/quickstep/res/values-pa/strings.xml
index 10efa76..73b971f 100644
--- a/quickstep/res/values-pa/strings.xml
+++ b/quickstep/res/values-pa/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ਐਪ ਵਰਤੋਂ ਦੀਆਂ ਸੈਟਿੰਗਾਂ"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"ਸਭ ਕਲੀਅਰ ਕਰੋ"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"ਹਾਲੀਆ ਐਪਾਂ"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"ਕਾਰਜ ਬੰਦ"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 ਮਿੰਟ"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"ਅੱਜ <xliff:g id="TIME">%1$s</xliff:g> ਬਾਕੀ"</string>
diff --git a/quickstep/res/values-pl/strings.xml b/quickstep/res/values-pl/strings.xml
index 46a4ae6..4eb9c45 100644
--- a/quickstep/res/values-pl/strings.xml
+++ b/quickstep/res/values-pl/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Ustawienia użycia aplikacji"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Wyczyść wszystko"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Ostatnie aplikacje"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Zadanie zamknięte"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"> 1 min"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Na dziś zostało <xliff:g id="TIME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-pt-rPT/strings.xml b/quickstep/res/values-pt-rPT/strings.xml
index b3da02a..a2e3a43 100644
--- a/quickstep/res/values-pt-rPT/strings.xml
+++ b/quickstep/res/values-pt-rPT/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Definições de utilização de aplicações"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Limpar tudo"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Apps recentes"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Tarefa fechada"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 minuto"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Resta(m) <xliff:g id="TIME">%1$s</xliff:g> hoje."</string>
diff --git a/quickstep/res/values-pt/strings.xml b/quickstep/res/values-pt/strings.xml
index c7fface..f7022ec 100644
--- a/quickstep/res/values-pt/strings.xml
+++ b/quickstep/res/values-pt/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Configurações de uso do app"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Remover tudo"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Apps recentes"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Tarefa encerrada"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 min"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> restante(s) hoje"</string>
diff --git a/quickstep/res/values-ro/strings.xml b/quickstep/res/values-ro/strings.xml
index 2f610a9..4ad6846 100644
--- a/quickstep/res/values-ro/strings.xml
+++ b/quickstep/res/values-ro/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Setări de utilizare a aplicației"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Șterge tot"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplicații recente"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Activitatea s-a încheiat"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 minut"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Au mai rămas <xliff:g id="TIME">%1$s</xliff:g> astăzi"</string>
diff --git a/quickstep/res/values-ru/strings.xml b/quickstep/res/values-ru/strings.xml
index f359ab3..2a52261 100644
--- a/quickstep/res/values-ru/strings.xml
+++ b/quickstep/res/values-ru/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Настройки использования приложения"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Очистить все"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Недавние приложения"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Задача закрыта"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>: <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 мин."</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Осталось сегодня: <xliff:g id="TIME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-si/strings.xml b/quickstep/res/values-si/strings.xml
index 94d9086..bb95988 100644
--- a/quickstep/res/values-si/strings.xml
+++ b/quickstep/res/values-si/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"යෙදුම් භාවිත සැකසීම්"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"සියල්ල හිස් කරන්න"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"මෑත යෙදුම්"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"කාර්යය අවසන් කරන ලදි"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 විනාඩියක්"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"අද <xliff:g id="TIME">%1$s</xliff:g>ක් ඉතුරුයි"</string>
diff --git a/quickstep/res/values-sk/strings.xml b/quickstep/res/values-sk/strings.xml
index 4d302bc..bd22b29 100644
--- a/quickstep/res/values-sk/strings.xml
+++ b/quickstep/res/values-sk/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Nastavenia využívania aplikácie"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Vymazať všetko"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Nedávne aplikácie"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Úloha bola zavretá"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"Menej ako 1 minúta"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Dnes ešte zostáva: <xliff:g id="TIME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-sl/strings.xml b/quickstep/res/values-sl/strings.xml
index 2b97af3..740d95e 100644
--- a/quickstep/res/values-sl/strings.xml
+++ b/quickstep/res/values-sl/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Nastavitve uporabe aplikacij"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Počisti vse"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Nedavne aplikacije"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Opravilo je zaprto"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 min"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Danes je ostalo še <xliff:g id="TIME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-sq/strings.xml b/quickstep/res/values-sq/strings.xml
index e59d34f..12d824f 100644
--- a/quickstep/res/values-sq/strings.xml
+++ b/quickstep/res/values-sq/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Cilësimet e përdorimit të aplikacionit"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Pastroji të gjitha"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplikacionet e fundit"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Detyra u mbyll"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 minutë"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> të mbetura sot"</string>
diff --git a/quickstep/res/values-sr/strings.xml b/quickstep/res/values-sr/strings.xml
index 2952e5b..b6830aa 100644
--- a/quickstep/res/values-sr/strings.xml
+++ b/quickstep/res/values-sr/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Подешавања коришћења апликације"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Обриши све"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Недавне апликације"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Задатак је затворен"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 мин"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Још <xliff:g id="TIME">%1$s</xliff:g> данас"</string>
diff --git a/quickstep/res/values-sv/strings.xml b/quickstep/res/values-sv/strings.xml
index 2df4fe9..c752a79 100644
--- a/quickstep/res/values-sv/strings.xml
+++ b/quickstep/res/values-sv/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Inställningar för appanvändning"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Rensa alla"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Senaste apparna"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Uppgiften har stängts"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 min"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> kvar i dag"</string>
diff --git a/quickstep/res/values-sw/strings.xml b/quickstep/res/values-sw/strings.xml
index 6bff9a5..05d120c 100644
--- a/quickstep/res/values-sw/strings.xml
+++ b/quickstep/res/values-sw/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Mipangilio ya matumizi ya programu"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Ondoa zote"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Programu za hivi karibuni"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Jukumu Limefungwa"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< dak 1"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Umebakisha <xliff:g id="TIME">%1$s</xliff:g> leo"</string>
diff --git a/quickstep/res/values-sw600dp-land/dimens.xml b/quickstep/res/values-sw600dp-land/dimens.xml
index 49239aa..0052a73 100644
--- a/quickstep/res/values-sw600dp-land/dimens.xml
+++ b/quickstep/res/values-sw600dp-land/dimens.xml
@@ -27,12 +27,6 @@
<dimen name="gesture_tutorial_menu_done_button_top_spacing">40dp</dimen>
<dimen name="gesture_tutorial_menu_back_shape_bottom_margin">49dp</dimen>
- <!-- Grid Only Overview -->
- <!-- The top margin above the top row of tasks in grid only overview -->
- <dimen name="overview_top_margin_grid_only">24dp</dimen>
- <!-- The bottom margin above the bottom row of tasks in grid only overview -->
- <dimen name="overview_bottom_margin_grid_only">40dp</dimen>
-
<dimen name="taskbar_suw_insets">24dp</dimen>
</resources>
diff --git a/quickstep/res/values-sw600dp/dimens.xml b/quickstep/res/values-sw600dp/dimens.xml
index e24d8fe..4996582 100644
--- a/quickstep/res/values-sw600dp/dimens.xml
+++ b/quickstep/res/values-sw600dp/dimens.xml
@@ -33,10 +33,6 @@
<dimen name="overview_page_spacing">36dp</dimen>
<!-- The space to the left and to the right of the "Clear all" button -->
<dimen name="overview_grid_side_margin">64dp</dimen>
- <!-- The top margin above the top row of tasks in grid only overview -->
- <dimen name="overview_top_margin_grid_only">80dp</dimen>
- <!-- The bottom margin above the bottom row of tasks in grid only overview -->
- <dimen name="overview_bottom_margin_grid_only">80dp</dimen>
<!-- Overview actions -->
<dimen name="overview_actions_top_margin">24dp</dimen>
@@ -44,9 +40,4 @@
<dimen name="allset_page_margin_horizontal">120dp</dimen>
<dimen name="allset_page_allset_text_size">38sp</dimen>
<dimen name="allset_page_swipe_up_text_size">15sp</dimen>
-
- <!-- Splitscreen -->
- <!-- Max width of the split instructions view -->
- <dimen name="split_instructions_view_max_width">300dp</dimen>
-
</resources>
diff --git a/quickstep/res/values-ta/strings.xml b/quickstep/res/values-ta/strings.xml
index 2d2aed0..75ef7f0 100644
--- a/quickstep/res/values-ta/strings.xml
+++ b/quickstep/res/values-ta/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ஆப்ஸ் உபயோக அமைப்புகள்"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"எல்லாம் அழி"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"சமீபத்திய ஆப்ஸ்"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"பணி முடிந்தது"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 நி"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"இன்று <xliff:g id="TIME">%1$s</xliff:g> மீதமுள்ளது"</string>
diff --git a/quickstep/res/values-te/strings.xml b/quickstep/res/values-te/strings.xml
index 4ab0cd5..ac5479e 100644
--- a/quickstep/res/values-te/strings.xml
+++ b/quickstep/res/values-te/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"యాప్ వినియోగ సెట్టింగ్లు"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"అన్నీ తీసివేయండి"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"ఇటీవలి యాప్లు"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"టాస్క్ మూసివేయబడింది"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 నిమిషం"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"నేటికి <xliff:g id="TIME">%1$s</xliff:g> మిగిలి ఉంది"</string>
diff --git a/quickstep/res/values-th/strings.xml b/quickstep/res/values-th/strings.xml
index e858a82..c9dadd7 100644
--- a/quickstep/res/values-th/strings.xml
+++ b/quickstep/res/values-th/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"การตั้งค่าการใช้แอป"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"ล้างทั้งหมด"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"แอปล่าสุด"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"ปิดงานแล้ว"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"<1 นาที"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"วันนี้เหลืออีก <xliff:g id="TIME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-tl/strings.xml b/quickstep/res/values-tl/strings.xml
index 3126775..c72e38b 100644
--- a/quickstep/res/values-tl/strings.xml
+++ b/quickstep/res/values-tl/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Mga setting ng paggamit ng app"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"I-clear lahat"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Mga kamakailang app"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Isinara ang Gawain"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 min"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> na lang ngayon"</string>
diff --git a/quickstep/res/values-tr/strings.xml b/quickstep/res/values-tr/strings.xml
index edff6d7..645ef54 100644
--- a/quickstep/res/values-tr/strings.xml
+++ b/quickstep/res/values-tr/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Uygulama kullanım ayarları"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Tümünü temizle"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Son uygulamalar"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Görev Kapatıldı"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 dk."</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Bugün <xliff:g id="TIME">%1$s</xliff:g> kaldı"</string>
diff --git a/quickstep/res/values-uk/strings.xml b/quickstep/res/values-uk/strings.xml
index 2b87c22..8629d49 100644
--- a/quickstep/res/values-uk/strings.xml
+++ b/quickstep/res/values-uk/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Налаштування використання додатка"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Очистити все"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Нещодавні додатки"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Завдання закрито"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 хв"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Сьогодні залишилося <xliff:g id="TIME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-ur/strings.xml b/quickstep/res/values-ur/strings.xml
index 28f7872..3ca1fb8 100644
--- a/quickstep/res/values-ur/strings.xml
+++ b/quickstep/res/values-ur/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ایپ کے استعمال کی ترتیبات"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"سبھی کو صاف کریں"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"حالیہ ایپس"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"ٹاسک بند ہے"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>،<xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 منٹ"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"آج <xliff:g id="TIME">%1$s</xliff:g> بچا ہے"</string>
diff --git a/quickstep/res/values-uz/strings.xml b/quickstep/res/values-uz/strings.xml
index 4335d99..b4c2ef1 100644
--- a/quickstep/res/values-uz/strings.xml
+++ b/quickstep/res/values-uz/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Ilovadan foydalanish sozlamalari"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Hammasini tozalash"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Oxirgi ilovalar"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Vazifalar yopildi"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 daqiqa"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Bugun <xliff:g id="TIME">%1$s</xliff:g> qoldi"</string>
diff --git a/quickstep/res/values-vi/strings.xml b/quickstep/res/values-vi/strings.xml
index 297b96a..a27b053 100644
--- a/quickstep/res/values-vi/strings.xml
+++ b/quickstep/res/values-vi/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Cài đặt mức sử dụng ứng dụng"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Xóa tất cả"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Ứng dụng gần đây"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Đã đóng tác vụ"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 phút"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Hôm nay còn <xliff:g id="TIME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-zh-rCN/strings.xml b/quickstep/res/values-zh-rCN/strings.xml
index 0843c38..048d675 100644
--- a/quickstep/res/values-zh-rCN/strings.xml
+++ b/quickstep/res/values-zh-rCN/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"应用使用设置"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"全部清除"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"最近用过的应用"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"任务已关闭"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>(<xliff:g id="REMAINING_TIME">%2$s</xliff:g>)"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"不到 1 分钟"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"今天还可使用 <xliff:g id="TIME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-zh-rHK/strings.xml b/quickstep/res/values-zh-rHK/strings.xml
index ac6633c..f1127da 100644
--- a/quickstep/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-zh-rHK/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"應用程式使用情況設定"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"全部清除"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"最近使用的應用程式"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"閂咗工作"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>,<xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"少於 1 分鐘"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"今天剩餘時間:<xliff:g id="TIME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-zh-rTW/strings.xml b/quickstep/res/values-zh-rTW/strings.xml
index 605df7e..b524414 100644
--- a/quickstep/res/values-zh-rTW/strings.xml
+++ b/quickstep/res/values-zh-rTW/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"應用程式使用情況設定"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"全部清除"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"最近使用的應用程式"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"工作已關閉"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> (<xliff:g id="REMAINING_TIME">%2$s</xliff:g>)"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 分鐘"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"今天還能使用 <xliff:g id="TIME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-zu/strings.xml b/quickstep/res/values-zu/strings.xml
index b205fad..4340857 100644
--- a/quickstep/res/values-zu/strings.xml
+++ b/quickstep/res/values-zu/strings.xml
@@ -28,7 +28,6 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Izilungiselelo zokusetshenziswa kohlelo lokusebenza"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Sula konke"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Izinhlelo zokusebenza zakamuva"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Umsebenzi Uvaliwe"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 iminithi"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> esele namhlanje"</string>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index b404bb5..493a5b8 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -34,9 +34,6 @@
<!-- Overview Task Views -->
<!-- The thumbnail uses up to this much of the total screen height/width in Overview -->
<item name="overview_max_scale" format="float" type="dimen">0.7</item>
- <!-- The thumbnail should not go smaller than this much of the total screen height/width in
- tablet app to Overview carousel -->
- <item name="overview_carousel_min_scale" format="float" type="dimen">0.46</item>
<!-- A touch target for icons, sometimes slightly larger than the icons themselves -->
<dimen name="task_thumbnail_icon_size">48dp</dimen>
<!-- The icon size for the focused task, placed in center of touch target -->
@@ -84,6 +81,12 @@
<!-- The size of the icon menu's icon touch target -->
<dimen name="task_thumbnail_icon_menu_drawable_touch_size">44dp</dimen>
<dimen name="task_thumbnail_icon_menu_elevation">4dp</dimen>
+ <!-- The size of the task thumbnail header -->
+ <dimen name="task_thumbnail_header_height">30dp</dimen>
+ <dimen name="task_thumbnail_header_margin_edge">18dp</dimen>
+ <dimen name="task_thumbnail_header_margin_between_views">9dp</dimen>
+ <dimen name="task_thumbnail_header_icon_size">18dp</dimen>
+ <dimen name="task_thumbnail_header_round_corner_radius">16dp</dimen>
<dimen name="task_icon_cache_default_icon_size">72dp</dimen>
<item name="overview_modal_max_scale" format="float" type="dimen">1.1</item>
@@ -465,6 +468,7 @@
<dimen name="bubblebar_icon_spacing_persistent_taskbar">@dimen/bubblebar_icon_spacing</dimen>
<dimen name="bubblebar_expanded_icon_spacing">12dp</dimen>
<dimen name="bubblebar_icon_elevation">1dp</dimen>
+ <dimen name="bubblebar_transient_taskbar_min_distance">12dp</dimen>
<!-- Bubble bar dismiss view -->
<dimen name="bubblebar_dismiss_target_size">@dimen/floating_dismiss_background_size</dimen>
@@ -522,9 +526,4 @@
<!-- Digital Wellbeing -->
<dimen name="digital_wellbeing_toast_height">48dp</dimen>
-
- <!-- Splitscreen -->
- <!-- Max width of the split instructions view -->
- <dimen name="split_instructions_view_max_width">220dp</dimen>
-
</resources>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index df949c3..324ea31 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -49,9 +49,6 @@
<!-- Accessibility title for the list of recent apps [CHAR_LIMIT=none] -->
<string name="accessibility_recent_apps">Recent apps</string>
- <!-- Accessibility confirmation for task closed -->
- <string name="task_view_closed">Task Closed</string>
-
<!-- Accessibility title for an app card in Recents for apps that have time limit set
[CHAR_LIMIT=none] -->
<string name="task_contents_description_with_remaining_time"><xliff:g id="task_description" example="GMail">%1$s</xliff:g>, <xliff:g id="remaining_time" example="7 minutes left today">%2$s</xliff:g></string>
@@ -360,4 +357,12 @@
<!-- Name of Google's new feature to circle to search anything on your phone screen, without
switching apps. [CHAR_LIMIT=60] -->
<string name="search_gesture_feature_title">Circle to Search</string>
+
+ <!-- Strings for task thumbnail header in Overview -->
+ <!-- Content description for the header app icon. [CHAR LIMIT=NONE] -->
+ <string name="header_app_icon_description">App icon</string>
+ <!-- Default app title for a task view in Overview. [CHAR LIMIT=NONE] -->
+ <string name="header_default_app_title">App title</string>
+ <!-- Content description for the header close button. [CHAR LIMIT=NONE] -->
+ <string name="header_close_icon_description">Close button</string>
</resources>
diff --git a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
index 22e491f..b329156 100644
--- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -35,7 +35,6 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.Flags;
-import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.FloatingHeaderRow;
@@ -150,8 +149,7 @@
int totalHeight = iconHeight + iconPadding + textHeight + mVerticalPadding * 2;
// Prediction row height will be 4dp bigger than the regular apps in A-Z list when two line
// is not enabled. Otherwise, the extra height will increase by just the textHeight.
- int extraHeight = (Flags.enableTwolineToggle() &&
- LauncherPrefs.ENABLE_TWOLINE_ALLAPPS_TOGGLE.get(getContext()))
+ int extraHeight = deviceProfile.inv.enableTwoLinesInAllApps
? (textHeight + mTopRowExtraHeight) : mTopRowExtraHeight;
totalHeight += extraHeight;
return getVisibility() == GONE ? 0 : totalHeight + getPaddingTop() + getPaddingBottom();
diff --git a/quickstep/src/com/android/launcher3/dagger/Modules.kt b/quickstep/src/com/android/launcher3/dagger/Modules.kt
new file mode 100644
index 0000000..04c1d5e
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/dagger/Modules.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.dagger
+
+import com.android.launcher3.uioverrides.SystemApiWrapper
+import com.android.launcher3.uioverrides.plugins.PluginManagerWrapperImpl
+import com.android.launcher3.util.ApiWrapper
+import com.android.launcher3.util.PluginManagerWrapper
+import com.android.launcher3.util.window.WindowManagerProxy
+import com.android.quickstep.util.SystemWindowManagerProxy
+import dagger.Binds
+import dagger.Module
+
+private object Modules {}
+
+@Module
+abstract class WindowManagerProxyModule {
+ @Binds abstract fun bindWindowManagerProxy(proxy: SystemWindowManagerProxy): WindowManagerProxy
+}
+
+@Module
+abstract class ApiWrapperModule {
+ @Binds abstract fun bindApiWrapper(systemApiWrapper: SystemApiWrapper): ApiWrapper
+}
+
+@Module
+abstract class PluginManagerWrapperModule {
+ @Binds
+ abstract fun bindPluginManagerWrapper(impl: PluginManagerWrapperImpl): PluginManagerWrapper
+}
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index c50e82d..c2cabd0 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -229,9 +229,7 @@
(WorkspaceItemInfo) mPredictedItems.get(predictionIndex++);
if (isPredictedIcon(child) && child.isEnabled()) {
PredictedAppIcon icon = (PredictedAppIcon) child;
- boolean animateIconChange = icon.shouldAnimateIconChange(predictedItem);
- icon.applyFromWorkspaceItem(predictedItem, animateIconChange, numViewsAnimated);
- if (animateIconChange) {
+ if (icon.applyFromWorkspaceItemWithAnimation(predictedItem, numViewsAnimated)) {
numViewsAnimated++;
}
icon.finishBinding(mPredictionLongClickListener);
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
deleted file mode 100644
index 488cea5..0000000
--- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
+++ /dev/null
@@ -1,521 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.statehandlers;
-
-import static android.view.View.VISIBLE;
-import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY;
-
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-
-import android.content.Context;
-import android.os.Debug;
-import android.util.Log;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.statemanager.BaseState;
-import com.android.launcher3.statemanager.StatefulActivity;
-import com.android.launcher3.uioverrides.QuickstepLauncher;
-import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.views.ActivityContext;
-import com.android.quickstep.GestureState;
-import com.android.quickstep.SystemUiProxy;
-import com.android.quickstep.fallback.RecentsState;
-import com.android.wm.shell.desktopmode.IDesktopTaskListener;
-import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
-
-import java.io.PrintWriter;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Controls the visibility of the workspace and the resumed / paused state when desktop mode
- * is enabled.
- */
-public class DesktopVisibilityController {
-
- private static final String TAG = "DesktopVisController";
- private static final boolean DEBUG = false;
- private final Set<DesktopVisibilityListener> mDesktopVisibilityListeners = new HashSet<>();
- private final Set<TaskbarDesktopModeListener> mTaskbarDesktopModeListeners = new HashSet<>();
-
- private int mVisibleDesktopTasksCount;
- private boolean mInOverviewState;
- private boolean mBackgroundStateEnabled;
- private boolean mGestureInProgress;
-
- @Nullable
- private DesktopTaskListenerImpl mDesktopTaskListener;
-
- @Nullable
- private Context mContext;
-
- public DesktopVisibilityController(@NonNull Context context) {
- setContext(context);
- }
-
- /** Sets the context and re-registers the System Ui listener */
- private void setContext(@Nullable Context context) {
- unregisterSystemUiListener();
- mContext = context;
- registerSystemUiListener();
- }
-
- /** Register a listener with System UI to receive updates about desktop tasks state */
- private void registerSystemUiListener() {
- if (mContext == null) {
- return;
- }
- if (mDesktopTaskListener != null) {
- return;
- }
- mDesktopTaskListener = new DesktopTaskListenerImpl(this, mContext.getDisplayId());
- SystemUiProxy.INSTANCE.get(mContext).setDesktopTaskListener(mDesktopTaskListener);
- }
-
- /**
- * Clear listener from System UI that was set with {@link #registerSystemUiListener()}
- */
- private void unregisterSystemUiListener() {
- if (mContext == null) {
- return;
- }
- if (mDesktopTaskListener == null) {
- return;
- }
- SystemUiProxy.INSTANCE.get(mContext).setDesktopTaskListener(null);
- mDesktopTaskListener.release();
- mDesktopTaskListener = null;
- }
-
- /**
- * Whether desktop tasks are visible in desktop mode.
- */
- public boolean areDesktopTasksVisible() {
- boolean desktopTasksVisible = mVisibleDesktopTasksCount > 0;
- if (DEBUG) {
- Log.d(TAG, "areDesktopTasksVisible: desktopVisible=" + desktopTasksVisible);
- }
- return desktopTasksVisible;
- }
-
- /**
- * Whether desktop tasks are visible in desktop mode.
- */
- public boolean areDesktopTasksVisibleAndNotInOverview() {
- boolean desktopTasksVisible = mVisibleDesktopTasksCount > 0;
- if (DEBUG) {
- Log.d(TAG, "areDesktopTasksVisible: desktopVisible=" + desktopTasksVisible
- + " overview=" + mInOverviewState);
- }
- return desktopTasksVisible && !mInOverviewState;
- }
-
- /**
- * Number of visible desktop windows in desktop mode.
- */
- public int getVisibleDesktopTasksCount() {
- return mVisibleDesktopTasksCount;
- }
-
- /** Registers a listener for Desktop Mode visibility updates. */
- public void registerDesktopVisibilityListener(DesktopVisibilityListener listener) {
- mDesktopVisibilityListeners.add(listener);
- }
-
- /** Removes a previously registered Desktop Mode visibility listener. */
- public void unregisterDesktopVisibilityListener(DesktopVisibilityListener listener) {
- mDesktopVisibilityListeners.remove(listener);
- }
-
- /** Registers a listener for Taskbar changes in Desktop Mode. */
- public void registerTaskbarDesktopModeListener(TaskbarDesktopModeListener listener) {
- mTaskbarDesktopModeListeners.add(listener);
- }
-
- /** Removes a previously registered listener for Taskbar changes in Desktop Mode. */
- public void unregisterTaskbarDesktopModeListener(TaskbarDesktopModeListener listener) {
- mTaskbarDesktopModeListeners.remove(listener);
- }
-
- /**
- * Sets the number of desktop windows that are visible and updates launcher visibility based on
- * it.
- */
- public void setVisibleDesktopTasksCount(int visibleTasksCount) {
- if (mContext == null) {
- return;
- }
- if (DEBUG) {
- Log.d(TAG, "setVisibleDesktopTasksCount: visibleTasksCount=" + visibleTasksCount
- + " currentValue=" + mVisibleDesktopTasksCount);
- }
-
- if (visibleTasksCount != mVisibleDesktopTasksCount) {
- final boolean wasVisible = mVisibleDesktopTasksCount > 0;
- final boolean isVisible = visibleTasksCount > 0;
- final boolean wereDesktopTasksVisibleBefore = areDesktopTasksVisibleAndNotInOverview();
- mVisibleDesktopTasksCount = visibleTasksCount;
- final boolean areDesktopTasksVisibleNow = areDesktopTasksVisibleAndNotInOverview();
- if (wereDesktopTasksVisibleBefore != areDesktopTasksVisibleNow) {
- notifyDesktopVisibilityListeners(areDesktopTasksVisibleNow);
- }
-
- if (!ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()
- && wasVisible != isVisible) {
- // TODO: b/333533253 - Remove after flag rollout
- if (mVisibleDesktopTasksCount > 0) {
- setLauncherViewsVisibility(View.INVISIBLE);
- if (!mInOverviewState) {
- // When desktop tasks are visible & we're not in overview, we want launcher
- // to appear paused, this ensures that taskbar displays.
- markLauncherPaused();
- }
- } else {
- setLauncherViewsVisibility(View.VISIBLE);
- // If desktop tasks aren't visible, ensure that launcher appears resumed to
- // behave normally.
- markLauncherResumed();
- }
- }
- }
- }
-
- public void onLauncherStateChanged(LauncherState state) {
- onLauncherStateChanged(
- state, state == LauncherState.BACKGROUND_APP, state.isRecentsViewVisible);
- }
-
- public void onLauncherStateChanged(RecentsState state) {
- onLauncherStateChanged(
- state, state == RecentsState.BACKGROUND_APP, state.isRecentsViewVisible());
- }
-
- /**
- * Process launcher state change and update launcher view visibility based on desktop state
- */
- public void onLauncherStateChanged(
- BaseState<?> state, boolean isBackgroundAppState, boolean isRecentsViewVisible) {
- if (DEBUG) {
- Log.d(TAG, "onLauncherStateChanged: newState=" + state);
- }
- setBackgroundStateEnabled(isBackgroundAppState);
- // Desktop visibility tracks overview and background state separately
- setOverviewStateEnabled(!isBackgroundAppState && isRecentsViewVisible);
- }
-
- private void setOverviewStateEnabled(boolean overviewStateEnabled) {
- if (mContext == null) {
- return;
- }
- if (DEBUG) {
- Log.d(TAG, "setOverviewStateEnabled: enabled=" + overviewStateEnabled
- + " currentValue=" + mInOverviewState);
- }
- if (overviewStateEnabled != mInOverviewState) {
- final boolean wereDesktopTasksVisibleBefore = areDesktopTasksVisibleAndNotInOverview();
- mInOverviewState = overviewStateEnabled;
- final boolean areDesktopTasksVisibleNow = areDesktopTasksVisibleAndNotInOverview();
- if (wereDesktopTasksVisibleBefore != areDesktopTasksVisibleNow) {
- notifyDesktopVisibilityListeners(areDesktopTasksVisibleNow);
- }
- if (ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()) {
- return;
- }
- // TODO: b/333533253 - Clean up after flag rollout
-
- if (mInOverviewState) {
- setLauncherViewsVisibility(View.VISIBLE);
- markLauncherResumed();
- } else if (areDesktopTasksVisibleNow && !mGestureInProgress) {
- // Switching out of overview state and gesture finished.
- // If desktop tasks are still visible, hide launcher again.
- setLauncherViewsVisibility(View.INVISIBLE);
- markLauncherPaused();
- }
- }
- }
-
- private void notifyDesktopVisibilityListeners(boolean areDesktopTasksVisible) {
- if (mContext == null) {
- return;
- }
- if (DEBUG) {
- Log.d(TAG, "notifyDesktopVisibilityListeners: visible=" + areDesktopTasksVisible);
- }
- for (DesktopVisibilityListener listener : mDesktopVisibilityListeners) {
- listener.onDesktopVisibilityChanged(areDesktopTasksVisible);
- }
- DisplayController.INSTANCE.get(mContext).notifyConfigChange();
- }
-
- private void notifyTaskbarDesktopModeListeners(boolean doesAnyTaskRequireTaskbarRounding) {
- if (DEBUG) {
- Log.d(TAG, "notifyTaskbarDesktopModeListeners: doesAnyTaskRequireTaskbarRounding="
- + doesAnyTaskRequireTaskbarRounding);
- }
- for (TaskbarDesktopModeListener listener : mTaskbarDesktopModeListeners) {
- listener.onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding);
- }
- }
-
- /**
- * TODO: b/333533253 - Remove after flag rollout
- */
- private void setBackgroundStateEnabled(boolean backgroundStateEnabled) {
- if (DEBUG) {
- Log.d(TAG, "setBackgroundStateEnabled: enabled=" + backgroundStateEnabled
- + " currentValue=" + mBackgroundStateEnabled);
- }
- if (backgroundStateEnabled != mBackgroundStateEnabled) {
- mBackgroundStateEnabled = backgroundStateEnabled;
- if (mBackgroundStateEnabled) {
- setLauncherViewsVisibility(View.VISIBLE);
- markLauncherResumed();
- } else if (areDesktopTasksVisibleAndNotInOverview() && !mGestureInProgress) {
- // Switching out of background state. If desktop tasks are visible, pause launcher.
- setLauncherViewsVisibility(View.INVISIBLE);
- markLauncherPaused();
- }
- }
- }
-
- /**
- * Whether recents gesture is currently in progress.
- *
- * TODO: b/333533253 - Remove after flag rollout
- */
- public boolean isRecentsGestureInProgress() {
- return mGestureInProgress;
- }
-
- /**
- * Notify controller that recents gesture has started.
- *
- * TODO: b/333533253 - Remove after flag rollout
- */
- public void setRecentsGestureStart() {
- if (DEBUG) {
- Log.d(TAG, "setRecentsGestureStart");
- }
- setRecentsGestureInProgress(true);
- }
-
- /**
- * Notify controller that recents gesture finished with the given
- * {@link com.android.quickstep.GestureState.GestureEndTarget}
- *
- * TODO: b/333533253 - Remove after flag rollout
- */
- public void setRecentsGestureEnd(@Nullable GestureState.GestureEndTarget endTarget) {
- if (DEBUG) {
- Log.d(TAG, "setRecentsGestureEnd: endTarget=" + endTarget);
- }
- setRecentsGestureInProgress(false);
-
- if (endTarget == null) {
- // Gesture did not result in a new end target. Ensure launchers gets paused again.
- markLauncherPaused();
- }
- }
-
- /**
- * TODO: b/333533253 - Remove after flag rollout
- */
- private void setRecentsGestureInProgress(boolean gestureInProgress) {
- if (gestureInProgress != mGestureInProgress) {
- mGestureInProgress = gestureInProgress;
- }
- }
-
- /**
- * TODO: b/333533253 - Remove after flag rollout
- */
- private void setLauncherViewsVisibility(int visibility) {
- if (mContext == null) {
- return;
- }
- if (ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()) {
- return;
- }
- if (DEBUG) {
- Log.d(TAG, "setLauncherViewsVisibility: visibility=" + visibility + " "
- + Debug.getCaller());
- }
- if (!(mContext instanceof ActivityContext activity)) {
- return;
- }
- View dragLayer = activity.getDragLayer();
- if (dragLayer != null) {
- dragLayer.setVisibility(visibility);
- }
- if (!(activity instanceof Launcher launcher)) {
- return;
- }
- View workspaceView = launcher.getWorkspace();
- if (workspaceView != null) {
- workspaceView.setVisibility(visibility);
- }
- if (launcher instanceof QuickstepLauncher ql
- && ql.getTaskbarUIController() != null
- && mVisibleDesktopTasksCount != 0) {
- ql.getTaskbarUIController().onLauncherVisibilityChanged(visibility == VISIBLE);
- }
- }
-
- /**
- * TODO: b/333533253 - Remove after flag rollout
- */
- private void markLauncherPaused() {
- if (mContext == null) {
- return;
- }
- if (ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()) {
- return;
- }
- if (DEBUG) {
- Log.d(TAG, "markLauncherPaused " + Debug.getCaller());
- }
- StatefulActivity<LauncherState> activity =
- QuickstepLauncher.ACTIVITY_TRACKER.getCreatedContext();
- if (activity != null) {
- activity.setPaused();
- }
- }
-
- /**
- * TODO: b/333533253 - Remove after flag rollout
- */
- private void markLauncherResumed() {
- if (mContext == null) {
- return;
- }
- if (ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()) {
- return;
- }
- if (DEBUG) {
- Log.d(TAG, "markLauncherResumed " + Debug.getCaller());
- }
- StatefulActivity<LauncherState> activity =
- QuickstepLauncher.ACTIVITY_TRACKER.getCreatedContext();
- // Check activity state before calling setResumed(). Launcher may have been actually
- // paused (eg fullscreen task moved to front).
- // In this case we should not mark the activity as resumed.
- if (activity != null && activity.isResumed()) {
- activity.setResumed();
- }
- }
-
- public void onDestroy() {
- setContext(null);
- }
-
- public void dumpLogs(String prefix, PrintWriter pw) {
- pw.println(prefix + "DesktopVisibilityController:");
-
- pw.println(prefix + "\tmDesktopVisibilityListeners=" + mDesktopVisibilityListeners);
- pw.println(prefix + "\tmVisibleDesktopTasksCount=" + mVisibleDesktopTasksCount);
- pw.println(prefix + "\tmInOverviewState=" + mInOverviewState);
- pw.println(prefix + "\tmBackgroundStateEnabled=" + mBackgroundStateEnabled);
- pw.println(prefix + "\tmGestureInProgress=" + mGestureInProgress);
- pw.println(prefix + "\tmDesktopTaskListener=" + mDesktopTaskListener);
- pw.println(prefix + "\tmContext=" + mContext);
- }
-
- /** A listener for when the user enters/exits Desktop Mode. */
- public interface DesktopVisibilityListener {
- /**
- * Callback for when the user enters or exits Desktop Mode
- *
- * @param visible whether Desktop Mode is now visible
- */
- void onDesktopVisibilityChanged(boolean visible);
- }
-
- /**
- * Wrapper for the IDesktopTaskListener stub to prevent lingering references to the launcher
- * activity via the controller.
- */
- private static class DesktopTaskListenerImpl extends IDesktopTaskListener.Stub {
-
- private DesktopVisibilityController mController;
- private final int mDisplayId;
-
- DesktopTaskListenerImpl(@NonNull DesktopVisibilityController controller, int displayId) {
- mController = controller;
- mDisplayId = displayId;
- }
-
- /**
- * Clears any references to the controller.
- */
- void release() {
- mController = null;
- }
-
- @Override
- public void onTasksVisibilityChanged(int displayId, int visibleTasksCount) {
- MAIN_EXECUTOR.execute(() -> {
- if (mController != null && displayId == mDisplayId) {
- if (DEBUG) {
- Log.d(TAG, "desktop visible tasks count changed=" + visibleTasksCount);
- }
- mController.setVisibleDesktopTasksCount(visibleTasksCount);
- }
- });
- }
-
- @Override
- public void onStashedChanged(int displayId, boolean stashed) {
- Log.w(TAG, "DesktopTaskListenerImpl: onStashedChanged is deprecated");
- }
-
- @Override
- public void onTaskbarCornerRoundingUpdate(boolean doesAnyTaskRequireTaskbarRounding) {
- MAIN_EXECUTOR.execute(() -> {
- if (mController != null && DesktopModeStatus.useRoundedCorners()) {
- Log.d(TAG, "DesktopTaskListenerImpl: doesAnyTaskRequireTaskbarRounding= "
- + doesAnyTaskRequireTaskbarRounding);
- mController.notifyTaskbarDesktopModeListeners(
- doesAnyTaskRequireTaskbarRounding);
- }
- });
- }
-
- public void onEnterDesktopModeTransitionStarted(int transitionDuration) {
-
- }
-
- @Override
- public void onExitDesktopModeTransitionStarted(int transitionDuration) {
-
- }
- }
-
- /** A listener for Taskbar in Desktop Mode. */
- public interface TaskbarDesktopModeListener {
- /**
- * Callback for when task is resized in desktop mode.
- *
- * @param doesAnyTaskRequireTaskbarRounding whether task requires taskbar corner roundness.
- */
- void onTaskbarCornerRoundingUpdate(boolean doesAnyTaskRequireTaskbarRounding);
- }
-}
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt
new file mode 100644
index 0000000..0703a61
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt
@@ -0,0 +1,420 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.statehandlers
+
+import android.content.Context
+import android.os.Debug
+import android.util.Log
+import android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY
+import com.android.launcher3.LauncherState
+import com.android.launcher3.dagger.ApplicationContext
+import com.android.launcher3.dagger.LauncherAppComponent
+import com.android.launcher3.dagger.LauncherAppSingleton
+import com.android.launcher3.statemanager.BaseState
+import com.android.launcher3.statemanager.StatefulActivity
+import com.android.launcher3.uioverrides.QuickstepLauncher
+import com.android.launcher3.util.DaggerSingletonObject
+import com.android.launcher3.util.DaggerSingletonTracker
+import com.android.launcher3.util.Executors
+import com.android.launcher3.util.window.WindowManagerProxy.DesktopVisibilityListener
+import com.android.quickstep.GestureState.GestureEndTarget
+import com.android.quickstep.SystemUiProxy
+import com.android.quickstep.fallback.RecentsState
+import com.android.wm.shell.desktopmode.IDesktopTaskListener.Stub
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
+import java.io.PrintWriter
+import java.lang.ref.WeakReference
+import javax.inject.Inject
+
+/**
+ * Controls the visibility of the workspace and the resumed / paused state when desktop mode is
+ * enabled.
+ */
+@LauncherAppSingleton
+class DesktopVisibilityController
+@Inject
+constructor(
+ @ApplicationContext private val context: Context,
+ systemUiProxy: SystemUiProxy,
+ lifecycleTracker: DaggerSingletonTracker,
+) {
+ private val desktopVisibilityListeners: MutableSet<DesktopVisibilityListener> = HashSet()
+ private val taskbarDesktopModeListeners: MutableSet<TaskbarDesktopModeListener> = HashSet()
+
+ /** Number of visible desktop windows in desktop mode. */
+ var visibleDesktopTasksCount: Int = 0
+ /**
+ * Sets the number of desktop windows that are visible and updates launcher visibility based
+ * on it.
+ */
+ set(visibleTasksCount) {
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ ("setVisibleDesktopTasksCount: visibleTasksCount=" +
+ visibleTasksCount +
+ " currentValue=" +
+ field),
+ )
+ }
+
+ if (visibleTasksCount != field) {
+ val wasVisible = field > 0
+ val isVisible = visibleTasksCount > 0
+ val wereDesktopTasksVisibleBefore = areDesktopTasksVisibleAndNotInOverview()
+ field = visibleTasksCount
+ val areDesktopTasksVisibleNow = areDesktopTasksVisibleAndNotInOverview()
+ if (wereDesktopTasksVisibleBefore != areDesktopTasksVisibleNow) {
+ notifyDesktopVisibilityListeners(areDesktopTasksVisibleNow)
+ }
+
+ if (
+ !ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue && wasVisible != isVisible
+ ) {
+ // TODO: b/333533253 - Remove after flag rollout
+ if (field > 0) {
+ if (!inOverviewState) {
+ // When desktop tasks are visible & we're not in overview, we want
+ // launcher
+ // to appear paused, this ensures that taskbar displays.
+ markLauncherPaused()
+ }
+ } else {
+ // If desktop tasks aren't visible, ensure that launcher appears resumed to
+ // behave normally.
+ markLauncherResumed()
+ }
+ }
+ }
+ }
+
+ private var inOverviewState = false
+ private var backgroundStateEnabled = false
+ private var gestureInProgress = false
+
+ private var desktopTaskListener: DesktopTaskListenerImpl?
+
+ init {
+ desktopTaskListener = DesktopTaskListenerImpl(this, context.displayId)
+ systemUiProxy.setDesktopTaskListener(desktopTaskListener)
+
+ lifecycleTracker.addCloseable {
+ desktopTaskListener = null
+ systemUiProxy.setDesktopTaskListener(null)
+ }
+ }
+
+ /** Whether desktop tasks are visible in desktop mode. */
+ fun areDesktopTasksVisible(): Boolean {
+ val desktopTasksVisible: Boolean = visibleDesktopTasksCount > 0
+ if (DEBUG) {
+ Log.d(TAG, "areDesktopTasksVisible: desktopVisible=$desktopTasksVisible")
+ }
+ return desktopTasksVisible
+ }
+
+ /** Whether desktop tasks are visible in desktop mode. */
+ fun areDesktopTasksVisibleAndNotInOverview(): Boolean {
+ val desktopTasksVisible: Boolean = visibleDesktopTasksCount > 0
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ ("areDesktopTasksVisible: desktopVisible=" +
+ desktopTasksVisible +
+ " overview=" +
+ inOverviewState),
+ )
+ }
+ return desktopTasksVisible && !inOverviewState
+ }
+
+ /** Registers a listener for Taskbar changes in Desktop Mode. */
+ fun registerTaskbarDesktopModeListener(listener: TaskbarDesktopModeListener) {
+ taskbarDesktopModeListeners.add(listener)
+ }
+
+ /** Removes a previously registered listener for Taskbar changes in Desktop Mode. */
+ fun unregisterTaskbarDesktopModeListener(listener: TaskbarDesktopModeListener) {
+ taskbarDesktopModeListeners.remove(listener)
+ }
+
+ fun onLauncherStateChanged(state: LauncherState) {
+ onLauncherStateChanged(
+ state,
+ state === LauncherState.BACKGROUND_APP,
+ state.isRecentsViewVisible,
+ )
+ }
+
+ fun onLauncherStateChanged(state: RecentsState) {
+ onLauncherStateChanged(
+ state,
+ state === RecentsState.BACKGROUND_APP,
+ state.isRecentsViewVisible,
+ )
+ }
+
+ /** Process launcher state change and update launcher view visibility based on desktop state */
+ fun onLauncherStateChanged(
+ state: BaseState<*>,
+ isBackgroundAppState: Boolean,
+ isRecentsViewVisible: Boolean,
+ ) {
+ if (DEBUG) {
+ Log.d(TAG, "onLauncherStateChanged: newState=$state")
+ }
+ setBackgroundStateEnabled(isBackgroundAppState)
+ // Desktop visibility tracks overview and background state separately
+ setOverviewStateEnabled(!isBackgroundAppState && isRecentsViewVisible)
+ }
+
+ private fun setOverviewStateEnabled(overviewStateEnabled: Boolean) {
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ ("setOverviewStateEnabled: enabled=" +
+ overviewStateEnabled +
+ " currentValue=" +
+ inOverviewState),
+ )
+ }
+ if (overviewStateEnabled != inOverviewState) {
+ val wereDesktopTasksVisibleBefore = areDesktopTasksVisibleAndNotInOverview()
+ inOverviewState = overviewStateEnabled
+ val areDesktopTasksVisibleNow = areDesktopTasksVisibleAndNotInOverview()
+ if (wereDesktopTasksVisibleBefore != areDesktopTasksVisibleNow) {
+ notifyDesktopVisibilityListeners(areDesktopTasksVisibleNow)
+ }
+
+ if (ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue) {
+ return
+ }
+
+ // TODO: b/333533253 - Clean up after flag rollout
+ if (inOverviewState) {
+ markLauncherResumed()
+ } else if (areDesktopTasksVisibleNow && !gestureInProgress) {
+ // Switching out of overview state and gesture finished.
+ // If desktop tasks are still visible, hide launcher again.
+ markLauncherPaused()
+ }
+ }
+ }
+
+ /** Registers a listener for Taskbar changes in Desktop Mode. */
+ fun registerDesktopVisibilityListener(listener: DesktopVisibilityListener) {
+ desktopVisibilityListeners.add(listener)
+ }
+
+ /** Removes a previously registered listener for Taskbar changes in Desktop Mode. */
+ fun unregisterDesktopVisibilityListener(listener: DesktopVisibilityListener) {
+ desktopVisibilityListeners.remove(listener)
+ }
+
+ private fun notifyDesktopVisibilityListeners(areDesktopTasksVisible: Boolean) {
+ if (DEBUG) {
+ Log.d(TAG, "notifyDesktopVisibilityListeners: visible=$areDesktopTasksVisible")
+ }
+ for (listener in desktopVisibilityListeners) {
+ listener.onDesktopVisibilityChanged(areDesktopTasksVisible)
+ }
+ }
+
+ private fun notifyTaskbarDesktopModeListeners(doesAnyTaskRequireTaskbarRounding: Boolean) {
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "notifyTaskbarDesktopModeListeners: doesAnyTaskRequireTaskbarRounding=" +
+ doesAnyTaskRequireTaskbarRounding,
+ )
+ }
+ for (listener in taskbarDesktopModeListeners) {
+ listener.onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding)
+ }
+ }
+
+ /** TODO: b/333533253 - Remove after flag rollout */
+ private fun setBackgroundStateEnabled(backgroundStateEnabled: Boolean) {
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ ("setBackgroundStateEnabled: enabled=" +
+ backgroundStateEnabled +
+ " currentValue=" +
+ this.backgroundStateEnabled),
+ )
+ }
+ if (backgroundStateEnabled != this.backgroundStateEnabled) {
+ this.backgroundStateEnabled = backgroundStateEnabled
+ if (this.backgroundStateEnabled) {
+ markLauncherResumed()
+ } else if (areDesktopTasksVisibleAndNotInOverview() && !gestureInProgress) {
+ // Switching out of background state. If desktop tasks are visible, pause launcher.
+ markLauncherPaused()
+ }
+ }
+ }
+
+ var isRecentsGestureInProgress: Boolean
+ /**
+ * Whether recents gesture is currently in progress.
+ *
+ * TODO: b/333533253 - Remove after flag rollout
+ */
+ get() = gestureInProgress
+ /** TODO: b/333533253 - Remove after flag rollout */
+ private set(gestureInProgress) {
+ if (gestureInProgress != this.gestureInProgress) {
+ this.gestureInProgress = gestureInProgress
+ }
+ }
+
+ /**
+ * Notify controller that recents gesture has started.
+ *
+ * TODO: b/333533253 - Remove after flag rollout
+ */
+ fun setRecentsGestureStart() {
+ if (DEBUG) {
+ Log.d(TAG, "setRecentsGestureStart")
+ }
+ isRecentsGestureInProgress = true
+ }
+
+ /**
+ * Notify controller that recents gesture finished with the given
+ * [com.android.quickstep.GestureState.GestureEndTarget]
+ *
+ * TODO: b/333533253 - Remove after flag rollout
+ */
+ fun setRecentsGestureEnd(endTarget: GestureEndTarget?) {
+ if (DEBUG) {
+ Log.d(TAG, "setRecentsGestureEnd: endTarget=$endTarget")
+ }
+ isRecentsGestureInProgress = false
+
+ if (endTarget == null) {
+ // Gesture did not result in a new end target. Ensure launchers gets paused again.
+ markLauncherPaused()
+ }
+ }
+
+ /** TODO: b/333533253 - Remove after flag rollout */
+ private fun markLauncherPaused() {
+ if (ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue) {
+ return
+ }
+ if (DEBUG) {
+ Log.d(TAG, "markLauncherPaused " + Debug.getCaller())
+ }
+ val activity: StatefulActivity<LauncherState>? =
+ QuickstepLauncher.ACTIVITY_TRACKER.getCreatedContext()
+ activity?.setPaused()
+ }
+
+ /** TODO: b/333533253 - Remove after flag rollout */
+ private fun markLauncherResumed() {
+ if (ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue) {
+ return
+ }
+ if (DEBUG) {
+ Log.d(TAG, "markLauncherResumed " + Debug.getCaller())
+ }
+ val activity: StatefulActivity<LauncherState>? =
+ QuickstepLauncher.ACTIVITY_TRACKER.getCreatedContext()
+ // Check activity state before calling setResumed(). Launcher may have been actually
+ // paused (eg fullscreen task moved to front).
+ // In this case we should not mark the activity as resumed.
+ if (activity != null && activity.isResumed) {
+ activity.setResumed()
+ }
+ }
+
+ fun dumpLogs(prefix: String, pw: PrintWriter) {
+ pw.println(prefix + "DesktopVisibilityController:")
+
+ pw.println("$prefix\tdesktopVisibilityListeners=$desktopVisibilityListeners")
+ pw.println("$prefix\tvisibleDesktopTasksCount=$visibleDesktopTasksCount")
+ pw.println("$prefix\tinOverviewState=$inOverviewState")
+ pw.println("$prefix\tbackgroundStateEnabled=$backgroundStateEnabled")
+ pw.println("$prefix\tgestureInProgress=$gestureInProgress")
+ pw.println("$prefix\tdesktopTaskListener=$desktopTaskListener")
+ pw.println("$prefix\tcontext=$context")
+ }
+
+ /**
+ * Wrapper for the IDesktopTaskListener stub to prevent lingering references to the launcher
+ * activity via the controller.
+ */
+ private class DesktopTaskListenerImpl(
+ controller: DesktopVisibilityController,
+ private val displayId: Int,
+ ) : Stub() {
+ private val controller = WeakReference(controller)
+
+ override fun onTasksVisibilityChanged(displayId: Int, visibleTasksCount: Int) {
+ if (displayId != this.displayId) return
+ Executors.MAIN_EXECUTOR.execute {
+ controller.get()?.apply {
+ if (DEBUG) {
+ Log.d(TAG, "desktop visible tasks count changed=$visibleTasksCount")
+ }
+ visibleDesktopTasksCount = visibleTasksCount
+ }
+ }
+ }
+
+ override fun onStashedChanged(displayId: Int, stashed: Boolean) {
+ Log.w(TAG, "DesktopTaskListenerImpl: onStashedChanged is deprecated")
+ }
+
+ override fun onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding: Boolean) {
+ if (!DesktopModeStatus.useRoundedCorners()) return
+ Executors.MAIN_EXECUTOR.execute {
+ controller.get()?.apply {
+ Log.d(
+ TAG,
+ "DesktopTaskListenerImpl: doesAnyTaskRequireTaskbarRounding= " +
+ doesAnyTaskRequireTaskbarRounding,
+ )
+ notifyTaskbarDesktopModeListeners(doesAnyTaskRequireTaskbarRounding)
+ }
+ }
+ }
+
+ override fun onEnterDesktopModeTransitionStarted(transitionDuration: Int) {}
+
+ override fun onExitDesktopModeTransitionStarted(transitionDuration: Int) {}
+ }
+
+ /** A listener for Taskbar in Desktop Mode. */
+ interface TaskbarDesktopModeListener {
+ /**
+ * Callback for when task is resized in desktop mode.
+ *
+ * @param doesAnyTaskRequireTaskbarRounding whether task requires taskbar corner roundness.
+ */
+ fun onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding: Boolean)
+ }
+
+ companion object {
+ @JvmField
+ val INSTANCE = DaggerSingletonObject(LauncherAppComponent::getDesktopVisibilityController)
+
+ private const val TAG = "DesktopVisController"
+ private const val DEBUG = false
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
index cd38e5e..cb16345 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
@@ -269,7 +269,7 @@
DesktopTask desktopTask = findDesktopTask(tasks);
if (desktopTask != null) {
- mTasks = desktopTask.tasks.stream()
+ mTasks = desktopTask.getTasks().stream()
.map(GroupTask::new)
.filter(task -> !shouldExcludeTask(task, taskIdsToExclude))
.collect(Collectors.toList());
@@ -349,15 +349,12 @@
pw.println(prefix + "\tmWasDesktopTaskFilteredOut=" + mWasDesktopTaskFilteredOut);
pw.println(prefix + "\tmTasks=[");
for (GroupTask task : mTasks) {
- Task task1 = task.task1;
- Task task2 = task.task2;
- ComponentName cn1 = task1.getTopComponent();
- ComponentName cn2 = task2 != null ? task2.getTopComponent() : null;
- pw.println(prefix + "\t\tt1: (id=" + task1.key.id
- + "; package=" + (cn1 != null ? cn1.getPackageName() + ")" : "no package)")
- + " t2: (id=" + (task2 != null ? task2.key.id : "-1")
- + "; package=" + (cn2 != null ? cn2.getPackageName() + ")"
- : "no package)"));
+ int count = 0;
+ for (Task t : task.getTasks()) {
+ ComponentName cn = t.getTopComponent();
+ pw.println(prefix + "\t\tt" + (++count) + ": (id=" + t.key.id
+ + "; package=" + (cn != null ? cn.getPackageName() + ")" : "no package)"));
+ }
}
pw.println(prefix + "\t]");
@@ -411,10 +408,7 @@
return false;
}
int runningTaskId = ActivityManagerWrapper.getInstance().getRunningTask().taskId;
- Task task2 = task.task2;
-
- return runningTaskId == task.task1.key.id
- || (task2 != null && runningTaskId == task2.key.id);
+ return task.containsTask(runningTaskId);
}
boolean isFirstTaskRunning() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index e09181e..6db6233 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -141,6 +141,7 @@
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.Executors;
+import com.android.launcher3.util.MultiPropertyFactory;
import com.android.launcher3.util.NavigationMode;
import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.SettingsCache;
@@ -195,6 +196,7 @@
private WindowManager.LayoutParams mWindowLayoutParams;
private WindowManager.LayoutParams mLastUpdatedLayoutParams;
private boolean mIsFullscreen;
+ private boolean mIsNotificationShadeExpanded = false;
// The size we should return to when we call setTaskbarWindowFullscreen(false)
private int mLastRequestedNonFullscreenSize;
/**
@@ -238,7 +240,6 @@
@Nullable Context navigationBarPanelContext, DeviceProfile launcherDp,
TaskbarNavButtonController buttonController,
ScopedUnfoldTransitionProgressProvider unfoldTransitionProgressProvider,
- @NonNull DesktopVisibilityController desktopVisibilityController,
boolean isPrimaryDisplay) {
super(windowContext);
mIsPrimaryDisplay = isPrimaryDisplay;
@@ -271,8 +272,7 @@
mWindowManager = c.getSystemService(WindowManager.class);
// Inflate views.
- final boolean isTransientTaskbar = DisplayController.isTransientTaskbar(this)
- && !isPhoneMode();
+ boolean isTransientTaskbar = isTransientTaskbar();
int taskbarLayout = isTransientTaskbar ? R.layout.transient_taskbar : R.layout.taskbar;
mDragLayer = (TaskbarDragLayer) mLayoutInflater.inflate(taskbarLayout, null, false);
TaskbarView taskbarView = mDragLayer.findViewById(R.id.taskbar_view);
@@ -364,7 +364,7 @@
new KeyboardQuickSwitchController(),
new TaskbarPinningController(this),
bubbleControllersOptional,
- new TaskbarDesktopModeController(desktopVisibilityController));
+ new TaskbarDesktopModeController(DesktopVisibilityController.INSTANCE.get(this)));
mLauncherPrefs = LauncherPrefs.get(this);
}
@@ -385,6 +385,11 @@
dispatchDeviceProfileChanged();
}
+ /** Returns whether current taskbar is transient. */
+ public boolean isTransientTaskbar() {
+ return DisplayController.isTransientTaskbar(this) && !isPhoneMode();
+ }
+
/**
* Copy the original DeviceProfile, match the number of hotseat icons and qsb width and update
* the icon size
@@ -1007,6 +1012,8 @@
* Hides the taskbar icons and background when the notification shade is expanded.
*/
private void onNotificationShadeExpandChanged(boolean isExpanded, boolean skipAnim) {
+ boolean isExpandedUpdated = isExpanded != mIsNotificationShadeExpanded;
+ mIsNotificationShadeExpanded = isExpanded;
// Close all floating views within the Taskbar window to make sure nothing is shown over
// the notification shade.
if (isExpanded) {
@@ -1020,11 +1027,18 @@
anim.play(mControllers.taskbarDragLayerController.getNotificationShadeBgTaskbar()
.animateToValue(alpha));
- mControllers.bubbleControllers.ifPresent(controllers -> {
- BubbleBarViewController bubbleBarViewController = controllers.bubbleBarViewController;
- anim.play(bubbleBarViewController.getBubbleBarAlpha().get(0).animateToValue(alpha));
- });
-
+ if (isExpandedUpdated) {
+ mControllers.bubbleControllers.ifPresent(controllers -> {
+ BubbleBarViewController bubbleBarViewController =
+ controllers.bubbleBarViewController;
+ anim.play(bubbleBarViewController.getBubbleBarAlpha().get(0).animateToValue(alpha));
+ MultiPropertyFactory<View>.MultiProperty handleAlpha =
+ controllers.bubbleStashController.getHandleViewAlpha();
+ if (handleAlpha != null) {
+ anim.play(handleAlpha.animateToValue(alpha));
+ }
+ });
+ }
anim.start();
if (skipAnim) {
anim.end();
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
index ea6d82b..e44bce1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
@@ -57,6 +57,7 @@
var backgroundHeight = context.deviceProfile.taskbarHeight.toFloat()
var translationYForSwipe = 0f
var translationYForStash = 0f
+ var translationXForBubbleBar = 0f
private val transientBackgroundBounds = context.transientTaskbarBounds
@@ -244,12 +245,12 @@
setColorAlphaBound(Color.BLACK, Math.round(newShadowAlpha)),
)
strokePaint.alpha = (paint.alpha * strokeAlpha) / 255
-
+ val currentTranslationX = translationXForBubbleBar * progress
lastDrawnTransientRect.set(
- transientBackgroundBounds.left + halfWidthDelta,
+ transientBackgroundBounds.left + halfWidthDelta + currentTranslationX,
bottom - newBackgroundHeight,
- transientBackgroundBounds.right - halfWidthDelta,
- bottom
+ transientBackgroundBounds.right - halfWidthDelta + currentTranslationX,
+ bottom,
)
val horizontalInset = fullWidth * widthInsetPercentage
lastDrawnTransientRect.inset(horizontalInset, 0f)
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
index 8b52112..59ef577 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
@@ -254,6 +254,11 @@
invalidate();
}
+ protected void setBackgroundTranslationXForBubbleBar(float translationX) {
+ mBackgroundRenderer.setTranslationXForBubbleBar(translationX);
+ invalidate();
+ }
+
/** Returns the bounds in DragLayer coordinates of where the transient background was drawn. */
protected RectF getLastDrawnTransientRect() {
return mBackgroundRenderer.getLastDrawnTransientRect();
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
index 925e10b..68c252a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
@@ -198,6 +198,13 @@
}
/**
+ * Sets the translation of the background for the bubble bar.
+ */
+ public void setTranslationXForBubbleBar(float transX) {
+ mTaskbarDragLayer.setBackgroundTranslationXForBubbleBar(transX);
+ }
+
+ /**
* Sets the translation of the background during the spring on stash animation.
*/
public void setTranslationYForStash(float transY) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 1905561..9a9575d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -393,10 +393,7 @@
* @param launcherState The current state launcher is in
*/
private void updateOverviewDragState(LauncherState launcherState) {
- boolean disallowLongClick =
- FeatureFlags.enableSplitContextually()
- ? mLauncher.isSplitSelectionActive() || mIsAnimatingToLauncher
- : launcherState == LauncherState.OVERVIEW_SPLIT_SELECT;
+ boolean disallowLongClick = mLauncher.isSplitSelectionActive() || mIsAnimatingToLauncher;
com.android.launcher3.taskbar.Utilities.setOverviewDragState(
mControllers, launcherState.disallowTaskbarGlobalDrag(),
disallowLongClick, launcherState.allowTaskbarInitialSplitSelection());
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index 13f9a51..7c6c7ac 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -61,8 +61,6 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.contextualeducation.ContextualEduStatsManager;
-import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarNavButtonCallbacks;
import com.android.launcher3.taskbar.unfold.NonDestroyableScopedUnfoldTransitionProgressProvider;
@@ -223,14 +221,11 @@
}
};
- @NonNull private final DesktopVisibilityController mDesktopVisibilityController;
-
@SuppressLint("WrongConstant")
public TaskbarManager(
Context context,
AllAppsActionManager allAppsActionManager,
- TaskbarNavButtonCallbacks navCallbacks,
- @NonNull DesktopVisibilityController desktopVisibilityController) {
+ TaskbarNavButtonCallbacks navCallbacks) {
Display display =
context.getSystemService(DisplayManager.class).getDisplay(context.getDisplayId());
mWindowContext = context.createWindowContext(display,
@@ -240,7 +235,6 @@
mNavigationBarPanelContext = ENABLE_TASKBAR_NAVBAR_UNIFICATION
? context.createWindowContext(display, TYPE_NAVIGATION_BAR_PANEL, null)
: null;
- mDesktopVisibilityController = desktopVisibilityController;
if (enableTaskbarNoRecreate()) {
mWindowManager = mWindowContext.getSystemService(WindowManager.class);
createTaskbarRootLayout(getDefaultDisplayId());
@@ -275,7 +269,6 @@
context,
navCallbacks,
SystemUiProxy.INSTANCE.get(mWindowContext),
- ContextualEduStatsManager.INSTANCE.get(mWindowContext),
new Handler(),
new ContextualSearchInvoker(mWindowContext));
}
@@ -800,7 +793,7 @@
private TaskbarActivityContext createTaskbarActivityContext(DeviceProfile dp, int displayId) {
TaskbarActivityContext newTaskbar = new TaskbarActivityContext(mWindowContext,
mNavigationBarPanelContext, dp, mDefaultNavButtonController,
- mUnfoldProgressProvider, mDesktopVisibilityController, isDefaultDisplay(displayId));
+ mUnfoldProgressProvider, isDefaultDisplay(displayId));
addTaskbarToMap(displayId, newTaskbar);
return newTaskbar;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
index d1f9be0..1adb2e9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
@@ -51,7 +51,6 @@
import androidx.annotation.StringRes;
import com.android.launcher3.R;
-import com.android.launcher3.contextualeducation.ContextualEduStatsManager;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
@@ -120,7 +119,6 @@
private final Context mContext;
private final TaskbarNavButtonCallbacks mCallbacks;
private final SystemUiProxy mSystemUiProxy;
- private final ContextualEduStatsManager mContextualEduStatsManager;
private final Handler mHandler;
private final ContextualSearchInvoker mContextualSearchInvoker;
@Nullable private StatsLogManager mStatsLogManager;
@@ -131,13 +129,11 @@
Context context,
TaskbarNavButtonCallbacks callbacks,
SystemUiProxy systemUiProxy,
- ContextualEduStatsManager contextualEduStatsManager,
Handler handler,
ContextualSearchInvoker contextualSearchInvoker) {
mContext = context;
mCallbacks = callbacks;
mSystemUiProxy = systemUiProxy;
- mContextualEduStatsManager = contextualEduStatsManager;
mHandler = handler;
mContextualSearchInvoker = contextualSearchInvoker;
}
@@ -159,13 +155,13 @@
break;
case BUTTON_HOME:
logEvent(LAUNCHER_TASKBAR_HOME_BUTTON_TAP);
- mContextualEduStatsManager.updateEduStats(/* isTrackpadGesture= */ false,
+ mSystemUiProxy.updateContextualEduStats(/* isTrackpadGesture= */ false,
GestureType.HOME);
navigateHome();
break;
case BUTTON_RECENTS:
logEvent(LAUNCHER_TASKBAR_OVERVIEW_BUTTON_TAP);
- mContextualEduStatsManager.updateEduStats(/* isTrackpadGesture= */ false,
+ mSystemUiProxy.updateContextualEduStats(/* isTrackpadGesture= */ false,
GestureType.OVERVIEW);
navigateToOverview();
break;
@@ -364,7 +360,7 @@
private void executeBack(@Nullable KeyEvent keyEvent) {
if (keyEvent == null || (keyEvent.getAction() == ACTION_UP && !keyEvent.isCanceled())) {
logEvent(LAUNCHER_TASKBAR_BACK_BUTTON_TAP);
- mContextualEduStatsManager.updateEduStats(/* isTrackpadGesture= */ false,
+ mSystemUiProxy.updateContextualEduStats(/* isTrackpadGesture= */ false,
GestureType.BACK);
}
mSystemUiProxy.onBackEvent(keyEvent);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index 741853e..6b9f5a9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -64,6 +64,7 @@
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.taskbar.customization.TaskbarAllAppsButtonContainer;
import com.android.launcher3.taskbar.customization.TaskbarDividerContainer;
+import com.android.launcher3.uioverrides.PredictedAppIcon;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.LauncherBindableItemsContainer;
import com.android.launcher3.util.Themes;
@@ -595,10 +596,12 @@
// Apply the Hotseat ItemInfos, or hide the view if there is none for a given index.
if (hotseatView instanceof BubbleTextView btv
&& hotseatItemInfo instanceof WorkspaceItemInfo workspaceInfo) {
- boolean animate = btv.shouldAnimateIconChange((WorkspaceItemInfo) hotseatItemInfo);
- btv.applyFromWorkspaceItem(workspaceInfo, animate, numViewsAnimated);
- if (animate) {
- numViewsAnimated++;
+ if (btv instanceof PredictedAppIcon pai) {
+ if (pai.applyFromWorkspaceItemWithAnimation(workspaceInfo, numViewsAnimated)) {
+ numViewsAnimated++;
+ }
+ } else {
+ btv.applyFromWorkspaceItem(workspaceInfo);
}
}
setClickAndLongClickListenersForIcon(hotseatView);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 60de066..e0be39d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -40,6 +40,7 @@
import static com.android.launcher3.taskbar.bubbles.BubbleBarView.FADE_IN_ANIM_ALPHA_DURATION_MS;
import static com.android.launcher3.taskbar.bubbles.BubbleBarView.FADE_OUT_ANIM_POSITION_DURATION_MS;
import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
+import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_BUBBLE_BAR_ANIM;
import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_NAV_BAR_ANIM;
import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_TASKBAR_ALIGNMENT_ANIM;
import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_TASKBAR_PINNING_ANIM;
@@ -81,6 +82,7 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.TaskItemInfo;
import com.android.launcher3.taskbar.bubbles.BubbleBarController;
+import com.android.launcher3.taskbar.bubbles.BubbleControllers;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LauncherBindableItemsContainer;
@@ -109,6 +111,8 @@
private static final Runnable NO_OP = () -> { };
+ public static long TRANSLATION_X_FOR_BUBBLEBAR_ANIM_DURATION_MS = 250;
+
public static final int ALPHA_INDEX_HOME = 0;
public static final int ALPHA_INDEX_KEYGUARD = 1;
public static final int ALPHA_INDEX_STASH = 2;
@@ -132,6 +136,7 @@
private static final int TRANSITION_FADE_OUT_DURATION = 83;
private final TaskbarActivityContext mActivity;
+ private @Nullable TaskbarDragLayerController mDragLayerController;
private final TaskbarView mTaskbarView;
private final MultiValueAlpha mTaskbarIconAlpha;
private final AnimatedFloat mTaskbarIconScaleForStash = new AnimatedFloat(this::updateScale);
@@ -144,15 +149,22 @@
this::updateTaskbarIconsScale);
private final AnimatedFloat mTaskbarIconTranslationXForPinning = new AnimatedFloat(
- this::updateTaskbarIconTranslationXForPinning);
+ () -> updateTaskbarIconTranslationXForPinning());
private final AnimatedFloat mIconsTranslationXForNavbar = new AnimatedFloat(
this::updateTranslationXForNavBar);
+ private final AnimatedFloat mTranslationXForBubbleBar = new AnimatedFloat(
+ this::updateTranslationXForBubbleBar);
+
@Nullable
private Animator mTaskbarShiftXAnim;
@Nullable
private BubbleBarLocation mCurrentBubbleBarLocation;
+ @Nullable
+ private BubbleControllers mBubbleControllers = null;
+ @Nullable
+ private ObjectAnimator mTranslationXAnimation;
private final AnimatedFloat mTaskbarIconTranslationYForPinning = new AnimatedFloat(
this::updateTranslationY);
@@ -174,11 +186,12 @@
private final View.OnLayoutChangeListener mTaskbarViewLayoutChangeListener =
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
if (!taskbarRecentsLayoutTransition()) {
- updateTaskbarIconTranslationXForPinning();
+ // update shiftX is handled with the animation at the end of the method
+ updateTaskbarIconTranslationXForPinning(/* updateShiftXForBubbleBar = */ false);
}
- if (BubbleBarController.isBubbleBarEnabled()) {
- mControllers.navbarButtonsViewController.onLayoutsUpdated();
- }
+ if (mBubbleControllers == null) return;
+ mControllers.navbarButtonsViewController.onLayoutsUpdated();
+ adjustTaskbarXForBubbleBar();
};
// Animation to align icons with Launcher, created lazily. This allows the controller to be
@@ -222,11 +235,11 @@
mIsRtl = Utilities.isRtl(mTaskbarView.getResources());
mTaskbarLeftRightMargin = mActivity.getResources().getDimensionPixelSize(
R.dimen.transient_taskbar_padding);
-
}
public void init(TaskbarControllers controllers) {
mControllers = controllers;
+ controllers.bubbleControllers.ifPresent(bc -> mBubbleControllers = bc);
mTaskbarView.init(TaskbarViewCallbacksFactory.newInstance(mActivity).create(
mActivity, mControllers, mTaskbarView));
mTaskbarView.getLayoutParams().height = mActivity.isPhoneMode()
@@ -252,7 +265,7 @@
controllers.navbarButtonsViewController.getTaskbarNavButtonTranslationY();
mTaskbarNavButtonTranslationYForInAppDisplay = controllers.navbarButtonsViewController
.getTaskbarNavButtonTranslationYForInAppDisplay();
-
+ mDragLayerController = controllers.taskbarDragLayerController;
mActivity.addOnDeviceProfileChangeListener(mDeviceProfileChangeListener);
if (ENABLE_TASKBAR_NAVBAR_UNIFICATION) {
@@ -270,24 +283,59 @@
@Override
public void onBubbleBarLocationUpdated(BubbleBarLocation location) {
updateCurrentBubbleBarLocation(location);
- if (!shouldMoveTaskbarOnBubbleBarLocationUpdate()) return;
- cancelTaskbarShiftAnimation();
- // reset translation x, taskbar will position icons with the updated location
- mIconsTranslationXForNavbar.updateValue(0);
- mTaskbarView.onBubbleBarLocationUpdated(location);
+ if (mActivity.isTransientTaskbar()) {
+ translateTaskbarXForBubbleBar(/* animate= */ false);
+ } else if (mActivity.shouldStartAlignTaskbar()) {
+ cancelTaskbarShiftAnimation();
+ // reset translation x, taskbar will position icons with the updated location
+ mIconsTranslationXForNavbar.updateValue(0);
+ mTaskbarView.onBubbleBarLocationUpdated(location);
+ }
}
/** Animates start aligned taskbar accordingly to the bubble bar position. */
@Override
public void onBubbleBarLocationAnimated(BubbleBarLocation location) {
- if (!updateCurrentBubbleBarLocation(location)
- || !shouldMoveTaskbarOnBubbleBarLocationUpdate()) {
- return;
+ boolean locationUpdated = updateCurrentBubbleBarLocation(location);
+ if (mActivity.isTransientTaskbar()) {
+ translateTaskbarXForBubbleBar(/* animate= */ true);
+ } else if (locationUpdated && mActivity.shouldStartAlignTaskbar()) {
+ cancelTaskbarShiftAnimation();
+ float translationX = mTaskbarView.getTranslationXForBubbleBarPosition(location);
+ mTaskbarShiftXAnim = createTaskbarIconsShiftAnimator(translationX);
+ mTaskbarShiftXAnim.start();
}
- cancelTaskbarShiftAnimation();
- float translationX = mTaskbarView.getTranslationXForBubbleBarPosition(location);
- mTaskbarShiftXAnim = createTaskbarIconsShiftAnimator(translationX);
- mTaskbarShiftXAnim.start();
+ }
+
+ private void translateTaskbarXForBubbleBar(boolean animate) {
+ cancelCurrentTranslationXAnimation();
+ if (!mActivity.isTransientTaskbar()) return;
+ int shiftX = getTransientTaskbarShiftXForBubbleBar();
+ if (animate) {
+ mTranslationXAnimation = mTranslationXForBubbleBar.animateToValue(shiftX);
+ mTranslationXAnimation.setInterpolator(EMPHASIZED);
+ mTranslationXAnimation.setDuration(TRANSLATION_X_FOR_BUBBLEBAR_ANIM_DURATION_MS);
+ mTranslationXAnimation.start();
+ } else {
+ mTranslationXForBubbleBar.updateValue(shiftX);
+ }
+ }
+
+ private void cancelCurrentTranslationXAnimation() {
+ if (mTranslationXAnimation != null) {
+ if (mTranslationXAnimation.isRunning()) {
+ mTranslationXAnimation.cancel();
+ }
+ mTranslationXAnimation = null;
+ }
+ }
+
+ private int getTransientTaskbarShiftXForBubbleBar() {
+ if (mBubbleControllers == null || !mActivity.isTransientTaskbar()) {
+ return 0;
+ }
+ return mBubbleControllers.bubbleBarViewController
+ .getTransientTaskbarTranslationXForBubbleBar(mCurrentBubbleBarLocation);
}
/** Updates the mCurrentBubbleBarLocation, returns {@code} true if location is updated. */
@@ -300,13 +348,6 @@
}
}
- /** Returns whether taskbar should be moved on the bubble bar location update. */
- private boolean shouldMoveTaskbarOnBubbleBarLocationUpdate() {
- return mControllers.bubbleControllers.isPresent()
- && mActivity.shouldStartAlignTaskbar()
- && mActivity.isThreeButtonNav();
- }
-
private void cancelTaskbarShiftAnimation() {
if (mTaskbarShiftXAnim != null) {
mTaskbarShiftXAnim.cancel();
@@ -450,16 +491,26 @@
}
void updateTaskbarIconTranslationXForPinning() {
+ updateTaskbarIconTranslationXForPinning(/* updateShiftXForBubbleBar = */ true);
+ }
+
+ void updateTaskbarIconTranslationXForPinning(boolean updateShiftXForBubbleBar) {
View[] iconViews = mTaskbarView.getIconViews();
float scale = mTaskbarIconTranslationXForPinning.value;
float transientTaskbarAllAppsOffset = mActivity.getResources().getDimension(
mTaskbarView.getAllAppsButtonContainer().getAllAppsButtonTranslationXOffset(true));
float persistentTaskbarAllAppsOffset = mActivity.getResources().getDimension(
mTaskbarView.getAllAppsButtonContainer().getAllAppsButtonTranslationXOffset(false));
-
+ if (mBubbleControllers != null && updateShiftXForBubbleBar) {
+ cancelCurrentTranslationXAnimation();
+ int translationXForTransientTaskbar = mBubbleControllers.bubbleBarViewController
+ .getTransientTaskbarTranslationXForBubbleBar(mCurrentBubbleBarLocation);
+ float currentTranslationXForTransientTaskbar = mapRange(scale,
+ translationXForTransientTaskbar, 0);
+ mTranslationXForBubbleBar.updateValue(currentTranslationXForTransientTaskbar);
+ }
float allAppIconTranslateRange = mapRange(scale, transientTaskbarAllAppsOffset,
persistentTaskbarAllAppsOffset);
-
// Task icons are laid out so the taskbar content is centered. The taskbar width (used for
// centering taskbar icons) depends on the all apps button X translation, and is different
// for persistent and transient taskbar. If the offset used for current taskbar layout is
@@ -551,13 +602,23 @@
}
private void updateTranslationXForNavBar() {
+ updateIconViewsTranslationX(INDEX_NAV_BAR_ANIM, mIconsTranslationXForNavbar.value);
+ }
+
+ private void updateTranslationXForBubbleBar() {
+ float translationX = mTranslationXForBubbleBar.value;
+ updateIconViewsTranslationX(INDEX_BUBBLE_BAR_ANIM, translationX);
+ if (mDragLayerController != null) {
+ mDragLayerController.setTranslationXForBubbleBar(translationX);
+ }
+ }
+
+ private void updateIconViewsTranslationX(int translationXChannel, float translationX) {
View[] iconViews = mTaskbarView.getIconViews();
- float translationX = mIconsTranslationXForNavbar.value;
- for (int iconIndex = 0; iconIndex < iconViews.length; iconIndex++) {
- View iconView = iconViews[iconIndex];
+ for (View iconView : iconViews) {
MultiTranslateDelegate translateDelegate =
((Reorderable) iconView).getTranslateDelegate();
- translateDelegate.getTranslationX(INDEX_NAV_BAR_ANIM).setValue(translationX);
+ translateDelegate.getTranslationX(translationXChannel).setValue(translationX);
}
}
@@ -811,6 +872,13 @@
if (mTaskbarView.updateMaxNumIcons()) {
commitRunningAppsToUI();
}
+ adjustTaskbarXForBubbleBar();
+ }
+
+ private void adjustTaskbarXForBubbleBar() {
+ if (mBubbleControllers != null && mActivity.isTransientTaskbar()) {
+ translateTaskbarXForBubbleBar(/* animate= */ true);
+ }
}
/**
@@ -841,7 +909,17 @@
setter.setFloat(mTaskbarIconTranslationYForHome, VALUE, -offsetY, interpolator);
setter.setFloat(mTaskbarNavButtonTranslationY, VALUE, -offsetY, interpolator);
setter.setFloat(mTaskbarNavButtonTranslationYForInAppDisplay, VALUE, offsetY, interpolator);
-
+ if (mBubbleControllers != null
+ && mCurrentBubbleBarLocation != null
+ && mActivity.isTransientTaskbar()) {
+ int offsetX = mBubbleControllers.bubbleBarViewController
+ .getTransientTaskbarTranslationXForBubbleBar(mCurrentBubbleBarLocation);
+ if (offsetX != 0) {
+ // if taskbar should be adjusted for the bubble bar adjust the taskbar translation
+ mTranslationXForBubbleBar.updateValue(offsetX);
+ setter.setFloat(mTranslationXForBubbleBar, VALUE, 0, interpolator);
+ }
+ }
int collapsedHeight = mActivity.getDefaultTaskbarWindowSize();
int expandedHeight = Math.max(collapsedHeight, taskbarDp.taskbarHeight + offsetY);
setter.addOnFrameListener(anim -> mActivity.setTaskbarWindowSize(
@@ -1042,8 +1120,8 @@
}
private boolean bubbleBarHasBubbles() {
- return mControllers.bubbleControllers.isPresent()
- && mControllers.bubbleControllers.get().bubbleBarViewController.hasBubbles();
+ return mBubbleControllers != null
+ && mBubbleControllers.bubbleBarViewController.hasBubbles();
}
public void onRotationChanged(DeviceProfile deviceProfile) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
index b63cf02..7d39bf8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
@@ -342,27 +342,40 @@
mBubbleBarViewController.showOverflow(update.showOverflow);
}
+ if (update.addedBubble != null) {
+ mBubbles.put(update.addedBubble.getKey(), update.addedBubble);
+ }
BubbleBarBubble bubbleToSelect = null;
-
+ if (update.selectedBubbleKey != null) {
+ if (mSelectedBubble == null
+ || !update.selectedBubbleKey.equals(mSelectedBubble.getKey())) {
+ BubbleBarBubble newlySelected = mBubbles.get(update.selectedBubbleKey);
+ if (newlySelected != null) {
+ bubbleToSelect = newlySelected;
+ } else {
+ Log.w(TAG, "trying to select bubble that doesn't exist:"
+ + update.selectedBubbleKey);
+ }
+ }
+ }
if (Flags.enableOptionalBubbleOverflow()
&& update.showOverflowChanged && !update.showOverflow && update.addedBubble != null
&& update.removedBubbles.isEmpty()
&& !mBubbles.isEmpty()) {
// A bubble was added from the overflow (& now it's empty / not showing)
- mBubbles.put(update.addedBubble.getKey(), update.addedBubble);
- mBubbleBarViewController.removeOverflowAndAddBubble(update.addedBubble);
+ mBubbleBarViewController.removeOverflowAndAddBubble(update.addedBubble, bubbleToSelect);
} else if (update.addedBubble != null && update.removedBubbles.size() == 1) {
// we're adding and removing a bubble at the same time. handle this as a single update.
RemovedBubble removedBubble = update.removedBubbles.get(0);
BubbleBarBubble bubbleToRemove = mBubbles.remove(removedBubble.getKey());
- mBubbles.put(update.addedBubble.getKey(), update.addedBubble);
boolean showOverflow = update.showOverflowChanged && update.showOverflow;
if (bubbleToRemove != null) {
mBubbleBarViewController.addBubbleAndRemoveBubble(update.addedBubble,
- bubbleToRemove, isExpanding, suppressAnimation, showOverflow);
+ bubbleToRemove, bubbleToSelect, isExpanding, suppressAnimation,
+ showOverflow);
} else {
mBubbleBarViewController.addBubble(update.addedBubble, isExpanding,
- suppressAnimation);
+ suppressAnimation, bubbleToSelect);
Log.w(TAG, "trying to remove bubble that doesn't exist: " + removedBubble.getKey());
}
} else {
@@ -375,7 +388,7 @@
if (bubble != null && overflowNeedsToBeAdded) {
// First removal, show the overflow
overflowNeedsToBeAdded = false;
- mBubbleBarViewController.addOverflowAndRemoveBubble(bubble);
+ mBubbleBarViewController.addOverflowAndRemoveBubble(bubble, bubbleToSelect);
} else if (bubble != null) {
mBubbleBarViewController.removeBubble(bubble);
} else {
@@ -385,9 +398,8 @@
}
}
if (update.addedBubble != null) {
- mBubbles.put(update.addedBubble.getKey(), update.addedBubble);
mBubbleBarViewController.addBubble(update.addedBubble, isExpanding,
- suppressAnimation);
+ suppressAnimation, bubbleToSelect);
}
if (Flags.enableOptionalBubbleOverflow()
&& update.showOverflowChanged
@@ -401,7 +413,7 @@
addBubbleInternally(update.updatedBubble, isExpanding, suppressAnimation);
}
- if (update.addedBubble != null && isCollapsed) {
+ if (update.addedBubble != null && isCollapsed && bubbleToSelect == null) {
// If we're collapsed, the most recently added bubble will be selected.
bubbleToSelect = update.addedBubble;
}
@@ -412,7 +424,7 @@
BubbleBarBubble bubble = update.currentBubbles.get(i);
if (bubble != null) {
addBubbleInternally(bubble, isExpanding, suppressAnimation);
- if (isCollapsed) {
+ if (isCollapsed && bubbleToSelect == null) {
// If we're collapsed, the most recently added bubble will be selected.
bubbleToSelect = bubble;
}
@@ -479,18 +491,6 @@
mBubbleBarViewController.reorderBubbles(newOrder);
}
}
- if (update.selectedBubbleKey != null) {
- if (mSelectedBubble == null
- || !update.selectedBubbleKey.equals(mSelectedBubble.getKey())) {
- BubbleBarBubble newlySelected = mBubbles.get(update.selectedBubbleKey);
- if (newlySelected != null) {
- bubbleToSelect = newlySelected;
- } else {
- Log.w(TAG, "trying to select bubble that doesn't exist:"
- + update.selectedBubbleKey);
- }
- }
- }
if (bubbleToSelect != null) {
setSelectedBubbleInternal(bubbleToSelect);
}
@@ -609,7 +609,8 @@
private void addBubbleInternally(BubbleBarBubble bubble, boolean isExpanding,
boolean suppressAnimation) {
mBubbles.put(bubble.getKey(), bubble);
- mBubbleBarViewController.addBubble(bubble, isExpanding, suppressAnimation);
+ mBubbleBarViewController.addBubble(bubble, isExpanding,
+ suppressAnimation, /* bubbleToSelect = */ null);
}
/** Listener of {@link BubbleBarLocation} updates. */
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
index 0d0feff..c001123 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
@@ -681,8 +681,18 @@
return mRelativePivotY;
}
- /** Add a new bubble to the bubble bar. */
+ /** Add a new bubble to the bubble bar without updating the selected bubble. */
public void addBubble(BubbleView bubble) {
+ addBubble(bubble, /* bubbleToSelect = */ null);
+ }
+
+ /**
+ * Add a new bubble to the bubble bar and selects the provided bubble.
+ *
+ * @param bubble bubble to add
+ * @param bubbleToSelect if {@code null}, then selected bubble does not change
+ */
+ public void addBubble(BubbleView bubble, @Nullable BubbleView bubbleToSelect) {
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams((int) mIconSize, (int) mIconSize,
Gravity.LEFT);
final int index = bubble.isOverflow() ? getChildCount() : 0;
@@ -718,7 +728,12 @@
invalidate();
}
};
- mBubbleAnimator.animateNewBubble(indexOfChild(mSelectedBubbleView), listener);
+ if (bubbleToSelect != null) {
+ mBubbleAnimator.animateNewBubble(indexOfChild(mSelectedBubbleView),
+ indexOfChild(bubbleToSelect), listener);
+ } else {
+ mBubbleAnimator.animateNewBubble(indexOfChild(mSelectedBubbleView), listener);
+ }
} else {
addView(bubble, index, lp);
}
@@ -726,35 +741,32 @@
/** Add a new bubble and remove an old bubble from the bubble bar. */
public void addBubbleAndRemoveBubble(BubbleView addedBubble, BubbleView removedBubble,
- Runnable onEndRunnable) {
+ @Nullable BubbleView bubbleToSelect, Runnable onEndRunnable) {
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams((int) mIconSize, (int) mIconSize,
Gravity.LEFT);
- boolean isOverflowSelected =
- mSelectedBubbleView != null && mSelectedBubbleView.isOverflow();
- boolean removingOverflow = removedBubble.isOverflow();
- boolean addingOverflow = addedBubble.isOverflow();
-
+ int addedIndex = addedBubble.isOverflow() ? getChildCount() : 0;
if (!isExpanded()) {
removeView(removedBubble);
- int index = addingOverflow ? getChildCount() : 0;
- addView(addedBubble, index, lp);
+ addView(addedBubble, addedIndex, lp);
if (onEndRunnable != null) {
onEndRunnable.run();
}
return;
}
- int index = addingOverflow ? getChildCount() : 0;
addedBubble.setScaleX(0f);
addedBubble.setScaleY(0f);
- addView(addedBubble, index, lp);
-
- if (isOverflowSelected && removingOverflow) {
- // The added bubble will be selected
- mSelectedBubbleView = addedBubble;
- }
- int indexOfSelectedBubble = indexOfChild(mSelectedBubbleView);
+ addView(addedBubble, addedIndex, lp);
+ int indexOfCurrentSelectedBubble = indexOfChild(mSelectedBubbleView);
int indexOfBubbleToRemove = indexOfChild(removedBubble);
-
+ int indexOfNewlySelectedBubble = bubbleToSelect == null
+ ? indexOfCurrentSelectedBubble : indexOfChild(bubbleToSelect);
+ // Since removed bubble is kept till the end of the animation we should check if there are
+ // more than one bubble. In such a case the bar will remain open without the selected bubble
+ if (mSelectedBubbleView == removedBubble
+ && bubbleToSelect == null
+ && getBubbleChildCount() > 1) {
+ Log.w(TAG, "Remove the currently selected bubble without selecting a new one.");
+ }
mBubbleAnimator = new BubbleAnimator(mIconSize, mExpandedBarIconsSpacing,
getChildCount(), mBubbleBarLocation.isOnLeft(isLayoutRtl()));
BubbleAnimator.Listener listener = new BubbleAnimator.Listener() {
@@ -787,8 +799,8 @@
invalidate();
}
};
- mBubbleAnimator.animateNewAndRemoveOld(indexOfSelectedBubble, indexOfBubbleToRemove,
- listener);
+ mBubbleAnimator.animateNewAndRemoveOld(indexOfCurrentSelectedBubble,
+ indexOfNewlySelectedBubble, indexOfBubbleToRemove, addedIndex, listener);
}
@Override
@@ -1327,6 +1339,14 @@
return getScaledIconSize() + mIconOverlapAmount + 2 * mBubbleBarPadding;
}
+ float getCollapsedWidthForIconSizeAndPadding(int iconSize, int bubbleBarPadding) {
+ final int bubbleChildCount = Math.min(getBubbleChildCount(), MAX_VISIBLE_BUBBLES_COLLAPSED);
+ if (bubbleChildCount == 0) return 0;
+ final int spacesCount = bubbleChildCount - 1;
+ final float horizontalPadding = 2 * bubbleBarPadding;
+ return iconSize * bubbleChildCount + mIconOverlapAmount * spacesCount + horizontalPadding;
+ }
+
/** Returns the child count excluding the overflow if it's present. */
int getBubbleChildCount() {
return hasOverflow() ? getChildCount() - 1 : getChildCount();
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index 569dd56..afbc932 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -122,7 +122,8 @@
private float mBubbleBarSwipeUpTranslationY;
// Modified when bubble bar is springing back into the stash handle.
private float mBubbleBarStashTranslationY;
-
+ // Minimum distance between the BubbleBar and the taskbar
+ private final int mBubbleBarTaskbarMinDistance;
// Whether the bar is hidden for a sysui state.
private boolean mHiddenForSysui;
// Whether the bar is hidden because there are no bubbles.
@@ -150,10 +151,11 @@
mBubbleBarContainer = bubbleBarContainer;
mSystemUiProxy = SystemUiProxy.INSTANCE.get(mActivity);
mBubbleBarAlpha = new MultiValueAlpha(mBarView, 1 /* num alpha channels */);
- mIconSize = activity.getResources().getDimensionPixelSize(
- R.dimen.bubblebar_icon_size);
- mDragElevation = activity.getResources().getDimensionPixelSize(
- R.dimen.bubblebar_drag_elevation);
+ Resources res = activity.getResources();
+ mIconSize = res.getDimensionPixelSize(R.dimen.bubblebar_icon_size);
+ mBubbleBarTaskbarMinDistance = res.getDimensionPixelSize(
+ R.dimen.bubblebar_transient_taskbar_min_distance);
+ mDragElevation = res.getDimensionPixelSize(R.dimen.bubblebar_drag_elevation);
mTaskbarTranslationDelta = getBubbleBarTranslationDeltaForTaskbar(activity);
}
@@ -481,7 +483,7 @@
/** Whether the bubble bar has bubbles. */
public boolean hasBubbles() {
- return mBubbleBarController.getSelectedBubbleKey() != null;
+ return mBarView.getBubbleChildCount() > 0;
}
/**
@@ -664,6 +666,45 @@
}
}
+ /**
+ * Returns the translation X of the transient taskbar according to the bubble bar location
+ * regardless of the current taskbar mode.
+ */
+ public int getTransientTaskbarTranslationXForBubbleBar(BubbleBarLocation location) {
+ int taskbarShift = 0;
+ if (!isBubbleBarVisible() || mTaskbarViewPropertiesProvider == null) return taskbarShift;
+ Rect taskbarViewBounds = mTaskbarViewPropertiesProvider.getTaskbarViewBounds();
+ if (taskbarViewBounds.isEmpty()) return taskbarShift;
+ int actualDistance =
+ getDistanceBetweenTransientTaskbarAndBubbleBar(location, taskbarViewBounds);
+ if (actualDistance < mBubbleBarTaskbarMinDistance) {
+ taskbarShift = mBubbleBarTaskbarMinDistance - actualDistance;
+ if (!location.isOnLeft(mBarView.isLayoutRtl())) {
+ taskbarShift = -taskbarShift;
+ }
+ }
+ return taskbarShift;
+ }
+
+ private int getDistanceBetweenTransientTaskbarAndBubbleBar(BubbleBarLocation location,
+ Rect taskbarViewBounds) {
+ Resources res = mActivity.getResources();
+ DeviceProfile transientDp = mActivity.getTransientTaskbarDeviceProfile();
+ int transientIconSize = getBubbleBarIconSizeFromDeviceProfile(res, transientDp);
+ int transientPadding = getBubbleBarPaddingFromDeviceProfile(res, transientDp);
+ int transientWidthWithMargin = (int) (mBarView.getCollapsedWidthForIconSizeAndPadding(
+ transientIconSize, transientPadding) + mBarView.getHorizontalMargin());
+ int distance;
+ if (location.isOnLeft(mBarView.isLayoutRtl())) {
+ distance = taskbarViewBounds.left - transientWidthWithMargin;
+ } else {
+ int displayWidth = res.getDisplayMetrics().widthPixels;
+ int bubbleBarLeft = displayWidth - transientWidthWithMargin;
+ distance = bubbleBarLeft - taskbarViewBounds.right;
+ }
+ return distance;
+ }
+
//
// Modifying view related properties.
//
@@ -843,11 +884,12 @@
}
/** Adds a new bubble and removes an old bubble at the same time. */
- public void addBubbleAndRemoveBubble(BubbleBarBubble addedBubble,
- BubbleBarBubble removedBubble, boolean isExpanding, boolean suppressAnimation,
- boolean addOverflowToo) {
+ public void addBubbleAndRemoveBubble(BubbleBarBubble addedBubble, BubbleBarBubble removedBubble,
+ @Nullable BubbleBarBubble bubbleToSelect, boolean isExpanding,
+ boolean suppressAnimation, boolean addOverflowToo) {
+ BubbleView bubbleToSelectView = bubbleToSelect == null ? null : bubbleToSelect.getView();
mBarView.addBubbleAndRemoveBubble(addedBubble.getView(), removedBubble.getView(),
- addOverflowToo ? () -> showOverflow(true) : null);
+ bubbleToSelectView, addOverflowToo ? () -> showOverflow(true) : null);
addedBubble.getView().setOnClickListener(mBubbleClickListener);
addedBubble.getView().setController(mBubbleViewController);
removedBubble.getView().setController(null);
@@ -878,22 +920,26 @@
}
/** Adds the overflow view to the bubble bar while animating a view away. */
- public void addOverflowAndRemoveBubble(BubbleBarBubble removedBubble) {
+ public void addOverflowAndRemoveBubble(BubbleBarBubble removedBubble,
+ @Nullable BubbleBarBubble bubbleToSelect) {
if (mOverflowAdded) return;
mOverflowAdded = true;
+ BubbleView bubbleToSelectView = bubbleToSelect == null ? null : bubbleToSelect.getView();
mBarView.addBubbleAndRemoveBubble(mOverflowBubble.getView(), removedBubble.getView(),
- null /* onEndRunnable */);
+ bubbleToSelectView, null /* onEndRunnable */);
mOverflowBubble.getView().setOnClickListener(mBubbleClickListener);
mOverflowBubble.getView().setController(mBubbleViewController);
removedBubble.getView().setController(null);
}
/** Removes the overflow view to the bubble bar while animating a view in. */
- public void removeOverflowAndAddBubble(BubbleBarBubble addedBubble) {
+ public void removeOverflowAndAddBubble(BubbleBarBubble addedBubble,
+ @Nullable BubbleBarBubble bubbleToSelect) {
if (!mOverflowAdded) return;
mOverflowAdded = false;
+ BubbleView bubbleToSelectView = bubbleToSelect == null ? null : bubbleToSelect.getView();
mBarView.addBubbleAndRemoveBubble(addedBubble.getView(), mOverflowBubble.getView(),
- null /* onEndRunnable */);
+ bubbleToSelectView, null /* onEndRunnable */);
addedBubble.getView().setOnClickListener(mBubbleClickListener);
addedBubble.getView().setController(mBubbleViewController);
mOverflowBubble.getView().setController(null);
@@ -902,9 +948,15 @@
/**
* Adds the provided bubble to the bubble bar.
*/
- public void addBubble(BubbleBarItem b, boolean isExpanding, boolean suppressAnimation) {
+ public void addBubble(BubbleBarItem b,
+ boolean isExpanding,
+ boolean suppressAnimation,
+ @Nullable BubbleBarBubble bubbleToSelect
+ ) {
if (b != null) {
- mBarView.addBubble(b.getView());
+ BubbleView bubbleToSelectView =
+ bubbleToSelect == null ? null : bubbleToSelect.getView();
+ mBarView.addBubble(b.getView(), bubbleToSelectView);
b.getView().setOnClickListener(mBubbleClickListener);
mBubbleDragController.setupBubbleView(b.getView());
b.getView().setController(mBubbleViewController);
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
index fd4cf0e..0abd88c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
@@ -299,7 +299,7 @@
private final PointF mTouchDownLocation = new PointF();
private final PointF mViewInitialPosition = new PointF();
private final VelocityTracker mVelocityTracker = VelocityTracker.obtain();
- private final long mPressToDragTimeout = ViewConfiguration.getLongPressTimeout() / 2;
+ private final long mPressToDragTimeout = ViewConfiguration.getLongPressTimeout();
private State mState = State.IDLE;
private int mTouchSlop = -1;
private BubbleDragAnimator mAnimator;
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleAnimator.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleAnimator.kt
index 3604167..26d6ccc 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleAnimator.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleAnimator.kt
@@ -18,6 +18,8 @@
import androidx.core.animation.Animator
import androidx.core.animation.ValueAnimator
+import kotlin.math.max
+import kotlin.math.min
/**
* Animates individual bubbles within the bubble bar while the bubble bar is expanded.
@@ -39,9 +41,14 @@
private var state: State = State.Idle
private lateinit var animator: ValueAnimator
- fun animateNewBubble(selectedBubbleIndex: Int, listener: Listener) {
+ @JvmOverloads
+ fun animateNewBubble(
+ selectedBubbleIndex: Int,
+ newlySelectedBubbleIndex: Int? = null,
+ listener: Listener,
+ ) {
animator = createAnimator(listener)
- state = State.AddingBubble(selectedBubbleIndex)
+ state = State.AddingBubble(selectedBubbleIndex, newlySelectedBubbleIndex)
animator.start()
}
@@ -65,14 +72,18 @@
fun animateNewAndRemoveOld(
selectedBubbleIndex: Int,
+ newlySelectedBubbleIndex: Int,
removedBubbleIndex: Int,
+ addedBubbleIndex: Int,
listener: Listener,
) {
animator = createAnimator(listener)
state =
State.AddingAndRemoving(
selectedBubbleIndex = selectedBubbleIndex,
+ newlySelectedBubbleIndex = newlySelectedBubbleIndex,
removedBubbleIndex = removedBubbleIndex,
+ addedBubbleIndex = addedBubbleIndex,
)
animator.start()
}
@@ -132,6 +143,7 @@
getBubbleTranslationXWhileAddingBubbleAtLimit(
bubbleIndex = bubbleIndex,
removedBubbleIndex = state.removedBubbleIndex,
+ addedBubbleIndex = state.addedBubbleIndex,
addedBubbleScale = animator.animatedFraction,
removedBubbleScale = 1 - animator.animatedFraction,
)
@@ -180,31 +192,33 @@
fun getArrowPosition(): Float {
return when (val state = state) {
State.Idle -> 0f
- is State.AddingBubble -> {
- val tx =
- getBubbleTranslationXWhileScalingBubble(
- bubbleIndex = state.selectedBubbleIndex,
- scalingBubbleIndex = 0,
- bubbleScale = animator.animatedFraction,
- )
- tx + iconSize / 2f
- }
-
+ is State.AddingBubble -> getArrowPositionWhenAddingBubble(state)
is State.RemovingBubble -> getArrowPositionWhenRemovingBubble(state)
- is State.AddingAndRemoving -> {
- // we never remove the selected bubble, so the arrow stays pointing to its center
- val tx =
- getBubbleTranslationXWhileAddingBubbleAtLimit(
- bubbleIndex = state.selectedBubbleIndex,
- removedBubbleIndex = state.removedBubbleIndex,
- addedBubbleScale = animator.animatedFraction,
- removedBubbleScale = 1 - animator.animatedFraction,
- )
- tx + iconSize / 2f
- }
+ is State.AddingAndRemoving -> getArrowPositionWhenAddingAndRemovingBubble(state)
}
}
+ private fun getArrowPositionWhenAddingBubble(state: State.AddingBubble): Float {
+ val scale = animator.animatedFraction
+ var tx =
+ getBubbleTranslationXWhileScalingBubble(
+ bubbleIndex = state.selectedBubbleIndex,
+ scalingBubbleIndex = 0,
+ bubbleScale = scale,
+ ) + iconSize / 2f
+ if (state.newlySelectedBubbleIndex != null) {
+ val selectedBubbleScale = if (state.newlySelectedBubbleIndex == 0) scale else 1f
+ val finalTx =
+ getBubbleTranslationXWhileScalingBubble(
+ bubbleIndex = state.newlySelectedBubbleIndex,
+ scalingBubbleIndex = 0,
+ bubbleScale = scale,
+ ) + iconSize * selectedBubbleScale / 2f
+ tx += (finalTx - tx) * animator.animatedFraction
+ }
+ return tx
+ }
+
private fun getArrowPositionWhenRemovingBubble(state: State.RemovingBubble): Float =
if (state.selectedBubbleIndex != state.bubbleIndex || state.removingLastRemainingBubble) {
// if we're not removing the selected bubble or if we're removing the last remaining
@@ -250,6 +264,46 @@
}
}
+ private fun getArrowPositionWhenAddingAndRemovingBubble(state: State.AddingAndRemoving): Float {
+ // The bubble bar keeps constant width while adding and removing bubble. So we just need to
+ // find selected bubble arrow position on the animation start and newly selected bubble
+ // arrow position on the animation end interpolating the arrow between these positions
+ // during the animation.
+ // The indexes in the state are provided for the bubble bar containing all bubbles. So for
+ // certain circumstances indexes should be adjusted.
+ // When animation is started added bubble has zero scale as well as removed bubble when the
+ // animation is ended, so for both cases we should compute translation as it is one less
+ // bubble.
+ val bubbleCountOnEnd = bubbleCount - 1
+ var selectedIndex = state.selectedBubbleIndex
+ // We only need to adjust the selected index if added bubble was added before the selected.
+ if (selectedIndex > state.addedBubbleIndex) {
+ // If the selectedIndex is higher index than the added bubble index, we need to reduce
+ // selectedIndex by one because the added bubble has zero scale when animation is
+ // started.
+ selectedIndex--
+ }
+ var newlySelectedIndex = state.newlySelectedBubbleIndex
+ // We only need to adjust newlySelectedIndex if removed bubble was removed before the newly
+ // selected bubble.
+ if (newlySelectedIndex > state.removedBubbleIndex) {
+ // If the newlySelectedIndex is higher index than the removed bubble index, we need to
+ // reduce newlySelectedIndex by one because the removed bubble has zero scale when
+ // animation is ended.
+ newlySelectedIndex--
+ }
+ val iconAndSpacing: Float = iconSize + expandedBarIconSpacing
+ val startTx = getBubblesToTheLeft(selectedIndex, bubbleCountOnEnd) * iconAndSpacing
+ val endTx = getBubblesToTheLeft(newlySelectedIndex, bubbleCountOnEnd) * iconAndSpacing
+ val tx = startTx + (endTx - startTx) * animator.animatedFraction
+ return tx + iconSize / 2f
+ }
+
+ private fun getBubblesToTheLeft(bubbleIndex: Int, bubbleCount: Int = this.bubbleCount): Int =
+ // when bar is on left the index - 0 corresponds to the right - most bubble and when the
+ // bubble bar is on the right - 0 corresponds to the left - most bubble.
+ if (onLeft) bubbleCount - bubbleIndex - 1 else bubbleIndex
+
/**
* Returns the translation X for the bubble at index {@code bubbleIndex} when the bubble bar is
* expanded and a bubble is animating in or out.
@@ -274,6 +328,7 @@
// the bar is on the left and the current bubble is to the right of the scaling
// bubble so account for its scale
(bubbleCount - bubbleIndex - 2 + bubbleScale) * iconAndSpacing
+
bubbleIndex == scalingBubbleIndex -> {
// the bar is on the left and this is the scaling bubble
val totalIconSize = (bubbleCount - bubbleIndex - 1) * iconSize
@@ -283,6 +338,7 @@
val scaledSpace = bubbleScale * expandedBarIconSpacing
totalIconSize + totalSpacing + scaledSpace + pivotAdjustment
}
+
else ->
// the bar is on the left and the scaling bubble is on the right. the current
// bubble is unaffected by the scaling bubble
@@ -294,10 +350,12 @@
// the bar is on the right and the scaling bubble is on the right. the current
// bubble is unaffected by the scaling bubble
iconAndSpacing * bubbleIndex
+
bubbleIndex == scalingBubbleIndex ->
// the bar is on the right, and this is the animating bubble. it only needs to
// be adjusted for the scaling pivot.
iconAndSpacing * bubbleIndex + pivotAdjustment
+
else ->
// the bar is on the right and the scaling bubble is on the left so account for
// its scale
@@ -309,61 +367,67 @@
private fun getBubbleTranslationXWhileAddingBubbleAtLimit(
bubbleIndex: Int,
removedBubbleIndex: Int,
+ addedBubbleIndex: Int,
addedBubbleScale: Float,
removedBubbleScale: Float,
): Float {
val iconAndSpacing = iconSize + expandedBarIconSpacing
// the bubbles are scaling from the center, so we need to adjust their translation so
// that the distance to the adjacent bubble scales at the same rate.
- val addedBubblePivotAdjustment = -(1 - addedBubbleScale) * iconSize / 2f
- val removedBubblePivotAdjustment = -(1 - removedBubbleScale) * iconSize / 2f
+ val addedBubblePivotAdjustment = (addedBubbleScale - 1) * iconSize / 2f
+ val removedBubblePivotAdjustment = (removedBubbleScale - 1) * iconSize / 2f
- return if (onLeft) {
- // this is how many bubbles there are to the left of the current bubble.
- // when the bubble bar is on the right the added bubble is the right-most bubble so it
- // doesn't affect the translation of any other bubble.
- // when the removed bubble is to the left of the current bubble, we need to subtract it
- // from bubblesToLeft and use removedBubbleScale instead when calculating the
- // translation.
- val bubblesToLeft = bubbleCount - bubbleIndex - 1
- when {
- bubbleIndex == 0 ->
- // this is the added bubble and it's the right-most bubble. account for all the
- // other bubbles -- including the removed bubble -- and adjust for the added
- // bubble pivot.
- (bubblesToLeft - 1 + removedBubbleScale) * iconAndSpacing +
- addedBubblePivotAdjustment
- bubbleIndex < removedBubbleIndex ->
+ val minAddedRemovedIndex = min(addedBubbleIndex, removedBubbleIndex)
+ val maxAddedRemovedIndex = max(addedBubbleIndex, removedBubbleIndex)
+ val isBetweenAddedAndRemoved =
+ bubbleIndex in (minAddedRemovedIndex + 1)..<maxAddedRemovedIndex
+ val isRemovedBubbleToLeftOfAddedBubble = onLeft == addedBubbleIndex < removedBubbleIndex
+ val bubblesToLeft = getBubblesToTheLeft(bubbleIndex)
+ return when {
+ isBetweenAddedAndRemoved -> {
+ if (isRemovedBubbleToLeftOfAddedBubble) {
// the removed bubble is to the left so account for it
(bubblesToLeft - 1 + removedBubbleScale) * iconAndSpacing
- bubbleIndex == removedBubbleIndex -> {
- // this is the removed bubble. all the bubbles to the left are at full scale
- // but we need to scale the spacing between the removed bubble and the bubble to
- // its left because the removed bubble disappears towards the left side
+ } else {
+ // the added bubble is to the left so account for it
+ (bubblesToLeft - 1 + addedBubbleScale) * iconAndSpacing
+ }
+ }
+
+ bubbleIndex == addedBubbleIndex -> {
+ if (isRemovedBubbleToLeftOfAddedBubble) {
+ // the removed bubble is to the left so account for it
+ (bubblesToLeft - 1 + removedBubbleScale) * iconAndSpacing
+ } else {
+ // it's the left-most scaling bubble, all bubbles on the left are at full scale
+ bubblesToLeft * iconAndSpacing
+ } + addedBubblePivotAdjustment
+ }
+
+ bubbleIndex == removedBubbleIndex -> {
+ if (isRemovedBubbleToLeftOfAddedBubble) {
+ // All the bubbles to the left are at full scale, but we need to scale the
+ // spacing between the removed bubble and the bubble next to it
val totalIconSize = bubblesToLeft * iconSize
val totalSpacing =
(bubblesToLeft - 1 + removedBubbleScale) * expandedBarIconSpacing
- totalIconSize + totalSpacing + removedBubblePivotAdjustment
- }
- else ->
- // both added and removed bubbles are to the right so they don't affect the tx
- bubblesToLeft * iconAndSpacing
+ totalIconSize + totalSpacing
+ } else {
+ // The added bubble is to the left, so account for it
+ (bubblesToLeft - 1 + addedBubbleScale) * iconAndSpacing
+ } + removedBubblePivotAdjustment
}
- } else {
- when {
- bubbleIndex == 0 -> addedBubblePivotAdjustment // we always add bubbles at index 0
- bubbleIndex < removedBubbleIndex ->
- // the bar is on the right and the removed bubble is on the right. the current
- // bubble is unaffected by the removed bubble. only need to factor in the added
- // bubble's scale.
- iconAndSpacing * (bubbleIndex - 1 + addedBubbleScale)
- bubbleIndex == removedBubbleIndex ->
- // the bar is on the right, and this is the animating bubble.
- iconAndSpacing * (bubbleIndex - 1 + addedBubbleScale) +
- removedBubblePivotAdjustment
- else ->
- // both the added and the removed bubbles are to the left of the current bubble
- iconAndSpacing * (bubbleIndex - 2 + addedBubbleScale + removedBubbleScale)
+
+ else -> {
+ // if bubble index is on the right side of the animated bubbles, we need to deduct
+ // one, since both the added and the removed bubbles takes a single place
+ val onTheRightOfAnimatedBubbles =
+ if (onLeft) {
+ bubbleIndex < minAddedRemovedIndex
+ } else {
+ bubbleIndex > maxAddedRemovedIndex
+ }
+ (bubblesToLeft - if (onTheRightOfAnimatedBubbles) 1 else 0) * iconAndSpacing
}
}
}
@@ -378,7 +442,12 @@
data object Idle : State
/** A new bubble is being added to the bubble bar. */
- data class AddingBubble(val selectedBubbleIndex: Int) : State
+ data class AddingBubble(
+ /** The index of the selected bubble. */
+ val selectedBubbleIndex: Int,
+ /** The index of the newly selected bubble. */
+ val newlySelectedBubbleIndex: Int?,
+ ) : State
/** A bubble is being removed from the bubble bar. */
data class RemovingBubble(
@@ -393,8 +462,16 @@
) : State
/** A new bubble is being added and an old bubble is being removed from the bubble bar. */
- data class AddingAndRemoving(val selectedBubbleIndex: Int, val removedBubbleIndex: Int) :
- State
+ data class AddingAndRemoving(
+ /** The index of the selected bubble. */
+ val selectedBubbleIndex: Int,
+ /** The index of the newly selected bubble. */
+ val newlySelectedBubbleIndex: Int,
+ /** The index of the bubble being removed. */
+ val removedBubbleIndex: Int,
+ /** The index of the added bubble. */
+ val addedBubbleIndex: Int,
+ ) : State
}
/** Callbacks for the animation. */
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt
index 595dac3..fec1eaf 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt
@@ -24,6 +24,7 @@
import com.android.launcher3.taskbar.bubbles.BubbleBarView
import com.android.launcher3.taskbar.bubbles.BubbleBarViewController
import com.android.launcher3.taskbar.bubbles.BubbleStashedHandleViewController
+import com.android.launcher3.util.MultiPropertyFactory
import com.android.wm.shell.shared.animation.PhysicsAnimator
import com.android.wm.shell.shared.bubbles.BubbleBarLocation
import java.io.PrintWriter
@@ -172,6 +173,9 @@
/** Returns bounds of the handle */
fun getHandleBounds(bounds: Rect)
+ /** Returns MultiValueAlpha of the handle view when the handle view is shown. */
+ fun getHandleViewAlpha(): MultiPropertyFactory<View>.MultiProperty? = null
+
/**
* Returns bubble bar Y position according to [isBubblesShowingOnHome] and
* [isBubblesShowingOnOverview] values. Default implementation only analyse
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt
index 3e3f569..9c148e2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt
@@ -512,6 +512,14 @@
}
}
+ override fun getHandleViewAlpha(): MultiPropertyFactory<View>.MultiProperty? =
+ // only return handle alpha if the bubble bar is stashed and has bubbles
+ if (isStashed && bubbleBarViewController.hasBubbles()) {
+ stashHandleViewAlpha
+ } else {
+ null
+ }
+
private fun Animator.updateTouchRegionOnAnimationEnd(): Animator {
doOnEnd { onIsStashedChanged() }
return this
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
deleted file mode 100644
index 721c831..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.uioverrides;
-
-import static com.android.app.animation.Interpolators.ACCELERATE_DECELERATE;
-import static com.android.app.animation.Interpolators.AGGRESSIVE_EASE_IN_OUT;
-import static com.android.app.animation.Interpolators.FINAL_FRAME;
-import static com.android.app.animation.Interpolators.INSTANT;
-import static com.android.app.animation.Interpolators.LINEAR;
-import static com.android.launcher3.Flags.enableLargeDesktopWindowingTile;
-import static com.android.launcher3.LauncherState.QUICK_SWITCH_FROM_HOME;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTION_EXIT_HOME;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_MODAL;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
-import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
-import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
-import static com.android.quickstep.views.RecentsView.DESKTOP_CAROUSEL_DETACH_PROGRESS;
-import static com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS;
-import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
-import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
-import static com.android.quickstep.views.RecentsView.TASK_THUMBNAIL_SPLASH_ALPHA;
-
-import android.util.FloatProperty;
-import android.view.animation.Interpolator;
-
-import androidx.annotation.NonNull;
-
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.statemanager.StateManager.StateHandler;
-import com.android.launcher3.states.StateAnimationConfig;
-import com.android.quickstep.views.RecentsView;
-
-/**
- * State handler for recents view. Manages UI changes and animations for recents view based off the
- * current {@link LauncherState}.
- *
- * @param <T> the recents view
- */
-public abstract class BaseRecentsViewStateController<T extends RecentsView>
- implements StateHandler<LauncherState> {
- protected final T mRecentsView;
- protected final QuickstepLauncher mLauncher;
-
- public BaseRecentsViewStateController(@NonNull QuickstepLauncher launcher) {
- mLauncher = launcher;
- mRecentsView = launcher.getOverviewPanel();
- }
-
- @Override
- public void setState(@NonNull LauncherState state) {
- float[] scaleAndOffset = state.getOverviewScaleAndOffset(mLauncher);
- RECENTS_SCALE_PROPERTY.set(mRecentsView, scaleAndOffset[0]);
- ADJACENT_PAGE_HORIZONTAL_OFFSET.set(mRecentsView, scaleAndOffset[1]);
- TASK_SECONDARY_TRANSLATION.set(mRecentsView, 0f);
-
- getContentAlphaProperty().set(mRecentsView, state.isRecentsViewVisible ? 1f : 0);
- getTaskModalnessProperty().set(mRecentsView, state.getOverviewModalness());
- RECENTS_GRID_PROGRESS.set(mRecentsView,
- state.displayOverviewTasksAsGrid(mLauncher.getDeviceProfile()) ? 1f : 0f);
- TASK_THUMBNAIL_SPLASH_ALPHA.set(mRecentsView, state.showTaskThumbnailSplash() ? 1f : 0f);
- if (enableLargeDesktopWindowingTile()) {
- DESKTOP_CAROUSEL_DETACH_PROGRESS.set(mRecentsView,
- state.detachDesktopCarousel() ? 1f : 0f);
- }
- }
-
- @Override
- public void setStateWithAnimation(LauncherState toState, StateAnimationConfig config,
- PendingAnimation builder) {
- if (config.hasAnimationFlag(SKIP_OVERVIEW)) {
- return;
- }
- setStateWithAnimationInternal(toState, config, builder);
- builder.addEndListener(success -> {
- if (!success && !toState.isRecentsViewVisible) {
- mRecentsView.reset();
- }
- });
- }
-
- /**
- * Core logic for animating the recents view UI.
- *
- * @param toState state to animate to
- * @param config current animation config
- * @param setter animator set builder
- */
- void setStateWithAnimationInternal(@NonNull final LauncherState toState,
- @NonNull StateAnimationConfig config, @NonNull PendingAnimation setter) {
- float[] scaleAndOffset = toState.getOverviewScaleAndOffset(mLauncher);
- setter.setFloat(mRecentsView, RECENTS_SCALE_PROPERTY, scaleAndOffset[0],
- config.getInterpolator(ANIM_OVERVIEW_SCALE, LINEAR));
- setter.setFloat(mRecentsView, ADJACENT_PAGE_HORIZONTAL_OFFSET, scaleAndOffset[1],
- config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_X, LINEAR));
- setter.setFloat(mRecentsView, TASK_SECONDARY_TRANSLATION, 0f,
- config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR));
-
- boolean exitingOverview =
- !FeatureFlags.enableSplitContextually() && !toState.isRecentsViewVisible;
- if (mRecentsView.isSplitSelectionActive() && exitingOverview) {
- setter.add(mRecentsView.getSplitSelectController().getSplitAnimationController()
- .createPlaceholderDismissAnim(mLauncher, LAUNCHER_SPLIT_SELECTION_EXIT_HOME,
- setter.getDuration()));
- setter.setViewAlpha(
- mRecentsView.getSplitInstructionsView(),
- 0,
- config.getInterpolator(
- ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE,
- LINEAR
- )
- );
- }
-
- setter.setFloat(mRecentsView, getContentAlphaProperty(),
- toState.isRecentsViewVisible ? 1 : 0,
- config.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
-
- setter.setFloat(
- mRecentsView, getTaskModalnessProperty(),
- toState.getOverviewModalness(),
- config.getInterpolator(ANIM_OVERVIEW_MODAL, LINEAR));
-
- LauncherState fromState = mLauncher.getStateManager().getState();
- setter.setFloat(mRecentsView, TASK_THUMBNAIL_SPLASH_ALPHA,
- toState.showTaskThumbnailSplash() ? 1f : 0f,
- getOverviewInterpolator(fromState, toState));
-
- setter.setFloat(mRecentsView, RECENTS_GRID_PROGRESS,
- toState.displayOverviewTasksAsGrid(mLauncher.getDeviceProfile()) ? 1f : 0f,
- getOverviewInterpolator(fromState, toState));
-
- if (enableLargeDesktopWindowingTile()) {
- setter.setFloat(mRecentsView, DESKTOP_CAROUSEL_DETACH_PROGRESS,
- toState.detachDesktopCarousel() ? 1f : 0f,
- getOverviewInterpolator(fromState, toState));
- }
- }
-
- private Interpolator getOverviewInterpolator(LauncherState fromState, LauncherState toState) {
- return fromState == QUICK_SWITCH_FROM_HOME
- ? ACCELERATE_DECELERATE
- : toState.isRecentsViewVisible ? INSTANT : FINAL_FRAME;
- }
-
- abstract FloatProperty getTaskModalnessProperty();
-
- /**
- * Get property for content alpha for the recents view.
- *
- * @return the float property for the view's content alpha
- */
- abstract FloatProperty getContentAlphaProperty();
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index 535ae1c..caac35e 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -16,7 +16,6 @@
package com.android.launcher3.uioverrides;
import static com.android.app.animation.Interpolators.ACCELERATE_DECELERATE;
-import static com.android.launcher3.icons.BitmapInfo.FLAG_THEMED;
import static com.android.launcher3.icons.FastBitmapDrawable.getDisabledColorFilter;
import android.animation.Animator;
@@ -26,8 +25,6 @@
import android.animation.Keyframe;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
-import android.animation.ValueAnimator;
-import android.annotation.Nullable;
import android.content.Context;
import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
@@ -37,7 +34,6 @@
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.os.Process;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.Property;
@@ -48,12 +44,12 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
+import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.celllayout.DelegatedCellDrawing;
-import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.icons.GraphicsUtils;
import com.android.launcher3.icons.IconNormalizer;
import com.android.launcher3.icons.LauncherIcons;
@@ -64,10 +60,6 @@
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.DoubleShadowBubbleTextView;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
/**
* A BubbleTextView with a ring around it's drawable
*/
@@ -105,12 +97,12 @@
private final BlurMaskFilter mShadowFilter;
private boolean mIsPinned = false;
- private int mPlateColor;
+ private final AnimColorHolder mPlateColor = new AnimColorHolder();
boolean mDrawForDrag = false;
- // Used for the "slot-machine" education animation.
- private List<Drawable> mSlotMachineIcons;
- private Animator mSlotMachineAnim;
+ // Used for the "slot-machine" animation when prediction changes.
+ private final Rect mSlotIconBound = new Rect(0, 0, getIconSize(), getIconSize());
+ private Drawable mSlotMachineIcon;
private float mSlotMachineIconTranslationY;
// Used to animate the "ring" around predicted icons
@@ -153,34 +145,26 @@
@Override
public void onDraw(Canvas canvas) {
int count = canvas.save();
- boolean isSlotMachineAnimRunning = mSlotMachineAnim != null;
+ boolean isSlotMachineAnimRunning = mSlotMachineIcon != null;
if (!mIsPinned) {
drawEffect(canvas);
if (isSlotMachineAnimRunning) {
// Clip to to outside of the ring during the slot machine animation.
canvas.clipPath(mRingPath);
}
- canvas.translate(getWidth() * RING_EFFECT_RATIO, getHeight() * RING_EFFECT_RATIO);
- canvas.scale(1 - 2 * RING_EFFECT_RATIO, 1 - 2 * RING_EFFECT_RATIO);
+ canvas.scale(1 - 2 * RING_EFFECT_RATIO, 1 - 2 * RING_EFFECT_RATIO,
+ getWidth() * .5f, getHeight() * .5f);
+ if (isSlotMachineAnimRunning) {
+ canvas.translate(0, mSlotMachineIconTranslationY);
+ mSlotMachineIcon.setBounds(mSlotIconBound);
+ mSlotMachineIcon.draw(canvas);
+ canvas.translate(0, getSlotMachineIconPlusSpacingSize());
+ }
}
- if (isSlotMachineAnimRunning) {
- drawSlotMachineIcons(canvas);
- } else {
- super.onDraw(canvas);
- }
+ super.onDraw(canvas);
canvas.restoreToCount(count);
}
- private void drawSlotMachineIcons(Canvas canvas) {
- canvas.translate((getWidth() - getIconSize()) / 2f,
- (getHeight() - getIconSize()) / 2f + mSlotMachineIconTranslationY);
- for (Drawable icon : mSlotMachineIcons) {
- icon.setBounds(0, 0, getIconSize(), getIconSize());
- icon.draw(canvas);
- canvas.translate(0, getSlotMachineIconPlusSpacingSize());
- }
- }
-
private float getSlotMachineIconPlusSpacingSize() {
return getIconSize() + getOutlineOffsetY();
}
@@ -196,104 +180,88 @@
mIsDrawingDot = false;
}
- @Override
- public void applyFromWorkspaceItem(WorkspaceItemInfo info, boolean animate, int staggerIndex) {
- // Create the slot machine animation first, since it uses the current icon to start.
- Animator slotMachineAnim = animate
- ? createSlotMachineAnim(Collections.singletonList(info.bitmap), false)
- : null;
- super.applyFromWorkspaceItem(info, animate, staggerIndex);
- int oldPlateColor = mPlateColor;
+ /**
+ * Returns whether the newInfo differs from the current getTag().
+ */
+ private boolean shouldAnimateIconChange(WorkspaceItemInfo newInfo) {
+ boolean changedIcons = getTag() instanceof WorkspaceItemInfo oldInfo
+ && oldInfo.getTargetComponent() != null
+ && newInfo.getTargetComponent() != null
+ && !oldInfo.getTargetComponent().equals(newInfo.getTargetComponent());
+ return changedIcons && isShown();
+ }
- int newPlateColor;
+ @Override
+ public void applyIconAndLabel(ItemInfoWithIcon info) {
+ super.applyIconAndLabel(info);
if (getIcon().isThemed()) {
- newPlateColor = getResources().getColor(android.R.color.system_accent1_300);
+ mPlateColor.endColor = getResources().getColor(android.R.color.system_accent1_300);
} else {
float[] hctPlateColor = new float[3];
ColorUtils.colorToM3HCT(mDotParams.appColor, hctPlateColor);
- newPlateColor = ColorUtils.M3HCTToColor(hctPlateColor[0], 36, 85);
+ mPlateColor.endColor = ColorUtils.M3HCTToColor(hctPlateColor[0], 36, 85);
}
+ mPlateColor.onUpdate();
+ }
+
+ /**
+ * Tries to apply the icon with animation and returns true if the icon was indeed animated
+ */
+ public boolean applyFromWorkspaceItemWithAnimation(WorkspaceItemInfo info, int staggerIndex) {
+ boolean animate = shouldAnimateIconChange(info);
+ Drawable oldIcon = getIcon();
+ int oldPlateColor = mPlateColor.currentColor;
+ applyFromWorkspaceItem(info, null);
+
+ setContentDescription(
+ mIsPinned ? info.contentDescription :
+ getContext().getString(R.string.hotseat_prediction_content_description,
+ info.contentDescription));
if (!animate) {
- mPlateColor = newPlateColor;
- }
- if (mIsPinned) {
- setContentDescription(info.contentDescription);
+ mPlateColor.startColor = mPlateColor.endColor;
+ mPlateColor.progress.value = 1;
+ mPlateColor.onUpdate();
} else {
- setContentDescription(
- getContext().getString(R.string.hotseat_prediction_content_description,
- info.contentDescription));
- }
+ mPlateColor.startColor = oldPlateColor;
+ mPlateColor.progress.value = 0;
+ mPlateColor.onUpdate();
- if (animate) {
- ValueAnimator plateColorAnim = ValueAnimator.ofObject(new ArgbEvaluator(),
- oldPlateColor, newPlateColor);
- plateColorAnim.addUpdateListener(valueAnimator -> {
- mPlateColor = (int) valueAnimator.getAnimatedValue();
- invalidate();
- });
AnimatorSet changeIconAnim = new AnimatorSet();
- if (slotMachineAnim != null) {
+
+ ObjectAnimator plateColorAnim =
+ ObjectAnimator.ofFloat(mPlateColor.progress, AnimatedFloat.VALUE, 0, 1);
+ plateColorAnim.setAutoCancel(true);
+ changeIconAnim.play(plateColorAnim);
+
+ if (!mIsPinned && oldIcon != null) {
+ // Play the slot machine icon
+ mSlotMachineIcon = oldIcon;
+
+ float finalTrans = -getSlotMachineIconPlusSpacingSize();
+ Keyframe[] keyframes = new Keyframe[] {
+ Keyframe.ofFloat(0f, 0f),
+ Keyframe.ofFloat(0.82f, finalTrans - getOutlineOffsetY() / 2f), // Overshoot
+ Keyframe.ofFloat(1f, finalTrans) // Ease back into the final position
+ };
+ keyframes[1].setInterpolator(ACCELERATE_DECELERATE);
+ keyframes[2].setInterpolator(ACCELERATE_DECELERATE);
+
+ ObjectAnimator slotMachineAnim = ObjectAnimator.ofPropertyValuesHolder(this,
+ PropertyValuesHolder.ofKeyframe(SLOT_MACHINE_TRANSLATION_Y, keyframes));
+ slotMachineAnim.addListener(AnimatorListeners.forEndCallback(() -> {
+ mSlotMachineIcon = null;
+ mSlotMachineIconTranslationY = 0;
+ invalidate();
+ }));
+ slotMachineAnim.setAutoCancel(true);
changeIconAnim.play(slotMachineAnim);
}
- changeIconAnim.play(plateColorAnim);
+
changeIconAnim.setStartDelay(staggerIndex * ICON_CHANGE_ANIM_STAGGER);
changeIconAnim.setDuration(ICON_CHANGE_ANIM_DURATION).start();
}
- }
-
- /**
- * Returns an Animator that translates the given icons in a "slot-machine" fashion, beginning
- * and ending with the original icon.
- */
- public @Nullable Animator createSlotMachineAnim(List<BitmapInfo> iconsToAnimate) {
- return createSlotMachineAnim(iconsToAnimate, true);
- }
-
- /**
- * Returns an Animator that translates the given icons in a "slot-machine" fashion, beginning
- * with the original icon, then cycling through the given icons, optionally ending back with
- * the original icon.
- * @param endWithOriginalIcon Whether we should land back on the icon we started with, rather
- * than the last item in iconsToAnimate.
- */
- public @Nullable Animator createSlotMachineAnim(List<BitmapInfo> iconsToAnimate,
- boolean endWithOriginalIcon) {
- if (mIsPinned || iconsToAnimate == null || iconsToAnimate.isEmpty()) {
- return null;
- }
- if (mSlotMachineAnim != null) {
- mSlotMachineAnim.end();
- }
-
- // Bookend the other animating icons with the original icon on both ends.
- mSlotMachineIcons = new ArrayList<>(iconsToAnimate.size() + 2);
- mSlotMachineIcons.add(getIcon());
- iconsToAnimate.stream()
- .map(iconInfo -> iconInfo.newIcon(mContext, FLAG_THEMED))
- .forEach(mSlotMachineIcons::add);
- if (endWithOriginalIcon) {
- mSlotMachineIcons.add(getIcon());
- }
-
- float finalTrans = -getSlotMachineIconPlusSpacingSize() * (mSlotMachineIcons.size() - 1);
- Keyframe[] keyframes = new Keyframe[] {
- Keyframe.ofFloat(0f, 0f),
- Keyframe.ofFloat(0.82f, finalTrans - getOutlineOffsetY() / 2f), // Overshoot
- Keyframe.ofFloat(1f, finalTrans) // Ease back into the final position
- };
- keyframes[1].setInterpolator(ACCELERATE_DECELERATE);
- keyframes[2].setInterpolator(ACCELERATE_DECELERATE);
-
- mSlotMachineAnim = ObjectAnimator.ofPropertyValuesHolder(this,
- PropertyValuesHolder.ofKeyframe(SLOT_MACHINE_TRANSLATION_Y, keyframes));
- mSlotMachineAnim.addListener(AnimatorListeners.forEndCallback(() -> {
- mSlotMachineIcons = null;
- mSlotMachineAnim = null;
- mSlotMachineIconTranslationY = 0;
- invalidate();
- }));
- return mSlotMachineAnim;
+ return animate;
}
/**
@@ -345,6 +313,7 @@
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
+ mSlotIconBound.offsetTo((w - getIconSize()) / 2, (h - getIconSize()) / 2);
updateRingPath();
}
@@ -355,18 +324,12 @@
}
private void updateRingPath() {
- boolean isBadged = false;
- if (getTag() instanceof WorkspaceItemInfo) {
- WorkspaceItemInfo info = (WorkspaceItemInfo) getTag();
- isBadged = !Process.myUserHandle().equals(info.user)
- || info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
- }
-
mRingPath.reset();
mTmpMatrix.setTranslate(getOutlineOffsetX(), getOutlineOffsetY());
-
mRingPath.addPath(mShapePath, mTmpMatrix);
- if (isBadged) {
+
+ FastBitmapDrawable icon = getIcon();
+ if (icon != null && icon.getBadge() != null) {
float outlineSize = mNormalizedIconSize * RING_EFFECT_RATIO;
float iconSize = getIconSize() * (1 - 2 * RING_EFFECT_RATIO);
float badgeSize = LauncherIcons.getBadgeSizeForIconSize((int) iconSize) + outlineSize;
@@ -422,7 +385,7 @@
canvas.scale(mRingScale, mRingScale, canvas.getWidth() / 2f, canvas.getHeight() / 2f);
}
canvas.drawPath(mRingPath, mIconRingPaint);
- mIconRingPaint.setColor(mPlateColor);
+ mIconRingPaint.setColor(mPlateColor.currentColor);
mIconRingPaint.setMaskFilter(null);
canvas.drawPath(mRingPath, mIconRingPaint);
canvas.restoreToCount(count);
@@ -474,6 +437,21 @@
return icon;
}
+ private class AnimColorHolder {
+
+ public final AnimatedFloat progress = new AnimatedFloat(this::onUpdate, 1);
+ public final ArgbEvaluator evaluator = ArgbEvaluator.getInstance();
+ public Integer startColor = 0;
+ public Integer endColor = 0;
+
+ public int currentColor = 0;
+
+ private void onUpdate() {
+ currentColor = (Integer) evaluator.evaluate(progress.value, startColor, endColor);
+ invalidate();
+ }
+ }
+
/**
* Draws Predicted Icon outline on cell layout
*/
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 6d744c2..810325c 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -39,7 +39,6 @@
import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
import static com.android.launcher3.Utilities.isRtl;
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
-import static com.android.launcher3.config.FeatureFlags.enableSplitContextually;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTION_EXIT_HOME;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTION_EXIT_INTERRUPTED;
@@ -170,7 +169,6 @@
import com.android.quickstep.OverviewCommandHelper;
import com.android.quickstep.OverviewComponentObserver;
import com.android.quickstep.OverviewComponentObserver.OverviewChangeListener;
-import com.android.quickstep.RecentsAnimationDeviceState;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskUtils;
@@ -282,7 +280,6 @@
getDepthController(), getStatsLogManager(),
systemUiProxy, RecentsModel.INSTANCE.get(this),
() -> onStateBack());
- RecentsAnimationDeviceState deviceState = new RecentsAnimationDeviceState(asContext());
if (DesktopModeStatus.canEnterDesktopMode(this)) {
mDesktopRecentsTransitionController = new DesktopRecentsTransitionController(
getStateManager(), systemUiProxy, getIApplicationThread(),
@@ -290,8 +287,8 @@
}
overviewPanel.init(mActionsView, mSplitSelectStateController,
mDesktopRecentsTransitionController);
- mSplitWithKeyboardShortcutController = new SplitWithKeyboardShortcutController(this,
- mSplitSelectStateController, deviceState);
+ mSplitWithKeyboardShortcutController = new SplitWithKeyboardShortcutController(
+ this, mSplitSelectStateController);
mSplitToWorkspaceController = new SplitToWorkspaceController(this,
mSplitSelectStateController);
mActionsView.updateDimension(getDeviceProfile(), overviewPanel.getLastComputedTaskSize());
@@ -564,9 +561,13 @@
mSplitSelectStateController.onDestroy();
}
+ RecentsView recentsView = getOverviewPanel();
+ if (recentsView != null) {
+ recentsView.destroy();
+ }
+
super.onDestroy();
mHotseatPredictionController.destroy();
- mSplitWithKeyboardShortcutController.onDestroy();
if (mViewCapture != null) mViewCapture.close();
removeBackAnimationCallback(mSplitSelectStateController.getSplitBackHandler());
}
@@ -721,11 +722,7 @@
splitSelectSource.alreadyRunningTaskId = taskWasFound
? foundTask.key.id
: INVALID_TASK_ID;
- if (enableSplitContextually()) {
- startSplitToHome(splitSelectSource);
- } else {
- recentsView.initiateSplitSelect(splitSelectSource);
- }
+ startSplitToHome(splitSelectSource);
}
);
}
@@ -815,15 +812,13 @@
super.onPause();
- if (enableSplitContextually()) {
- // If Launcher pauses before both split apps are selected, exit split screen.
- if (!mSplitSelectStateController.isBothSplitAppsConfirmed() &&
- !mSplitSelectStateController.isLaunchingFirstAppFullscreen()) {
- mSplitSelectStateController
- .logExitReason(LAUNCHER_SPLIT_SELECTION_EXIT_INTERRUPTED);
- mSplitSelectStateController.getSplitAnimationController()
- .playPlaceholderDismissAnim(this, LAUNCHER_SPLIT_SELECTION_EXIT_INTERRUPTED);
- }
+ // If Launcher pauses before both split apps are selected, exit split screen.
+ if (!mSplitSelectStateController.isBothSplitAppsConfirmed() &&
+ !mSplitSelectStateController.isLaunchingFirstAppFullscreen()) {
+ mSplitSelectStateController
+ .logExitReason(LAUNCHER_SPLIT_SELECTION_EXIT_INTERRUPTED);
+ mSplitSelectStateController.getSplitAnimationController()
+ .playPlaceholderDismissAnim(this, LAUNCHER_SPLIT_SELECTION_EXIT_INTERRUPTED);
}
if (mTaskbarUIController != null && FeatureFlags.enableHomeTransitionListener()) {
@@ -1018,9 +1013,9 @@
@Override
public void setResumed() {
- DesktopVisibilityController desktopVisibilityController = getDesktopVisibilityController();
+ DesktopVisibilityController desktopVisibilityController =
+ DesktopVisibilityController.INSTANCE.get(this);
if (!ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()
- && desktopVisibilityController != null
&& desktopVisibilityController.areDesktopTasksVisibleAndNotInOverview()
&& !desktopVisibilityController.isRecentsGestureInProgress()) {
// Return early to skip setting activity to appear as resumed
@@ -1187,12 +1182,6 @@
}
@Nullable
- @Override
- public DesktopVisibilityController getDesktopVisibilityController() {
- return mTISBindHelper.getDesktopVisibilityController();
- }
-
- @Nullable
public UnfoldTransitionProgressProvider getUnfoldTransitionProgressProvider() {
return mUnfoldTransitionProgressProvider;
}
@@ -1347,11 +1336,8 @@
@Override
public boolean areDesktopTasksVisible() {
- DesktopVisibilityController desktopVisibilityController = getDesktopVisibilityController();
- if (desktopVisibilityController != null) {
- return desktopVisibilityController.areDesktopTasksVisibleAndNotInOverview();
- }
- return false;
+ return DesktopVisibilityController.INSTANCE.get(this)
+ .areDesktopTasksVisibleAndNotInOverview();
}
@Override
@@ -1431,7 +1417,7 @@
@Override
public boolean handleIncorrectSplitTargetSelection() {
- if (!enableSplitContextually() || !mSplitSelectStateController.isSplitSelectActive()) {
+ if (!mSplitSelectStateController.isSplitSelectActive()) {
return false;
}
mSplitSelectStateController.getSplitInstructionsView().goBoing();
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
deleted file mode 100644
index 111069f..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.uioverrides;
-
-import static com.android.app.animation.Interpolators.LINEAR;
-import static com.android.launcher3.LauncherState.CLEAR_ALL_BUTTON;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.LauncherState.OVERVIEW_ACTIONS;
-import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE;
-import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
-import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
-import static com.android.quickstep.views.RecentsView.TASK_MODALNESS;
-import static com.android.quickstep.views.RecentsView.TASK_PRIMARY_SPLIT_TRANSLATION;
-import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_SPLIT_TRANSLATION;
-import static com.android.quickstep.views.TaskView.FLAG_UPDATE_ALL;
-import static com.android.wm.shell.Flags.enableSplitContextual;
-
-import android.animation.AnimatorSet;
-import android.annotation.TargetApi;
-import android.os.Build;
-import android.util.FloatProperty;
-import android.util.Log;
-import android.util.Pair;
-
-import androidx.annotation.NonNull;
-
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.anim.AnimatedFloat;
-import com.android.launcher3.anim.AnimatorListeners;
-import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.anim.PropertySetter;
-import com.android.launcher3.states.StateAnimationConfig;
-import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
-import com.android.quickstep.util.AnimUtils;
-import com.android.quickstep.util.SplitAnimationTimings;
-import com.android.quickstep.views.ClearAllButton;
-import com.android.quickstep.views.LauncherRecentsView;
-import com.android.quickstep.views.RecentsView;
-
-/**
- * State handler for handling UI changes for {@link LauncherRecentsView}. In addition to managing
- * the basic view properties, this class also manages changes in the task visuals.
- */
-@TargetApi(Build.VERSION_CODES.O)
-public final class RecentsViewStateController extends
- BaseRecentsViewStateController<LauncherRecentsView> {
-
- public RecentsViewStateController(QuickstepLauncher launcher) {
- super(launcher);
- }
-
- @Override
- public void setState(@NonNull LauncherState state) {
- super.setState(state);
- if (state.isRecentsViewVisible) {
- mRecentsView.updateEmptyMessage();
- } else {
- mRecentsView.resetTaskVisuals();
- }
- setAlphas(PropertySetter.NO_ANIM_PROPERTY_SETTER, new StateAnimationConfig(), state);
- mRecentsView.setFullscreenProgress(state.getOverviewFullscreenProgress());
- // In Overview, we may be layering app surfaces behind Launcher, so we need to notify
- // DepthController to prevent optimizations which might occlude the layers behind
- mLauncher.getDepthController().setHasContentBehindLauncher(state.isRecentsViewVisible);
-
- PendingAnimation builder =
- new PendingAnimation(state.getTransitionDuration(mLauncher, true));
-
- handleSplitSelectionState(state, builder, /* animate */false);
- }
-
- @Override
- void setStateWithAnimationInternal(@NonNull LauncherState toState,
- @NonNull StateAnimationConfig config, @NonNull PendingAnimation builder) {
- super.setStateWithAnimationInternal(toState, config, builder);
-
- if (toState.isRecentsViewVisible) {
- // While animating into recents, update the visible task data as needed
- builder.addOnFrameCallback(() -> mRecentsView.loadVisibleTaskData(FLAG_UPDATE_ALL));
- mRecentsView.updateEmptyMessage();
- // TODO(b/246283207): Remove logging once root cause of flake detected.
- if (Utilities.isRunningInTestHarness()) {
- Log.d("b/246283207", "RecentsView#setStateWithAnimationInternal getCurrentPage(): "
- + mRecentsView.getCurrentPage()
- + ", getScrollForPage(getCurrentPage())): "
- + mRecentsView.getScrollForPage(mRecentsView.getCurrentPage()));
- }
- } else {
- builder.addListener(
- AnimatorListeners.forSuccessCallback(mRecentsView::resetTaskVisuals));
- }
- // In Overview, we may be layering app surfaces behind Launcher, so we need to notify
- // DepthController to prevent optimizations which might occlude the layers behind
- builder.addListener(AnimatorListeners.forSuccessCallback(() ->
- mLauncher.getDepthController().setHasContentBehindLauncher(
- toState.isRecentsViewVisible)));
-
- handleSplitSelectionState(toState, builder, /* animate */true);
-
- setAlphas(builder, config, toState);
- builder.setFloat(mRecentsView, FULLSCREEN_PROGRESS,
- toState.getOverviewFullscreenProgress(), LINEAR);
- }
-
- /**
- * Create or dismiss split screen select animations.
- * @param builder if null then this will run the split select animations right away, otherwise
- * will add animations to builder.
- */
- private void handleSplitSelectionState(@NonNull LauncherState toState,
- @NonNull PendingAnimation builder, boolean animate) {
- boolean goingToOverviewFromWorkspaceContextual = enableSplitContextual() &&
- toState == OVERVIEW && mLauncher.isSplitSelectionActive();
- if (toState != OVERVIEW_SPLIT_SELECT && !goingToOverviewFromWorkspaceContextual) {
- // Not going to split
- return;
- }
-
- // Create transition animations to split select
- RecentsPagedOrientationHandler orientationHandler =
- ((RecentsView) mLauncher.getOverviewPanel()).getPagedOrientationHandler();
- Pair<FloatProperty<RecentsView>, FloatProperty<RecentsView>> taskViewsFloat =
- orientationHandler.getSplitSelectTaskOffset(
- TASK_PRIMARY_SPLIT_TRANSLATION, TASK_SECONDARY_SPLIT_TRANSLATION,
- mLauncher.getDeviceProfile());
-
- SplitAnimationTimings timings =
- AnimUtils.getDeviceOverviewToSplitTimings(mLauncher.getDeviceProfile().isTablet);
- if (!goingToOverviewFromWorkspaceContextual) {
- // This animation is already done for the contextual case, don't redo it
- mRecentsView.createSplitSelectInitAnimation(builder,
- toState.getTransitionDuration(mLauncher, true /* isToState */));
- }
- // Shift tasks vertically downward to get out of placeholder view
- builder.setFloat(mRecentsView, taskViewsFloat.first,
- toState.getSplitSelectTranslation(mLauncher),
- timings.getGridSlidePrimaryInterpolator());
- // Zero out horizontal translation
- builder.setFloat(mRecentsView, taskViewsFloat.second,
- 0,
- timings.getGridSlideSecondaryInterpolator());
-
- mRecentsView.handleDesktopTaskInSplitSelectState(builder,
- timings.getDesktopTaskFadeInterpolator());
-
- if (!animate) {
- AnimatorSet as = builder.buildAnim();
- as.start();
- as.end();
- }
- }
-
- private void setAlphas(PropertySetter propertySetter, StateAnimationConfig config,
- LauncherState state) {
- float clearAllButtonAlpha = state.areElementsVisible(mLauncher, CLEAR_ALL_BUTTON) ? 1 : 0;
- propertySetter.setFloat(mRecentsView.getClearAllButton(), ClearAllButton.VISIBILITY_ALPHA,
- clearAllButtonAlpha, LINEAR);
- float overviewButtonAlpha = state.areElementsVisible(mLauncher, OVERVIEW_ACTIONS) ? 1 : 0;
- propertySetter.setFloat(mLauncher.getActionsView().getVisibilityAlpha(),
- AnimatedFloat.VALUE, overviewButtonAlpha, config.getInterpolator(
- ANIM_OVERVIEW_ACTIONS_FADE, LINEAR));
- }
-
- @Override
- FloatProperty<RecentsView> getTaskModalnessProperty() {
- return TASK_MODALNESS;
- }
-
- @Override
- FloatProperty<RecentsView> getContentAlphaProperty() {
- return CONTENT_ALPHA;
- }
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.kt b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.kt
new file mode 100644
index 0000000..c8f46a9
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.kt
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.uioverrides
+
+import com.android.app.animation.Interpolators.ACCELERATE_DECELERATE
+import com.android.app.animation.Interpolators.AGGRESSIVE_EASE_IN_OUT
+import com.android.app.animation.Interpolators.FINAL_FRAME
+import com.android.app.animation.Interpolators.INSTANT
+import com.android.app.animation.Interpolators.LINEAR
+import com.android.launcher3.Flags.enableLargeDesktopWindowingTile
+import com.android.launcher3.LauncherState
+import com.android.launcher3.anim.AnimatedFloat
+import com.android.launcher3.anim.AnimatorListeners.forSuccessCallback
+import com.android.launcher3.anim.PendingAnimation
+import com.android.launcher3.anim.PropertySetter
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent
+import com.android.launcher3.statemanager.StateManager.StateHandler
+import com.android.launcher3.states.StateAnimationConfig
+import com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE
+import com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE
+import com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_MODAL
+import com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE
+import com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE
+import com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X
+import com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y
+import com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW
+import com.android.quickstep.util.AnimUtils
+import com.android.quickstep.views.ClearAllButton
+import com.android.quickstep.views.RecentsView
+import com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET
+import com.android.quickstep.views.RecentsView.CONTENT_ALPHA
+import com.android.quickstep.views.RecentsView.DESKTOP_CAROUSEL_DETACH_PROGRESS
+import com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS
+import com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS
+import com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY
+import com.android.quickstep.views.RecentsView.TASK_MODALNESS
+import com.android.quickstep.views.RecentsView.TASK_PRIMARY_SPLIT_TRANSLATION
+import com.android.quickstep.views.RecentsView.TASK_SECONDARY_SPLIT_TRANSLATION
+import com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION
+import com.android.quickstep.views.RecentsView.TASK_THUMBNAIL_SPLASH_ALPHA
+import com.android.quickstep.views.TaskView.Companion.FLAG_UPDATE_ALL
+
+/**
+ * State handler for handling UI changes for [com.android.quickstep.views.LauncherRecentsView]. In
+ * addition to managing the basic view properties, this class also manages changes in the task
+ * visuals.
+ */
+class RecentsViewStateController(private val launcher: QuickstepLauncher) :
+ StateHandler<LauncherState> {
+ private val recentsView: RecentsView<*, *> = launcher.getOverviewPanel()
+
+ override fun setState(state: LauncherState) {
+ val scaleAndOffset = state.getOverviewScaleAndOffset(launcher)
+ RECENTS_SCALE_PROPERTY.set(recentsView, scaleAndOffset[0])
+ ADJACENT_PAGE_HORIZONTAL_OFFSET.set(recentsView, scaleAndOffset[1])
+ TASK_SECONDARY_TRANSLATION.set(recentsView, 0f)
+
+ CONTENT_ALPHA.set(recentsView, if (state.isRecentsViewVisible) 1f else 0f)
+ TASK_MODALNESS.set(recentsView, state.overviewModalness)
+ RECENTS_GRID_PROGRESS.set(
+ recentsView,
+ if (state.displayOverviewTasksAsGrid(launcher.deviceProfile)) 1f else 0f,
+ )
+ TASK_THUMBNAIL_SPLASH_ALPHA.set(
+ recentsView,
+ if (state.showTaskThumbnailSplash()) 1f else 0f,
+ )
+ if (enableLargeDesktopWindowingTile()) {
+ DESKTOP_CAROUSEL_DETACH_PROGRESS.set(
+ recentsView,
+ if (state.detachDesktopCarousel()) 1f else 0f,
+ )
+ }
+
+ if (state.isRecentsViewVisible) {
+ recentsView.updateEmptyMessage()
+ } else {
+ recentsView.resetTaskVisuals()
+ }
+ setAlphas(PropertySetter.NO_ANIM_PROPERTY_SETTER, StateAnimationConfig(), state)
+ recentsView.setFullscreenProgress(state.overviewFullscreenProgress)
+ // In Overview, we may be layering app surfaces behind Launcher, so we need to notify
+ // DepthController to prevent optimizations which might occlude the layers behind
+ launcher.depthController.setHasContentBehindLauncher(state.isRecentsViewVisible)
+
+ val builder = PendingAnimation(state.getTransitionDuration(launcher, true).toLong())
+ handleSplitSelectionState(state, builder, animate = false)
+ }
+
+ override fun setStateWithAnimation(
+ toState: LauncherState,
+ config: StateAnimationConfig,
+ builder: PendingAnimation,
+ ) {
+ if (config.hasAnimationFlag(SKIP_OVERVIEW)) return
+
+ val scaleAndOffset = toState.getOverviewScaleAndOffset(launcher)
+ builder.setFloat(
+ recentsView,
+ RECENTS_SCALE_PROPERTY,
+ scaleAndOffset[0],
+ config.getInterpolator(ANIM_OVERVIEW_SCALE, LINEAR),
+ )
+ builder.setFloat(
+ recentsView,
+ ADJACENT_PAGE_HORIZONTAL_OFFSET,
+ scaleAndOffset[1],
+ config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_X, LINEAR),
+ )
+ builder.setFloat(
+ recentsView,
+ TASK_SECONDARY_TRANSLATION,
+ 0f,
+ config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR),
+ )
+
+ builder.setFloat(
+ recentsView,
+ CONTENT_ALPHA,
+ if (toState.isRecentsViewVisible) 1f else 0f,
+ config.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT),
+ )
+
+ builder.setFloat(
+ recentsView,
+ TASK_MODALNESS,
+ toState.overviewModalness,
+ config.getInterpolator(ANIM_OVERVIEW_MODAL, LINEAR),
+ )
+
+ val fromState = launcher.stateManager.state
+ builder.setFloat(
+ recentsView,
+ TASK_THUMBNAIL_SPLASH_ALPHA,
+ if (toState.showTaskThumbnailSplash()) 1f else 0f,
+ getOverviewInterpolator(fromState, toState),
+ )
+
+ builder.setFloat(
+ recentsView,
+ RECENTS_GRID_PROGRESS,
+ if (toState.displayOverviewTasksAsGrid(launcher.deviceProfile)) 1f else 0f,
+ getOverviewInterpolator(fromState, toState),
+ )
+
+ if (enableLargeDesktopWindowingTile()) {
+ builder.setFloat(
+ recentsView,
+ DESKTOP_CAROUSEL_DETACH_PROGRESS,
+ if (toState.detachDesktopCarousel()) 1f else 0f,
+ getOverviewInterpolator(fromState, toState),
+ )
+ }
+
+ if (toState.isRecentsViewVisible) {
+ // While animating into recents, update the visible task data as needed
+ builder.addOnFrameCallback { recentsView.loadVisibleTaskData(FLAG_UPDATE_ALL) }
+ recentsView.updateEmptyMessage()
+ } else {
+ builder.addListener(forSuccessCallback { recentsView.resetTaskVisuals() })
+ }
+ // In Overview, we may be layering app surfaces behind Launcher, so we need to notify
+ // DepthController to prevent optimizations which might occlude the layers behind
+ builder.addListener(
+ forSuccessCallback {
+ launcher.depthController.setHasContentBehindLauncher(toState.isRecentsViewVisible)
+ }
+ )
+
+ handleSplitSelectionState(toState, builder, animate = true)
+
+ setAlphas(builder, config, toState)
+ builder.setFloat(
+ recentsView,
+ FULLSCREEN_PROGRESS,
+ toState.overviewFullscreenProgress,
+ LINEAR,
+ )
+
+ builder.addEndListener { success: Boolean ->
+ if (!success && !toState.isRecentsViewVisible) {
+ recentsView.reset()
+ }
+ }
+ }
+
+ /**
+ * Create or dismiss split screen select animations.
+ *
+ * @param builder if null then this will run the split select animations right away, otherwise
+ * will add animations to builder.
+ */
+ private fun handleSplitSelectionState(
+ toState: LauncherState,
+ builder: PendingAnimation,
+ animate: Boolean,
+ ) {
+ val goingToOverviewFromWorkspaceContextual = toState == LauncherState.OVERVIEW &&
+ launcher.isSplitSelectionActive
+ if (
+ toState != LauncherState.OVERVIEW_SPLIT_SELECT &&
+ !goingToOverviewFromWorkspaceContextual
+ ) {
+ // Not going to split
+ return
+ }
+
+ // Create transition animations to split select
+ val orientationHandler = recentsView.pagedOrientationHandler
+ val taskViewsFloat =
+ orientationHandler.getSplitSelectTaskOffset(
+ TASK_PRIMARY_SPLIT_TRANSLATION,
+ TASK_SECONDARY_SPLIT_TRANSLATION,
+ launcher.deviceProfile,
+ )
+
+ val timings = AnimUtils.getDeviceOverviewToSplitTimings(launcher.deviceProfile.isTablet)
+ if (!goingToOverviewFromWorkspaceContextual) {
+ // This animation is already done for the contextual case, don't redo it
+ recentsView.createSplitSelectInitAnimation(
+ builder,
+ toState.getTransitionDuration(launcher, true),
+ )
+ }
+ // Shift tasks vertically downward to get out of placeholder view
+ builder.setFloat(
+ recentsView,
+ taskViewsFloat.first,
+ toState.getSplitSelectTranslation(launcher),
+ timings.gridSlidePrimaryInterpolator,
+ )
+ // Zero out horizontal translation
+ builder.setFloat(
+ recentsView,
+ taskViewsFloat.second,
+ 0f,
+ timings.gridSlideSecondaryInterpolator,
+ )
+
+ recentsView.handleDesktopTaskInSplitSelectState(
+ builder,
+ timings.desktopTaskFadeInterpolator,
+ )
+
+ if (!animate) {
+ builder.buildAnim().apply {
+ start()
+ end()
+ }
+ }
+ }
+
+ private fun setAlphas(
+ propertySetter: PropertySetter,
+ config: StateAnimationConfig,
+ state: LauncherState,
+ ) {
+ val clearAllButtonAlpha =
+ if (state.areElementsVisible(launcher, LauncherState.CLEAR_ALL_BUTTON)) 1f else 0f
+ propertySetter.setFloat(
+ recentsView.clearAllButton,
+ ClearAllButton.VISIBILITY_ALPHA,
+ clearAllButtonAlpha,
+ LINEAR,
+ )
+ val overviewButtonAlpha =
+ if (state.areElementsVisible(launcher, LauncherState.OVERVIEW_ACTIONS)) 1f else 0f
+ propertySetter.setFloat(
+ launcher.actionsView.visibilityAlpha,
+ AnimatedFloat.VALUE,
+ overviewButtonAlpha,
+ config.getInterpolator(ANIM_OVERVIEW_ACTIONS_FADE, LINEAR),
+ )
+ }
+
+ private fun getOverviewInterpolator(fromState: LauncherState, toState: LauncherState) =
+ when {
+ fromState == LauncherState.QUICK_SWITCH_FROM_HOME -> ACCELERATE_DECELERATE
+ toState.isRecentsViewVisible -> INSTANT
+ else -> FINAL_FRAME
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/flags/DevOptionsUiHelper.kt b/quickstep/src/com/android/launcher3/uioverrides/flags/DevOptionsUiHelper.kt
index 417bb74..7c09e9a 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/flags/DevOptionsUiHelper.kt
+++ b/quickstep/src/com/android/launcher3/uioverrides/flags/DevOptionsUiHelper.kt
@@ -17,8 +17,6 @@
package com.android.launcher3.uioverrides.flags
import android.app.PendingIntent
-import android.app.blob.BlobHandle.createWithSha256
-import android.app.blob.BlobStoreManager
import android.content.Context
import android.content.IIntentReceiver
import android.content.IIntentSender.Stub
@@ -29,10 +27,8 @@
import android.net.Uri
import android.os.Bundle
import android.os.IBinder
-import android.os.ParcelFileDescriptor.AutoCloseOutputStream
import android.provider.DeviceConfig
import android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS
-import android.provider.Settings.Secure
import android.text.Html
import android.util.AttributeSet
import android.view.inputmethod.EditorInfo
@@ -44,33 +40,16 @@
import androidx.preference.PreferenceGroup
import androidx.preference.PreferenceViewHolder
import androidx.preference.SwitchPreference
-import com.android.launcher3.AutoInstallsLayout
import com.android.launcher3.ExtendedEditText
import com.android.launcher3.LauncherAppState
import com.android.launcher3.LauncherPrefs
-import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP
-import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT
-import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
-import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
-import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
-import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_FOLDER
-import com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_LABEL
-import com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_TAG
-import com.android.launcher3.LauncherSettings.Settings.LAYOUT_PROVIDER_KEY
-import com.android.launcher3.LauncherSettings.Settings.createBlobProviderKey
import com.android.launcher3.R
-import com.android.launcher3.model.data.FolderInfo
-import com.android.launcher3.model.data.ItemInfo
-import com.android.launcher3.model.data.LauncherAppWidgetInfo
-import com.android.launcher3.pm.UserCache
import com.android.launcher3.proxy.ProxyActivityStarter
import com.android.launcher3.secondarydisplay.SecondaryDisplayLauncher
-import com.android.launcher3.shortcuts.ShortcutKey
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapperImpl
import com.android.launcher3.util.Executors.MAIN_EXECUTOR
-import com.android.launcher3.util.Executors.MODEL_EXECUTOR
import com.android.launcher3.util.Executors.ORDERED_BG_EXECUTOR
-import com.android.launcher3.util.LauncherLayoutBuilder
+import com.android.launcher3.util.LayoutImportExportHelper
import com.android.launcher3.util.OnboardingPrefs.ALL_APPS_VISITED_COUNT
import com.android.launcher3.util.OnboardingPrefs.HOME_BOUNCE_COUNT
import com.android.launcher3.util.OnboardingPrefs.HOME_BOUNCE_SEEN
@@ -80,14 +59,12 @@
import com.android.launcher3.util.OnboardingPrefs.TASKBAR_SEARCH_EDU_SEEN
import com.android.launcher3.util.PluginManagerWrapper
import com.android.launcher3.util.StartActivityParams
-import com.android.launcher3.util.UserIconInfo
import com.android.quickstep.util.DeviceConfigHelper
import com.android.quickstep.util.DeviceConfigHelper.Companion.NAMESPACE_LAUNCHER
import com.android.quickstep.util.DeviceConfigHelper.DebugInfo
import com.android.systemui.shared.plugins.PluginEnabler
import com.android.systemui.shared.plugins.PluginPrefs
-import java.io.OutputStreamWriter
-import java.security.MessageDigest
+import java.nio.charset.StandardCharsets
import java.util.Locale
import java.util.concurrent.Executor
@@ -421,26 +398,12 @@
title = "Export"
intent =
createUriPickerIntent(ACTION_CREATE_DOCUMENT, MAIN_EXECUTOR) { uri ->
- model.enqueueModelUpdateTask { _, dataModel, _ ->
- val builder = LauncherLayoutBuilder()
- dataModel.workspaceItems.forEach { info ->
- val loc =
- when (info.container) {
- CONTAINER_DESKTOP ->
- builder.atWorkspace(info.cellX, info.cellY, info.screenId)
- CONTAINER_HOTSEAT -> builder.atHotseat(info.screenId)
- else -> return@forEach
- }
- loc.addItem(info)
- }
- dataModel.appWidgets.forEach { info ->
- builder.atWorkspace(info.cellX, info.cellY, info.screenId).addItem(info)
- }
-
+ LayoutImportExportHelper.exportModelDbAsXml(context) { layoutXml ->
context.contentResolver.openOutputStream(uri).use { os ->
- builder.build(OutputStreamWriter(os))
+ val bytes: ByteArray =
+ layoutXml.toByteArray(StandardCharsets.UTF_8) // Encode to UTF-8
+ os?.write(bytes)
}
-
MAIN_EXECUTOR.execute {
Toast.makeText(context, "File saved", Toast.LENGTH_LONG).show()
}
@@ -458,66 +421,12 @@
resolver.openInputStream(uri).use { stream ->
stream?.readAllBytes() ?: return@createUriPickerIntent
}
-
- val digest = MessageDigest.getInstance("SHA-256").digest(data)
- val handle = createWithSha256(digest, LAYOUT_DIGEST_LABEL, 0, LAYOUT_DIGEST_TAG)
- val blobManager = context.getSystemService(BlobStoreManager::class.java)!!
-
- blobManager.openSession(blobManager.createSession(handle)).use { session ->
- AutoCloseOutputStream(session.openWrite(0, -1)).use { it.write(data) }
- session.allowPublicAccess()
-
- session.commit(ORDERED_BG_EXECUTOR) {
- Secure.putString(
- resolver,
- LAYOUT_PROVIDER_KEY,
- createBlobProviderKey(digest),
- )
-
- MODEL_EXECUTOR.submit { model.modelDbController.createEmptyDB() }.get()
- MAIN_EXECUTOR.submit { model.forceReload() }.get()
- MODEL_EXECUTOR.submit {}.get()
- Secure.putString(resolver, LAYOUT_PROVIDER_KEY, null)
- }
- }
+ LayoutImportExportHelper.importModelFromXml(context, data)
}
category.addPreference(this)
}
}
- private fun LauncherLayoutBuilder.ItemTarget.addItem(info: ItemInfo) {
- val userType: String? =
- when (UserCache.INSTANCE.get(context).getUserInfo(info.user).type) {
- UserIconInfo.TYPE_WORK -> AutoInstallsLayout.USER_TYPE_WORK
- UserIconInfo.TYPE_CLONED -> AutoInstallsLayout.USER_TYPE_CLONED
- else -> null
- }
- when (info.itemType) {
- ITEM_TYPE_APPLICATION ->
- info.targetComponent?.let { c -> putApp(c.packageName, c.className, userType) }
- ITEM_TYPE_DEEP_SHORTCUT ->
- ShortcutKey.fromItemInfo(info).let { key ->
- putShortcut(key.packageName, key.id, userType)
- }
- ITEM_TYPE_FOLDER ->
- (info as FolderInfo).let { folderInfo ->
- putFolder(folderInfo.title?.toString() ?: "").also { folderBuilder ->
- folderInfo.getContents().forEach { folderContent ->
- folderBuilder.addItem(folderContent)
- }
- }
- }
- ITEM_TYPE_APPWIDGET ->
- putWidget(
- (info as LauncherAppWidgetInfo).providerName.packageName,
- info.providerName.className,
- info.spanX,
- info.spanY,
- userType,
- )
- }
- }
-
private fun createUriPickerIntent(
action: String,
executor: Executor,
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index c48ba4f..5c16a62 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -18,7 +18,6 @@
import static com.android.app.animation.Interpolators.DECELERATE_2;
import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
-import static com.android.wm.shell.Flags.enableSplitContextual;
import android.content.Context;
import android.graphics.Rect;
@@ -123,7 +122,7 @@
if (showFloatingSearch) {
elements |= FLOATING_SEARCH_BAR;
}
- if (enableSplitContextual() && launcher.isSplitSelectionActive()) {
+ if (launcher.isSplitSelectionActive()) {
elements &= ~CLEAR_ALL_BUTTON;
}
return elements;
@@ -131,7 +130,7 @@
@Override
public float getSplitSelectTranslation(Launcher launcher) {
- if (!enableSplitContextual() || !launcher.isSplitSelectionActive()) {
+ if (!launcher.isSplitSelectionActive()) {
return 0f;
}
RecentsView recentsView = launcher.getOverviewPanel();
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
index 1d9e492..a4f8b81 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
@@ -45,11 +45,11 @@
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.contextualeducation.ContextualEduStatsManager;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.TouchController;
+import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskUtils;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.OverviewToHomeAnim;
@@ -210,10 +210,7 @@
mLauncher.getStateManager().addStateListener(listener);
onSwipeInteractionCompleted(mEndState);
};
- new OverviewToHomeAnim(mLauncher, onReachedHome,
- FeatureFlags.enableSplitContextually()
- ? mCancelSplitRunnable
- : null)
+ new OverviewToHomeAnim(mLauncher, onReachedHome, mCancelSplitRunnable)
.animateWithVelocity(velocity);
} else {
mLauncher.getStateManager().goToState(mEndState, true,
@@ -221,7 +218,7 @@
}
if (mStartState != mEndState) {
logHomeGesture();
- ContextualEduStatsManager.INSTANCE.get(mLauncher).updateEduStats(
+ SystemUiProxy.INSTANCE.get(mLauncher).updateContextualEduStats(
mSwipeDetector.isTrackpadGesture(), GestureType.HOME);
}
AbstractFloatingView topOpenView = AbstractFloatingView.getTopOpenView(mLauncher);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index b562838..a74b350 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -37,6 +37,7 @@
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.views.RecentsView;
+import com.android.systemui.contextualeducation.GestureType;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
/**
@@ -163,8 +164,19 @@
@Override
protected void onSwipeInteractionCompleted(LauncherState targetState) {
super.onSwipeInteractionCompleted(targetState);
+ SystemUiProxy sysUIProxy = SystemUiProxy.INSTANCE.get(mLauncher);
if (mStartState == NORMAL && targetState == OVERVIEW) {
- SystemUiProxy.INSTANCE.get(mLauncher).onOverviewShown(true, TAG);
+ sysUIProxy.onOverviewShown(true, TAG);
+ }
+
+ if (targetState == OVERVIEW) {
+ sysUIProxy.updateContextualEduStats(
+ mDetector.isTrackpadGesture(), GestureType.OVERVIEW);
+ } else if (targetState == ALL_APPS && !mDetector.isTrackpadGesture()) {
+ // Only update if it is touch gesture as trackpad gesture is not relevant for all apps
+ // which only provides keyboard education.
+ sysUIProxy.updateContextualEduStats(
+ /* isTrackpadGesture= */ false, GestureType.ALL_APPS);
}
}
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 03394ef..3dd1473 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -31,7 +31,6 @@
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER;
import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
import static com.android.launcher3.Flags.enableAdditionalHomeAnimations;
-import static com.android.launcher3.Flags.enableGridOnlyOverview;
import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
import static com.android.launcher3.Flags.msdlFeedback;
import static com.android.launcher3.PagedView.INVALID_PAGE;
@@ -43,6 +42,8 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_EXIT_DESKTOP_MODE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_LEFT;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_RIGHT;
+import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
+import static com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_STATE_ORDINAL;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
@@ -98,6 +99,7 @@
import android.widget.Toast;
import android.window.DesktopModeFlags;
import android.window.PictureInPictureSurfaceTransaction;
+import android.window.TransitionInfo;
import android.window.WindowAnimationState;
import androidx.annotation.NonNull;
@@ -114,7 +116,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.contextualeducation.ContextualEduStatsManager;
+import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.logging.StatsLogManager.StatsLogger;
@@ -196,6 +198,7 @@
// Fraction of the scroll and transform animation in which the current task fades out
private static final float KQS_TASK_FADE_ANIMATION_FRACTION = 0.4f;
+ protected final RecentsAnimationDeviceState mDeviceState;
protected final BaseContainerInterface<STATE, RECENTS_CONTAINER> mContainerInterface;
protected final InputConsumerProxy mInputConsumerProxy;
protected final ContextInitListener mContextInitListener;
@@ -371,12 +374,13 @@
private final MSDLPlayerWrapper mMSDLPlayerWrapper;
- public AbsSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState,
+ public AbsSwipeUpHandler(Context context,
TaskAnimationManager taskAnimationManager, GestureState gestureState,
long touchTimeMs, boolean continuingLastGesture,
InputConsumerController inputConsumer,
MSDLPlayerWrapper msdlPlayerWrapper) {
- super(context, deviceState, gestureState);
+ super(context, gestureState);
+ mDeviceState = RecentsAnimationDeviceState.INSTANCE.get(mContext);
mContainerInterface = gestureState.getContainerInterface();
mContextInitListener =
mContainerInterface.createActivityInitListener(this::onActivityInit);
@@ -594,7 +598,7 @@
// as that will set the state as BACKGROUND_APP, overriding the animation to NORMAL.
if (mGestureState.getEndTarget() != HOME) {
Runnable initAnimFactory = () -> {
- mAnimationFactory = mContainerInterface.prepareRecentsUI(mDeviceState,
+ mAnimationFactory = mContainerInterface.prepareRecentsUI(
mWasLauncherAlreadyVisible, this::onAnimatorPlaybackControllerCreated);
maybeUpdateRecentsAttachedState(false /* animate */);
if (mGestureState.getEndTarget() != null) {
@@ -660,12 +664,9 @@
mGestureState.getContainerInterface().setOnDeferredActivityLaunchCallback(
mOnDeferredActivityLaunch);
- mGestureState.runOnceAtState(STATE_END_TARGET_SET,
- () -> {
- mDeviceState.getRotationTouchHelper()
- .onEndTargetCalculated(mGestureState.getEndTarget(),
- mContainerInterface);
- });
+ mGestureState.runOnceAtState(STATE_END_TARGET_SET, () ->
+ RotationTouchHelper.INSTANCE.get(mContext)
+ .onEndTargetCalculated(mGestureState.getEndTarget(), mContainerInterface));
notifyGestureStarted();
}
@@ -705,7 +706,7 @@
if (mRecentsView == null) {
return;
}
- mRecentsView.onGestureAnimationStart(runningTasks, mDeviceState.getRotationTouchHelper());
+ mRecentsView.onGestureAnimationStart(runningTasks);
TaskView currentPageTaskView = mRecentsView.getCurrentPageTaskView();
if (currentPageTaskView != null) {
mPreviousTaskViewType = currentPageTaskView.getType();
@@ -954,10 +955,10 @@
@Override
public void onRecentsAnimationStart(RecentsAnimationController controller,
- RecentsAnimationTargets targets) {
- super.onRecentsAnimationStart(controller, targets);
+ RecentsAnimationTargets targets, TransitionInfo transitionInfo) {
+ super.onRecentsAnimationStart(controller, targets, transitionInfo);
if (targets.hasDesktopTasks(mContext)) {
- mRemoteTargetHandles = mTargetGluer.assignTargetsForDesktop(targets);
+ mRemoteTargetHandles = mTargetGluer.assignTargetsForDesktop(targets, transitionInfo);
} else {
int untrimmedAppCount = mRemoteTargetHandles.length;
mRemoteTargetHandles = mTargetGluer.assignTargetsForSplitScreen(targets);
@@ -1185,11 +1186,13 @@
if (endTarget != HOME) {
InteractionJankMonitorWrapper.cancel(Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME);
} else {
+ AccessibilityManagerCompat.sendStateEventToTest(mContext, NORMAL_STATE_ORDINAL);
InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME);
}
if (endTarget != RECENTS) {
InteractionJankMonitorWrapper.cancel(Cuj.CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS);
} else {
+ AccessibilityManagerCompat.sendStateEventToTest(mContext, OVERVIEW_STATE_ORDINAL);
InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS);
}
@@ -1408,7 +1411,7 @@
duration = mContainer != null && mContainer.getDeviceProfile().isTaskbarPresent
? QuickstepTransitionManager.getTaskbarToHomeDuration(isPinnedTaskbar)
: StaggeredWorkspaceAnim.DURATION_MS;
- ContextualEduStatsManager.INSTANCE.get(mContext).updateEduStats(
+ SystemUiProxy.INSTANCE.get(mContext).updateContextualEduStats(
mGestureState.isTrackpadGesture(), GestureType.HOME);
} else if (endTarget == RECENTS) {
if (mRecentsView != null) {
@@ -1434,7 +1437,7 @@
if (!mGestureState.isHandlingAtomicEvent() || isScrolling) {
duration = Math.max(duration, mRecentsView.getScroller().getDuration());
}
- ContextualEduStatsManager.INSTANCE.get(mContext).updateEduStats(
+ SystemUiProxy.INSTANCE.get(mContext).updateContextualEduStats(
mGestureState.isTrackpadGesture(), GestureType.OVERVIEW);
}
} else if (endTarget == LAST_TASK && mRecentsView != null
@@ -1980,7 +1983,7 @@
}
// Make sure recents is in its final state
maybeUpdateRecentsAttachedState(false);
- mContainerInterface.onSwipeUpToHomeComplete(mDeviceState);
+ mContainerInterface.onSwipeUpToHomeComplete();
}
});
if (mRecentsAnimationTargets != null) {
@@ -2684,9 +2687,7 @@
}
float scrollOffset = Math.abs(mRecentsView.getScrollOffset(mRecentsView.getCurrentPage()));
- Rect carouselTaskSize = enableGridOnlyOverview()
- ? mRecentsView.getLastComputedCarouselTaskSize()
- : mRecentsView.getLastComputedTaskSize();
+ Rect carouselTaskSize = mRecentsView.getLastComputedTaskSize();
int maxScrollOffset = mRecentsView.getPagedOrientationHandler().getPrimaryValue(
carouselTaskSize.width(), carouselTaskSize.height());
maxScrollOffset += mRecentsView.getPageSpacing();
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 1437a6e..7cab751 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -86,8 +86,8 @@
if (endTarget != null) {
// We were on our way to this state when we got canceled, end there instead.
startState = stateFromGestureEndTarget(endTarget);
- DesktopVisibilityController controller = getDesktopVisibilityController();
- if (controller != null && controller.areDesktopTasksVisibleAndNotInOverview()
+ if (DesktopVisibilityController.INSTANCE.get(activity)
+ .areDesktopTasksVisibleAndNotInOverview()
&& 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.
diff --git a/quickstep/src/com/android/quickstep/BaseContainerInterface.java b/quickstep/src/com/android/quickstep/BaseContainerInterface.java
index 2171c47..6d588d9 100644
--- a/quickstep/src/com/android/quickstep/BaseContainerInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseContainerInterface.java
@@ -131,7 +131,7 @@
}
public abstract BaseContainerInterface.AnimationFactory prepareRecentsUI(
- RecentsAnimationDeviceState deviceState, boolean activityVisible,
+ boolean activityVisible,
Consumer<AnimatorControllerWithResistance> callback);
public abstract ContextInitListener createActivityInitListener(
@@ -151,11 +151,10 @@
public abstract void onLaunchTaskFailed();
- public abstract void onExitOverview(RotationTouchHelper deviceState,
- Runnable exitRunnable);
+ public abstract void onExitOverview(Runnable exitRunnable);
/** Called when the animation to home has fully settled. */
- public void onSwipeUpToHomeComplete(RecentsAnimationDeviceState deviceState) {}
+ public void onSwipeUpToHomeComplete() {}
/**
* Sets a callback to be run when an activity launch happens while launcher is not yet resumed.
@@ -179,13 +178,6 @@
mOnInitBackgroundStateUICallback = callback;
}
- @Nullable
- public DesktopVisibilityController getDesktopVisibilityController() {
- CONTAINER_TYPE container = getCreatedContainer();
-
- return container == null ? null : container.getDesktopVisibilityController();
- }
-
/**
* Called when the gesture ends and the animation starts towards the given target. Used to add
* an optional additional animation with the same duration.
@@ -241,9 +233,8 @@
if (endTarget != null) {
// We were on our way to this state when we got canceled, end there instead.
startState = stateFromGestureEndTarget(endTarget);
- DesktopVisibilityController controller = getDesktopVisibilityController();
- if (controller != null && controller.areDesktopTasksVisibleAndNotInOverview()
- && endTarget == LAST_TASK) {
+ if (DesktopVisibilityController.INSTANCE.get(recentsView.getContext())
+ .areDesktopTasksVisibleAndNotInOverview() && 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
@@ -260,11 +251,7 @@
public final void calculateTaskSize(Context context, DeviceProfile dp, Rect outRect,
RecentsPagedOrientationHandler orientationHandler) {
if (dp.isTablet) {
- if (Flags.enableGridOnlyOverview()) {
- calculateGridTaskSize(context, dp, outRect, orientationHandler);
- } else {
- calculateFocusTaskSize(context, dp, outRect);
- }
+ calculateLargeTileSize(context, dp, outRect);
} else {
Resources res = context.getResources();
float maxScale = res.getFloat(R.dimen.overview_max_scale);
@@ -285,24 +272,7 @@
}
}
- /**
- * Calculates the taskView size for carousel during app to overview animation on tablets.
- */
- public final void calculateCarouselTaskSize(Context context, DeviceProfile dp, Rect outRect,
- RecentsPagedOrientationHandler orientationHandler) {
- if (dp.isTablet && dp.isGestureMode) {
- Resources res = context.getResources();
- float minScale = res.getFloat(R.dimen.overview_carousel_min_scale);
- Rect gridRect = new Rect();
- calculateGridSize(dp, context, gridRect);
- calculateTaskSizeInternal(context, dp, gridRect, minScale, Gravity.CENTER | Gravity.TOP,
- outRect);
- } else {
- calculateTaskSize(context, dp, outRect, orientationHandler);
- }
- }
-
- private void calculateFocusTaskSize(Context context, DeviceProfile dp, Rect outRect) {
+ private void calculateLargeTileSize(Context context, DeviceProfile dp, Rect outRect) {
Resources res = context.getResources();
float maxScale = res.getFloat(R.dimen.overview_max_scale);
Rect gridRect = new Rect();
@@ -390,12 +360,6 @@
Rect insets = dp.getInsets();
int topMargin = dp.overviewTaskThumbnailTopMarginPx;
int bottomMargin = dp.getOverviewActionsClaimedSpace();
- if (dp.isTaskbarPresent && Flags.enableGridOnlyOverview()) {
- topMargin += context.getResources().getDimensionPixelSize(
- R.dimen.overview_top_margin_grid_only);
- bottomMargin += context.getResources().getDimensionPixelSize(
- R.dimen.overview_bottom_margin_grid_only);
- }
int sideMargin = dp.overviewGridSideMargin;
outRect.set(0, 0, dp.widthPx, dp.heightPx);
@@ -410,11 +374,7 @@
RecentsPagedOrientationHandler orientationHandler) {
Resources res = context.getResources();
Rect potentialTaskRect = new Rect();
- if (Flags.enableGridOnlyOverview()) {
- calculateGridSize(dp, context, potentialTaskRect);
- } else {
- calculateFocusTaskSize(context, dp, potentialTaskRect);
- }
+ calculateLargeTileSize(context, dp, potentialTaskRect);
float rowHeight = (potentialTaskRect.height() + dp.overviewTaskThumbnailTopMarginPx
- dp.overviewRowSpacing) / 2f;
diff --git a/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt b/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt
index 94f4920..d60dab6 100644
--- a/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt
+++ b/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt
@@ -17,6 +17,7 @@
package com.android.quickstep
import android.view.View
+import com.android.internal.jank.Cuj
import com.android.launcher3.AbstractFloatingViewHelper
import com.android.launcher3.R
import com.android.launcher3.logging.StatsLogManager.LauncherEvent
@@ -24,6 +25,7 @@
import com.android.quickstep.views.RecentsView
import com.android.quickstep.views.RecentsViewContainer
import com.android.quickstep.views.TaskContainer
+import com.android.systemui.shared.system.InteractionJankMonitorWrapper
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource
@@ -31,7 +33,7 @@
class DesktopSystemShortcut(
container: RecentsViewContainer,
private val taskContainer: TaskContainer,
- abstractFloatingViewHelper: AbstractFloatingViewHelper
+ abstractFloatingViewHelper: AbstractFloatingViewHelper,
) :
SystemShortcut<RecentsViewContainer>(
R.drawable.ic_desktop,
@@ -39,15 +41,17 @@
container,
taskContainer.itemInfo,
taskContainer.taskView,
- abstractFloatingViewHelper
+ abstractFloatingViewHelper,
) {
override fun onClick(view: View) {
+ InteractionJankMonitorWrapper.begin(view, Cuj.CUJ_DESKTOP_MODE_ENTER_FROM_OVERVIEW_MENU)
dismissTaskMenuView()
val recentsView = mTarget.getOverviewPanel<RecentsView<*, *>>()
recentsView.moveTaskToDesktop(
taskContainer,
- DesktopModeTransitionSource.APP_FROM_OVERVIEW
+ DesktopModeTransitionSource.APP_FROM_OVERVIEW,
) {
+ InteractionJankMonitorWrapper.end(Cuj.CUJ_DESKTOP_MODE_ENTER_FROM_OVERVIEW_MENU)
mTarget.statsLogManager
.logger()
.withItemInfo(taskContainer.itemInfo)
@@ -64,7 +68,7 @@
return object : TaskShortcutFactory {
override fun getShortcuts(
container: RecentsViewContainer,
- taskContainer: TaskContainer
+ taskContainer: TaskContainer,
): List<DesktopSystemShortcut>? {
return if (!DesktopModeStatus.canEnterDesktopMode(container.asContext())) null
else if (!taskContainer.task.isDockable) null
@@ -73,7 +77,7 @@
DesktopSystemShortcut(
container,
taskContainer,
- abstractFloatingViewHelper
+ abstractFloatingViewHelper,
)
)
}
diff --git a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
index b787399..d8e0296 100644
--- a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
@@ -79,9 +79,9 @@
/** 6 */
@Override
- public AnimationFactory prepareRecentsUI(RecentsAnimationDeviceState deviceState,
+ public AnimationFactory prepareRecentsUI(
boolean activityVisible, Consumer<AnimatorControllerWithResistance> callback) {
- notifyRecentsOfOrientation(deviceState.getRotationTouchHelper());
+ notifyRecentsOfOrientation();
DefaultAnimationFactory factory = new DefaultAnimationFactory(callback);
factory.initBackgroundStateUI();
return factory;
@@ -142,12 +142,12 @@
}
@Override
- public void onExitOverview(RotationTouchHelper deviceState, Runnable exitRunnable) {
+ public void onExitOverview(Runnable exitRunnable) {
final StateManager<RecentsState, RecentsActivity> stateManager =
getCreatedContainer().getStateManager();
if (stateManager.getState() == HOME) {
exitRunnable.run();
- notifyRecentsOfOrientation(deviceState);
+ notifyRecentsOfOrientation();
return;
}
@@ -158,7 +158,7 @@
// Are we going from Recents to Workspace?
if (toState == HOME) {
exitRunnable.run();
- notifyRecentsOfOrientation(deviceState);
+ notifyRecentsOfOrientation();
stateManager.removeStateListener(this);
}
}
@@ -197,11 +197,9 @@
}
}
- private void notifyRecentsOfOrientation(RotationTouchHelper rotationTouchHelper) {
+ private void notifyRecentsOfOrientation() {
// reset layout on swipe to home
- RecentsView recentsView = getCreatedContainer().getOverviewPanel();
- recentsView.setLayoutRotation(rotationTouchHelper.getCurrentActiveRotation(),
- rotationTouchHelper.getDisplayRotation());
+ getCreatedContainer().getOverviewPanel().reapplyActiveRotation();
}
@Override
diff --git a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
index 1e857ca..331580c 100644
--- a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -101,11 +101,11 @@
private boolean mAppCanEnterPip;
- public FallbackSwipeHandler(Context context, RecentsAnimationDeviceState deviceState,
+ public FallbackSwipeHandler(Context context,
TaskAnimationManager taskAnimationManager, GestureState gestureState, long touchTimeMs,
boolean continuingLastGesture, InputConsumerController inputConsumer,
MSDLPlayerWrapper msdlPlayerWrapper) {
- super(context, deviceState, taskAnimationManager, gestureState, touchTimeMs,
+ super(context, taskAnimationManager, gestureState, touchTimeMs,
continuingLastGesture, inputConsumer, msdlPlayerWrapper);
mRunningOverHome = mGestureState.getRunningTask() != null
@@ -216,8 +216,7 @@
if (mRunningOverHome) {
if (DisplayController.getNavigationMode(mContext).hasGestures) {
mRecentsView.onGestureAnimationStartOnHome(
- mGestureState.getRunningTask().getPlaceholderTasks(),
- mDeviceState.getRotationTouchHelper());
+ mGestureState.getRunningTask().getPlaceholderTasks());
}
} else {
super.notifyGestureAnimationStartToRecents();
diff --git a/quickstep/src/com/android/quickstep/FallbackWindowInterface.java b/quickstep/src/com/android/quickstep/FallbackWindowInterface.java
index f7836b0..35630ef 100644
--- a/quickstep/src/com/android/quickstep/FallbackWindowInterface.java
+++ b/quickstep/src/com/android/quickstep/FallbackWindowInterface.java
@@ -80,10 +80,9 @@
/** 6 */
@Override
- public BaseWindowInterface.AnimationFactory prepareRecentsUI(RecentsAnimationDeviceState
- deviceState, boolean activityVisible,
+ public BaseWindowInterface.AnimationFactory prepareRecentsUI(boolean activityVisible,
Consumer<AnimatorControllerWithResistance> callback) {
- notifyRecentsOfOrientation(deviceState.getRotationTouchHelper());
+ notifyRecentsOfOrientation();
BaseWindowInterface.DefaultAnimationFactory factory =
new BaseWindowInterface.DefaultAnimationFactory(callback);
factory.initBackgroundStateUI();
@@ -153,12 +152,12 @@
}
@Override
- public void onExitOverview(RotationTouchHelper deviceState, Runnable exitRunnable) {
+ public void onExitOverview(Runnable exitRunnable) {
final StateManager<RecentsState, RecentsWindowManager> stateManager =
getCreatedContainer().getStateManager();
if (stateManager.getState() == HOME) {
exitRunnable.run();
- notifyRecentsOfOrientation(deviceState);
+ notifyRecentsOfOrientation();
return;
}
@@ -169,7 +168,7 @@
// Are we going from Recents to Workspace?
if (toState == HOME) {
exitRunnable.run();
- notifyRecentsOfOrientation(deviceState);
+ notifyRecentsOfOrientation();
stateManager.removeStateListener(this);
}
}
@@ -208,11 +207,9 @@
}
}
- private void notifyRecentsOfOrientation(RotationTouchHelper rotationTouchHelper) {
+ private void notifyRecentsOfOrientation() {
// reset layout on swipe to home
- RecentsView recentsView = getCreatedContainer().getOverviewPanel();
- recentsView.setLayoutRotation(rotationTouchHelper.getCurrentActiveRotation(),
- rotationTouchHelper.getDisplayRotation());
+ ((RecentsView) getCreatedContainer().getOverviewPanel()).reapplyActiveRotation();
}
@Override
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index cfbcf0a..e0fa77a 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -33,6 +33,7 @@
import android.os.SystemClock;
import android.view.MotionEvent;
import android.view.RemoteAnimationTarget;
+import android.window.TransitionInfo;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -491,7 +492,7 @@
@Override
public void onRecentsAnimationStart(RecentsAnimationController controller,
- RecentsAnimationTargets targets) {
+ RecentsAnimationTargets targets, TransitionInfo info) {
mStateCallback.setState(STATE_RECENTS_ANIMATION_STARTED);
}
diff --git a/quickstep/src/com/android/quickstep/HighResLoadingState.kt b/quickstep/src/com/android/quickstep/HighResLoadingState.kt
new file mode 100644
index 0000000..8a21c4f
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/HighResLoadingState.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep
+
+import android.content.res.Resources
+import com.android.quickstep.recents.data.HighResLoadingStateNotifier
+
+/** Determines when high res or low res thumbnails should be loaded. */
+class HighResLoadingState : HighResLoadingStateNotifier {
+ // If the device does not support low-res thumbnails, only attempt to load high-res thumbnails
+ private val forceHighResThumbnails = !supportsLowResThumbnails()
+ var visible: Boolean = false
+ set(value) {
+ field = value
+ updateState()
+ }
+
+ var flingingFast = false
+ set(value) {
+ field = value
+ updateState()
+ }
+
+ var isEnabled: Boolean = false
+ private set
+
+ private val callbacks = ArrayList<HighResLoadingStateChangedCallback>()
+
+ interface HighResLoadingStateChangedCallback {
+ fun onHighResLoadingStateChanged(enabled: Boolean)
+ }
+
+ override fun addCallback(callback: HighResLoadingStateChangedCallback) {
+ callbacks.add(callback)
+ }
+
+ override fun removeCallback(callback: HighResLoadingStateChangedCallback) {
+ callbacks.remove(callback)
+ }
+
+ private fun updateState() {
+ val prevState = isEnabled
+ isEnabled = forceHighResThumbnails || (visible && !flingingFast)
+ if (prevState != isEnabled) {
+ for (callback in callbacks.asReversed()) {
+ callback.onHighResLoadingStateChanged(isEnabled)
+ }
+ }
+ }
+
+ /**
+ * Returns Whether device supports low-res thumbnails. Low-res files are an optimization for
+ * faster load times of snapshots. Devices can optionally disable low-res files so that they
+ * only store snapshots at high-res scale. The actual scale can be configured in frameworks/base
+ * config overlay.
+ */
+ private fun supportsLowResThumbnails(): Boolean {
+ val res = Resources.getSystem()
+ val resId = res.getIdentifier("config_lowResTaskSnapshotScale", "dimen", "android")
+ if (resId != 0) {
+ return 0 < res.getFloat(resId)
+ }
+ return true
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/InputConsumerUtils.kt b/quickstep/src/com/android/quickstep/InputConsumerUtils.kt
index 558178f..c340c92 100644
--- a/quickstep/src/com/android/quickstep/InputConsumerUtils.kt
+++ b/quickstep/src/com/android/quickstep/InputConsumerUtils.kt
@@ -332,14 +332,7 @@
reasonPrefix,
SUBSTRING_PREFIX,
)
- base =
- AccessibilityInputConsumer(
- context,
- deviceState,
- gestureState,
- base,
- inputMonitorCompat,
- )
+ base = AccessibilityInputConsumer(context, deviceState, base, inputMonitorCompat)
}
} else {
val reasonPrefix = "device is not in gesture navigation mode"
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index d193fee..ac0aa76 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -80,7 +80,7 @@
}
@Override
- public void onSwipeUpToHomeComplete(RecentsAnimationDeviceState deviceState) {
+ public void onSwipeUpToHomeComplete() {
QuickstepLauncher launcher = getCreatedContainer();
if (launcher == null) {
return;
@@ -93,7 +93,7 @@
MAIN_EXECUTOR.getHandler().post(launcher.getStateManager()::reapplyState);
launcher.getRootView().setForceHideBackArrow(false);
- notifyRecentsOfOrientation(deviceState.getRotationTouchHelper());
+ notifyRecentsOfOrientation();
}
@Override
@@ -106,9 +106,9 @@
}
@Override
- public AnimationFactory prepareRecentsUI(RecentsAnimationDeviceState deviceState,
+ public AnimationFactory prepareRecentsUI(
boolean activityVisible, Consumer<AnimatorControllerWithResistance> callback) {
- notifyRecentsOfOrientation(deviceState.getRotationTouchHelper());
+ notifyRecentsOfOrientation();
DefaultAnimationFactory factory = new DefaultAnimationFactory(callback) {
@Override
protected void createBackgroundToOverviewAnim(QuickstepLauncher activity,
@@ -227,7 +227,7 @@
@Override
- public void onExitOverview(RotationTouchHelper deviceState, Runnable exitRunnable) {
+ public void onExitOverview(Runnable exitRunnable) {
final StateManager<LauncherState, Launcher> stateManager =
getCreatedContainer().getStateManager();
stateManager.addStateListener(
@@ -237,18 +237,16 @@
// Are we going from Recents to Workspace?
if (toState == LauncherState.NORMAL) {
exitRunnable.run();
- notifyRecentsOfOrientation(deviceState);
+ notifyRecentsOfOrientation();
stateManager.removeStateListener(this);
}
}
});
}
- private void notifyRecentsOfOrientation(RotationTouchHelper rotationTouchHelper) {
+ private void notifyRecentsOfOrientation() {
// reset layout on swipe to home
- RecentsView recentsView = getCreatedContainer().getOverviewPanel();
- recentsView.setLayoutRotation(rotationTouchHelper.getCurrentActiveRotation(),
- rotationTouchHelper.getDisplayRotation());
+ ((RecentsView) getCreatedContainer().getOverviewPanel()).reapplyActiveRotation();
}
@Override
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
index c1e018d..be0a339 100644
--- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -278,6 +278,10 @@
private void onCancelFinished() {
customizeStatusBarAppearance(false);
+ if (Flags.predictiveBackToHomePolish() && !mLauncher.getWorkspace().isOverlayShown()
+ && !mLauncher.isInState(LauncherState.ALL_APPS)) {
+ setLauncherScale(ScalingWorkspaceRevealAnim.MAX_SIZE);
+ }
finishAnimation();
}
@@ -538,12 +542,10 @@
if (mScrimLayer != null) {
removeScrimLayer();
}
- if (Flags.predictiveBackToHomePolish() && !mLauncher.getWorkspace().isOverlayShown()
+ if (Flags.predictiveBackToHomePolish() && Flags.predictiveBackToHomeBlur()
+ && !mLauncher.getWorkspace().isOverlayShown()
&& !mLauncher.isInState(LauncherState.ALL_APPS)) {
- setLauncherScale(ScalingWorkspaceRevealAnim.MAX_SIZE);
- if (Flags.predictiveBackToHomeBlur()) {
- mLauncher.getDepthController().pauseBlursOnWindows(false);
- }
+ mLauncher.getDepthController().pauseBlursOnWindows(false);
}
mLastBlurRadius = 0;
}
diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index 243a577..c60d3e8 100644
--- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -39,6 +39,7 @@
import com.android.launcher3.LauncherState;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.MSDLPlayerWrapper;
@@ -66,11 +67,10 @@
public class LauncherSwipeHandlerV2 extends AbsSwipeUpHandler<
QuickstepLauncher, RecentsView<QuickstepLauncher, LauncherState>, LauncherState> {
- public LauncherSwipeHandlerV2(Context context, RecentsAnimationDeviceState deviceState,
- TaskAnimationManager taskAnimationManager, GestureState gestureState, long touchTimeMs,
- boolean continuingLastGesture, InputConsumerController inputConsumer,
- MSDLPlayerWrapper msdlPlayerWrapper) {
- super(context, deviceState, taskAnimationManager, gestureState, touchTimeMs,
+ public LauncherSwipeHandlerV2(Context context, TaskAnimationManager taskAnimationManager,
+ GestureState gestureState, long touchTimeMs, boolean continuingLastGesture,
+ InputConsumerController inputConsumer, MSDLPlayerWrapper msdlPlayerWrapper) {
+ super(context, taskAnimationManager, gestureState, touchTimeMs,
continuingLastGesture, inputConsumer, msdlPlayerWrapper);
}
@@ -105,9 +105,8 @@
boolean canUseWorkspaceView = workspaceView != null
&& workspaceView.isAttachedToWindow()
&& workspaceView.getHeight() > 0
- && (mContainer.getDesktopVisibilityController() == null
- || !mContainer.getDesktopVisibilityController()
- .areDesktopTasksVisibleAndNotInOverview());
+ && !DesktopVisibilityController.INSTANCE.get(mContainer)
+ .areDesktopTasksVisibleAndNotInOverview();
mContainer.getRootView().setForceHideBackArrow(true);
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
index 089706f..66f307c 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
@@ -23,6 +23,7 @@
import android.os.Trace
import android.util.Log
import android.view.View
+import android.window.TransitionInfo
import androidx.annotation.BinderThread
import androidx.annotation.UiThread
import androidx.annotation.VisibleForTesting
@@ -369,6 +370,7 @@
override fun onRecentsAnimationStart(
controller: RecentsAnimationController,
targets: RecentsAnimationTargets,
+ transitionInfo: TransitionInfo,
) {
Log.d(TAG, "recents animation started: $command")
updateRecentsViewFocus(command)
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index 64c1129..1f95c41 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -268,7 +268,7 @@
*
* @return the overview intent
*/
- Intent getOverviewIntentIgnoreSysUiState() {
+ public Intent getOverviewIntentIgnoreSysUiState() {
return mIsDefaultHome ? mMyHomeIntent : mOverviewIntent;
}
diff --git a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
index a04ff2e..9b0e75c 100644
--- a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -19,6 +19,7 @@
import com.android.quickstep.util.TISBindHelper;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.RecentsViewContainer;
+import com.android.systemui.shared.recents.model.Task;
import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
@@ -44,11 +45,9 @@
CountDownLatch latch = new CountDownLatch(1);
RecentsModel.INSTANCE.get(mContext).getTasks((taskGroups) -> {
for (GroupTask group : taskGroups) {
- taskBaseIntentComponents.add(
- group.task1.key.baseIntent.getComponent().flattenToString());
- if (group.task2 != null) {
+ for (Task t : group.getTasks()) {
taskBaseIntentComponents.add(
- group.task2.key.baseIntent.getComponent().flattenToString());
+ t.key.baseIntent.getComponent().flattenToString());
}
}
latch.countDown();
@@ -217,7 +216,7 @@
@Override
protected boolean isLauncherInitialized() {
- return super.isLauncherInitialized() && TouchInteractionService.isInitialized();
+ return super.isLauncherInitialized() && SystemUiProxy.INSTANCE.get(mContext).isActive();
}
private void enableBlockingTimeout(
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index 08d43c0..32ccd72 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -422,14 +422,12 @@
writer.println(prefix + " mChangeId=" + mChangeId);
writer.println(prefix + " mResultsUi=[id=" + mResultsUi.mRequestId + ", tasks=");
for (GroupTask task : mResultsUi) {
- Task task1 = task.task1;
- Task task2 = task.task2;
- ComponentName cn1 = task1.getTopComponent();
- ComponentName cn2 = task2 != null ? task2.getTopComponent() : null;
- writer.println(prefix + " t1: (id=" + task1.key.id
- + "; package=" + (cn1 != null ? cn1.getPackageName() + ")" : "no package)")
- + " t2: (id=" + (task2 != null ? task2.key.id : "-1")
- + "; package=" + (cn2 != null ? cn2.getPackageName() + ")" : "no package)"));
+ int count = 0;
+ for (Task t : task.getTasks()) {
+ ComponentName cn = t.getTopComponent();
+ writer.println(prefix + " t" + (++count) + ": (id=" + t.key.id
+ + "; package=" + (cn != null ? cn.getPackageName() + ")" : "no package)"));
+ }
}
writer.println(prefix + " ]");
int currentUserId = Process.myUserHandle().getIdentifier();
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 17c17cc..5e8ea37 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -65,7 +65,6 @@
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.desktop.DesktopRecentsTransitionController;
import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
import com.android.launcher3.statemanager.StateManager.StateHandler;
@@ -364,14 +363,6 @@
}
@Override
- public void onUiChangedWhileSleeping() {
- super.onUiChangedWhileSleeping();
- // Dismiss recents and navigate to home if the device goes to sleep
- // while in recents.
- startHome();
- }
-
- @Override
protected void onResume() {
super.onResume();
AccessibilityManagerCompat.sendStateEventToTest(getBaseContext(), OVERVIEW_STATE_ORDINAL);
@@ -461,6 +452,10 @@
@Override
protected void onDestroy() {
+ RecentsView recentsView = getOverviewPanel();
+ if (recentsView != null) {
+ recentsView.destroy();
+ }
super.onDestroy();
ACTIVITY_TRACKER.onContextDestroyed(this);
mActivityLaunchAnimationRunner = null;
@@ -558,10 +553,4 @@
public boolean isRecentsViewVisible() {
return getStateManager().getState().isRecentsViewVisible();
}
-
- @Nullable
- @Override
- public DesktopVisibilityController getDesktopVisibilityController() {
- return mTISBindHelper.getDesktopVisibilityController();
- }
}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
index 8fc1a78..87bf81c 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -26,6 +26,7 @@
import android.os.Bundle;
import android.util.ArraySet;
import android.view.RemoteAnimationTarget;
+import android.window.TransitionInfo;
import androidx.annotation.BinderThread;
import androidx.annotation.NonNull;
@@ -93,7 +94,7 @@
RemoteAnimationTarget[] appTargets, Rect homeContentInsets,
Rect minimizedHomeBounds, Bundle extras) {
onAnimationStart(controller, appTargets, new RemoteAnimationTarget[0],
- homeContentInsets, minimizedHomeBounds, extras);
+ homeContentInsets, minimizedHomeBounds, extras, /* transitionInfo= */ null);
}
// Called only in R+ platform
@@ -101,7 +102,8 @@
public final void onAnimationStart(RecentsAnimationControllerCompat animationController,
RemoteAnimationTarget[] appTargets,
RemoteAnimationTarget[] wallpaperTargets,
- Rect homeContentInsets, Rect minimizedHomeBounds, Bundle extras) {
+ Rect homeContentInsets, Rect minimizedHomeBounds, Bundle extras,
+ TransitionInfo transitionInfo) {
long appCount = Arrays.stream(appTargets)
.filter(app -> app.mode == MODE_CLOSING)
.count();
@@ -141,7 +143,7 @@
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
ActiveGestureProtoLogProxy.logOnRecentsAnimationStart(targets.apps.length);
for (RecentsAnimationListener listener : getListeners()) {
- listener.onRecentsAnimationStart(mController, targets);
+ listener.onRecentsAnimationStart(mController, targets, transitionInfo);
}
});
}
@@ -205,7 +207,7 @@
*/
public interface RecentsAnimationListener {
default void onRecentsAnimationStart(RecentsAnimationController controller,
- RecentsAnimationTargets targets) {}
+ RecentsAnimationTargets targets, TransitionInfo transitionInfo) {}
/**
* Callback from the system when the recents animation is canceled. {@param thumbnailData}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
index 145773d..865cc47 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -36,6 +36,7 @@
import com.android.quickstep.util.ActiveGestureProtoLogProxy;
import com.android.systemui.animation.TransitionAnimator;
import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
import com.android.wm.shell.recents.IRecentsAnimationController;
@@ -71,7 +72,7 @@
* currently being animated.
*/
public ThumbnailData screenshotTask(int taskId) {
- return mController.screenshotTask(taskId);
+ return ActivityManagerWrapper.getInstance().takeTaskThumbnail(taskId);
}
/**
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index e296449..d4305a5 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -67,7 +67,9 @@
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
import com.android.launcher3.util.DisplayController.Info;
+import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.NavigationMode;
+import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.util.SettingsCache;
import com.android.quickstep.TopTaskTracker.CachedTaskInfo;
import com.android.quickstep.util.ActiveGestureLog;
@@ -88,9 +90,8 @@
/**
* Manages the state of the system during a swipe up gesture.
*/
-public class RecentsAnimationDeviceState implements DisplayInfoChangeListener, ExclusionListener {
-
- private static final String TAG = "RecentsAnimationDeviceState";
+public class RecentsAnimationDeviceState implements DisplayInfoChangeListener, ExclusionListener,
+ SafeCloseable {
static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
@@ -98,6 +99,9 @@
private static final float QUICKSTEP_TOUCH_SLOP_RATIO_TWO_BUTTON = 3f;
private static final float QUICKSTEP_TOUCH_SLOP_RATIO_GESTURAL = 1.414f;
+ public static MainThreadInitializedObject<RecentsAnimationDeviceState> INSTANCE =
+ new MainThreadInitializedObject<>(RecentsAnimationDeviceState::new);
+
private final Context mContext;
private final DisplayController mDisplayController;
@@ -130,41 +134,21 @@
private @NonNull Region mExclusionRegion = GestureExclusionManager.EMPTY_REGION;
private boolean mExclusionListenerRegistered;
- public RecentsAnimationDeviceState(Context context) {
- this(context, false, GestureExclusionManager.INSTANCE);
- }
-
- public RecentsAnimationDeviceState(Context context, boolean isInstanceForTouches) {
- this(context, isInstanceForTouches, GestureExclusionManager.INSTANCE);
+ private RecentsAnimationDeviceState(Context context) {
+ this(context, GestureExclusionManager.INSTANCE);
}
@VisibleForTesting
RecentsAnimationDeviceState(Context context, GestureExclusionManager exclusionManager) {
- this(context, false, exclusionManager);
- }
-
- /**
- * @param isInstanceForTouches {@code true} if this is the persistent instance being used for
- * gesture touch handling
- */
- RecentsAnimationDeviceState(
- Context context, boolean isInstanceForTouches,
- GestureExclusionManager exclusionManager) {
mContext = context;
mDisplayController = DisplayController.INSTANCE.get(context);
mExclusionManager = exclusionManager;
mContextualSearchStateManager = ContextualSearchStateManager.INSTANCE.get(context);
mIsOneHandedModeSupported = SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false);
mRotationTouchHelper = RotationTouchHelper.INSTANCE.get(context);
- if (isInstanceForTouches) {
- // rotationTouchHelper doesn't get initialized after being destroyed, so only destroy
- // if primary TouchInteractionService instance needs to be destroyed.
- mRotationTouchHelper.init();
- runOnDestroy(mRotationTouchHelper::destroy);
- }
// Register for exclusion updates
- runOnDestroy(() -> unregisterExclusionListener());
+ runOnDestroy(this::unregisterExclusionListener);
// Register for display changes changes
mDisplayController.addChangeListener(this);
@@ -225,10 +209,8 @@
mOnDestroyActions.add(action);
}
- /**
- * Cleans up all the registered listeners and receivers.
- */
- public void destroy() {
+ @Override
+ public void close() {
for (Runnable r : mOnDestroyActions) {
r.run();
}
@@ -603,10 +585,6 @@
return mPipIsActive;
}
- public RotationTouchHelper getRotationTouchHelper() {
- return mRotationTouchHelper;
- }
-
/** Returns whether IME is rendering nav buttons, and IME is currently showing. */
public boolean isImeRenderingNavButtons() {
return mCanImeRenderGesturalNavButtons && mMode == NO_BUTTON
diff --git a/quickstep/src/com/android/quickstep/RecentsFilterState.java b/quickstep/src/com/android/quickstep/RecentsFilterState.java
index ff6951d..70d5696 100644
--- a/quickstep/src/com/android/quickstep/RecentsFilterState.java
+++ b/quickstep/src/com/android/quickstep/RecentsFilterState.java
@@ -19,6 +19,7 @@
import androidx.annotation.Nullable;
import com.android.quickstep.util.GroupTask;
+import com.android.systemui.shared.recents.model.Task;
import java.util.HashMap;
import java.util.List;
@@ -122,9 +123,7 @@
return DEFAULT_FILTER;
}
- return (groupTask) -> (groupTask.task2 != null
- && groupTask.task2.key.getPackageName().equals(packageName))
- || groupTask.task1.key.getPackageName().equals(packageName);
+ return (groupTask) -> (groupTask.containsPackage(packageName));
}
/**
@@ -136,17 +135,9 @@
Map<String, Integer> instanceCountMap = new HashMap<>();
for (GroupTask groupTask : groupTasks) {
- final String firstTaskPkgName = groupTask.task1.key.getPackageName();
- final String secondTaskPkgName =
- groupTask.task2 == null ? null : groupTask.task2.key.getPackageName();
-
- // increment the instance count for the first task's base activity package name
- incrementOrAddIfNotExists(instanceCountMap, firstTaskPkgName);
-
- // check if second task is non existent
- if (secondTaskPkgName != null) {
- // increment the instance count for the second task's base activity package name
- incrementOrAddIfNotExists(instanceCountMap, secondTaskPkgName);
+ for (Task t : groupTask.getTasks()) {
+ final String taskPkgName = t.key.getPackageName();
+ incrementOrAddIfNotExists(instanceCountMap, taskPkgName);
}
}
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index d073580..6498b7a 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -37,8 +37,9 @@
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import com.android.launcher3.graphics.ThemeManager;
+import com.android.launcher3.graphics.ThemeManager.ThemeChangeListener;
import com.android.launcher3.icons.IconProvider;
-import com.android.launcher3.icons.IconProvider.IconChangeListener;
import com.android.launcher3.util.Executors.SimpleThreadFactory;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.SafeCloseable;
@@ -66,9 +67,9 @@
* Singleton class to load and manage recents model.
*/
@TargetApi(Build.VERSION_CODES.O)
-public class RecentsModel implements RecentTasksDataSource, IconChangeListener,
- TaskStackChangeListener, TaskVisualsChangeListener, TaskVisualsChangeNotifier,
- SafeCloseable {
+public class RecentsModel implements RecentTasksDataSource, TaskStackChangeListener,
+ TaskVisualsChangeListener, TaskVisualsChangeNotifier,
+ ThemeChangeListener, SafeCloseable {
// We do not need any synchronization for this variable as its only written on UI thread.
public static final MainThreadInitializedObject<RecentsModel> INSTANCE =
@@ -85,8 +86,10 @@
private final TaskIconCache mIconCache;
private final TaskThumbnailCache mThumbnailCache;
private final ComponentCallbacks mCallbacks;
+ private final ThemeManager mThemeManager;
private final TaskStackChangeListeners mTaskStackChangeListeners;
+ private final SafeCloseable mIconChangeCloseable;
private RecentsModel(Context context) {
this(context, new IconProvider(context));
@@ -103,13 +106,15 @@
new TaskIconCache(context, RECENTS_MODEL_EXECUTOR, iconProvider),
new TaskThumbnailCache(context, RECENTS_MODEL_EXECUTOR),
iconProvider,
- TaskStackChangeListeners.getInstance());
+ TaskStackChangeListeners.getInstance(),
+ ThemeManager.INSTANCE.get(context));
}
@VisibleForTesting
RecentsModel(Context context, RecentTasksList taskList, TaskIconCache iconCache,
TaskThumbnailCache thumbnailCache, IconProvider iconProvider,
- TaskStackChangeListeners taskStackChangeListeners) {
+ TaskStackChangeListeners taskStackChangeListeners,
+ ThemeManager themeManager) {
mContext = context;
mTaskList = taskList;
mIconCache = iconCache;
@@ -133,7 +138,10 @@
mTaskStackChangeListeners = taskStackChangeListeners;
mTaskStackChangeListeners.registerTaskStackListener(this);
- iconProvider.registerIconChangeListener(this, MAIN_EXECUTOR.getHandler());
+ mIconChangeCloseable = iconProvider.registerIconChangeListener(
+ this::onAppIconChanged, MAIN_EXECUTOR.getHandler());
+ mThemeManager = themeManager;
+ themeManager.addChangeListener(this);
}
public TaskIconCache getIconCache() {
@@ -231,8 +239,8 @@
// time the user next enters overview
continue;
}
- mThumbnailCache.updateThumbnailInCache(group.task1, /* lowResolution= */ true);
- mThumbnailCache.updateThumbnailInCache(group.task2, /* lowResolution= */ true);
+ group.getTasks().forEach(
+ t -> mThumbnailCache.updateThumbnailInCache(t, /* lowResolution= */ true));
}
});
}
@@ -268,8 +276,7 @@
}
}
- @Override
- public void onAppIconChanged(String packageName, UserHandle user) {
+ private void onAppIconChanged(String packageName, UserHandle user) {
mIconCache.invalidateCacheEntries(packageName, user);
for (TaskVisualsChangeListener listener : mThumbnailChangeListeners) {
listener.onTaskIconChanged(packageName, user);
@@ -284,7 +291,7 @@
}
@Override
- public void onSystemIconStateChanged(String iconState) {
+ public void onThemeChanged() {
mIconCache.clearCache();
}
@@ -367,8 +374,8 @@
mTaskList.getTaskKeys(mThumbnailCache.getCacheSize(), taskGroups -> {
for (GroupTask group : taskGroups) {
- mThumbnailCache.updateThumbnailInCache(group.task1, /* lowResolution= */ false);
- mThumbnailCache.updateThumbnailInCache(group.task2, /* lowResolution= */ false);
+ group.getTasks().forEach(
+ t -> mThumbnailCache.updateThumbnailInCache(t, /* lowResolution= */ false));
}
});
}
@@ -394,6 +401,8 @@
}
mIconCache.removeTaskVisualsChangeListener();
mTaskStackChangeListeners.unregisterTaskStackListener(this);
+ mIconChangeCloseable.close();
+ mThemeManager.removeChangeListener(this);
}
private boolean isCachePreloadingEnabled() {
diff --git a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
index 91d0776..8edbacb 100644
--- a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
+++ b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
@@ -24,6 +24,7 @@
import android.graphics.Rect;
import android.util.Log;
import android.view.RemoteAnimationTarget;
+import android.window.TransitionInfo;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -67,16 +68,13 @@
* running tasks
*/
public RemoteTargetGluer(Context context, BaseContainerInterface sizingStrategy) {
- DesktopVisibilityController desktopVisibilityController =
- sizingStrategy.getDesktopVisibilityController();
- if (desktopVisibilityController != null) {
- int visibleTasksCount = desktopVisibilityController.getVisibleDesktopTasksCount();
- 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;
- }
+ int visibleTasksCount = DesktopVisibilityController.INSTANCE.get(context)
+ .getVisibleDesktopTasksCount();
+ 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;
}
// Assume 2 handles needed for split, scale down as needed later on when we actually
@@ -216,7 +214,8 @@
* Similar to {@link #assignTargets(RemoteAnimationTargets)}, except this creates distinct
* transform params per app in {@code targets.apps} list.
*/
- public RemoteTargetHandle[] assignTargetsForDesktop(RemoteAnimationTargets targets) {
+ public RemoteTargetHandle[] assignTargetsForDesktop(
+ RemoteAnimationTargets targets, TransitionInfo transitionInfo) {
resizeRemoteTargetHandles(targets);
for (int i = 0; i < mRemoteTargetHandles.length; i++) {
@@ -225,6 +224,7 @@
.filter(target -> target.taskId != primaryTaskTarget.taskId).toList();
mRemoteTargetHandles[i].mTransformParams.setTargetSet(
createRemoteAnimationTargetsForTarget(targets, excludeTargets));
+ mRemoteTargetHandles[i].mTransformParams.setTransitionInfo(transitionInfo);
mRemoteTargetHandles[i].mTaskViewSimulator.setPreview(primaryTaskTarget, null);
}
return mRemoteTargetHandles;
diff --git a/quickstep/src/com/android/quickstep/RotationTouchHelper.java b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
index 909cc35..f54b655 100644
--- a/quickstep/src/com/android/quickstep/RotationTouchHelper.java
+++ b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
@@ -47,7 +47,6 @@
import com.android.systemui.shared.system.TaskStackChangeListeners;
import java.io.PrintWriter;
-import java.util.ArrayList;
/**
* Helper class for transforming touch events
@@ -57,16 +56,14 @@
public static final MainThreadInitializedObject<RotationTouchHelper> INSTANCE =
new MainThreadInitializedObject<>(RotationTouchHelper::new);
- private OrientationTouchTransformer mOrientationTouchTransformer;
- private DisplayController mDisplayController;
- private int mDisplayId;
+ private final OrientationTouchTransformer mOrientationTouchTransformer;
+ private final DisplayController mDisplayController;
+ private final int mDisplayId;
private int mDisplayRotation;
- private final ArrayList<Runnable> mOnDestroyActions = new ArrayList<>();
-
private NavigationMode mMode = THREE_BUTTONS;
- private TaskStackChangeListener mFrozenTaskListener = new TaskStackChangeListener() {
+ private final TaskStackChangeListener mFrozenTaskListener = new TaskStackChangeListener() {
@Override
public void onRecentTaskListFrozenChanged(boolean frozen) {
mTaskListFrozen = frozen;
@@ -93,7 +90,7 @@
}
};
- private Runnable mExitOverviewRunnable = new Runnable() {
+ private final Runnable mExitOverviewRunnable = new Runnable() {
@Override
public void run() {
mInOverview = false;
@@ -107,7 +104,7 @@
* rotates rotates the device to match that orientation, this triggers calls to sysui to adjust
* the navbar.
*/
- private OrientationEventListener mOrientationListener;
+ private final OrientationEventListener mOrientationListener;
private int mSensorRotation = ROTATION_0;
/**
* This is the configuration of the foreground app or the app that will be in the foreground
@@ -120,7 +117,6 @@
* would indicate the user's intention to rotate the foreground app.
*/
private boolean mPrioritizeDeviceRotation = false;
- private Runnable mOnDestroyFrozenTaskRunnable;
/**
* Set to true when user swipes to recents. In recents, we ignore the state of the recents
* task list being frozen or not to allow the user to keep interacting with nav bar rotation
@@ -131,23 +127,8 @@
private boolean mTaskListFrozen;
private final Context mContext;
- /**
- * Keeps track of whether destroy has been called for this instance. Mainly used for TAPL tests
- * where multiple instances of RotationTouchHelper are being created. b/177316094
- */
- private boolean mNeedsInit = true;
-
private RotationTouchHelper(Context context) {
mContext = context;
- if (mNeedsInit) {
- init();
- }
- }
-
- public void init() {
- if (!mNeedsInit) {
- return;
- }
mDisplayController = DisplayController.INSTANCE.get(mContext);
Resources resources = mContext.getResources();
mDisplayId = DEFAULT_DISPLAY;
@@ -158,8 +139,7 @@
// Register for navigation mode changes
mDisplayController.addChangeListener(this);
DisplayController.Info info = mDisplayController.getInfo();
- onDisplayInfoChangedInternal(info, CHANGE_ALL, hasGestures(info.getNavigationMode()));
- runOnDestroy(() -> mDisplayController.removeChangeListener(this));
+ onDisplayInfoChanged(context, info, CHANGE_ALL);
mOrientationListener = new OrientationEventListener(mContext) {
@Override
@@ -180,40 +160,14 @@
}
}
};
- runOnDestroy(() -> mOrientationListener.disable());
- mNeedsInit = false;
- }
-
- private void setupOrientationSwipeHandler() {
- TaskStackChangeListeners.getInstance().registerTaskStackListener(mFrozenTaskListener);
- mOnDestroyFrozenTaskRunnable = () -> TaskStackChangeListeners.getInstance()
- .unregisterTaskStackListener(mFrozenTaskListener);
- runOnDestroy(mOnDestroyFrozenTaskRunnable);
- }
-
- private void destroyOrientationSwipeHandlerCallback() {
- TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mFrozenTaskListener);
- mOnDestroyActions.remove(mOnDestroyFrozenTaskRunnable);
- }
-
- private void runOnDestroy(Runnable action) {
- mOnDestroyActions.add(action);
}
@Override
public void close() {
- destroy();
- }
-
- /**
- * Cleans up all the registered listeners and receivers.
- */
- public void destroy() {
- for (Runnable r : mOnDestroyActions) {
- r.run();
- }
- mNeedsInit = true;
- mOnDestroyActions.clear();
+ mDisplayController.removeChangeListener(this);
+ mOrientationListener.disable();
+ TaskStackChangeListeners.getInstance()
+ .unregisterTaskStackListener(mFrozenTaskListener);
}
public boolean isTaskListFrozen() {
@@ -264,10 +218,6 @@
@Override
public void onDisplayInfoChanged(Context context, Info info, int flags) {
- onDisplayInfoChangedInternal(info, flags, false);
- }
-
- private void onDisplayInfoChangedInternal(Info info, int flags, boolean forceRegister) {
if ((flags & (CHANGE_ROTATION | CHANGE_ACTIVE_SCREEN | CHANGE_NAVIGATION_MODE
| CHANGE_SUPPORTED_BOUNDS)) != 0) {
mDisplayRotation = info.rotation;
@@ -300,12 +250,12 @@
mOrientationTouchTransformer.setNavigationMode(newMode, mDisplayController.getInfo(),
mContext.getResources());
- if (forceRegister || (!hasGestures(mMode) && hasGestures(newMode))) {
- setupOrientationSwipeHandler();
- } else if (hasGestures(mMode) && !hasGestures(newMode)) {
- destroyOrientationSwipeHandlerCallback();
+ TaskStackChangeListeners.getInstance()
+ .unregisterTaskStackListener(mFrozenTaskListener);
+ if (hasGestures(newMode)) {
+ TaskStackChangeListeners.getInstance()
+ .registerTaskStackListener(mFrozenTaskListener);
}
-
mMode = newMode;
}
}
@@ -363,7 +313,7 @@
// If we're in landscape w/o ever quickswitching, show the navbar in landscape
enableMultipleRegions(true);
}
- containerInterface.onExitOverview(this, mExitOverviewRunnable);
+ containerInterface.onExitOverview(mExitOverviewRunnable);
} else if (endTarget == GestureState.GestureEndTarget.HOME
|| endTarget == GestureState.GestureEndTarget.ALL_APPS) {
enableMultipleRegions(false);
diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
index f813d9a..233f0a9 100644
--- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
+++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
@@ -58,7 +58,7 @@
import java.util.function.Consumer;
public abstract class SwipeUpAnimationLogic implements
- RecentsAnimationCallbacks.RecentsAnimationListener{
+ RecentsAnimationCallbacks.RecentsAnimationListener {
protected static final Rect TEMP_RECT = new Rect();
protected final RemoteTargetGluer mTargetGluer;
@@ -66,7 +66,6 @@
protected DeviceProfile mDp;
protected final Context mContext;
- protected final RecentsAnimationDeviceState mDeviceState;
protected final GestureState mGestureState;
protected RemoteTargetHandle[] mRemoteTargetHandles;
@@ -85,20 +84,19 @@
protected boolean mIsSwipeForSplit;
- public SwipeUpAnimationLogic(Context context, RecentsAnimationDeviceState deviceState,
- GestureState gestureState) {
+ public SwipeUpAnimationLogic(Context context, GestureState gestureState) {
mContext = context;
- mDeviceState = deviceState;
mGestureState = gestureState;
updateIsGestureForSplit(TopTaskTracker.INSTANCE.get(context)
.getRunningSplitTaskIds().length);
mTargetGluer = new RemoteTargetGluer(mContext, mGestureState.getContainerInterface());
mRemoteTargetHandles = mTargetGluer.getRemoteTargetHandles();
+ RotationTouchHelper rotationTouchHelper = RotationTouchHelper.INSTANCE.get(context);
runActionOnRemoteHandles(remoteTargetHandle ->
remoteTargetHandle.getTaskViewSimulator().getOrientationState().update(
- mDeviceState.getRotationTouchHelper().getCurrentActiveRotation(),
- mDeviceState.getRotationTouchHelper().getDisplayRotation()
+ rotationTouchHelper.getCurrentActiveRotation(),
+ rotationTouchHelper.getDisplayRotation()
));
}
@@ -114,7 +112,7 @@
PendingAnimation pendingAnimation = new PendingAnimation(mTransitionDragLength * 2);
TaskViewSimulator taskViewSimulator = remoteHandle.getTaskViewSimulator();
taskViewSimulator.setDp(dp);
- taskViewSimulator.addAppToOverviewAnim(pendingAnimation, LINEAR);
+ taskViewSimulator.addAppToCarouselAnim(pendingAnimation, LINEAR);
AnimatorPlaybackController playbackController =
pendingAnimation.createPlaybackController();
@@ -505,6 +503,11 @@
}
}
+ if (Float.isNaN(scale)) {
+ Log.e(TAG, "Scale is NaN: starting dimensions=[" + startWidth + ", " + startHeight
+ + "], current dimensions=[" + currentWidth + ", " + currentHeight + "]");
+ }
+
mTargetTaskView.setScaleX(scale);
mTargetTaskView.setScaleY(scale);
mTargetTaskView.setTranslationX(
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.kt b/quickstep/src/com/android/quickstep/SystemUiProxy.kt
index 75694af..f2cedba 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.kt
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.kt
@@ -45,6 +45,7 @@
import android.window.RemoteTransition
import android.window.TaskSnapshot
import android.window.TransitionFilter
+import android.window.TransitionInfo
import androidx.annotation.MainThread
import androidx.annotation.VisibleForTesting
import androidx.annotation.WorkerThread
@@ -62,6 +63,7 @@
import com.android.quickstep.util.ActiveGestureProtoLogProxy
import com.android.quickstep.util.ContextualSearchInvoker
import com.android.quickstep.util.unfold.ProxyUnfoldTransitionProvider
+import com.android.systemui.contextualeducation.GestureType
import com.android.systemui.shared.recents.ISystemUiProxy
import com.android.systemui.shared.recents.model.ThumbnailData.Companion.wrap
import com.android.systemui.shared.system.QuickStepContract
@@ -215,9 +217,9 @@
systemUiProxy?.onImeSwitcherLongPress()
}
- fun updateContextualEduStats(isTrackpadGesture: Boolean, gestureType: String) =
+ fun updateContextualEduStats(isTrackpadGesture: Boolean, gestureType: GestureType) =
executeWithErrorLog({ "Failed call updateContextualEduStats" }) {
- systemUiProxy?.updateContextualEduStats(isTrackpadGesture, gestureType)
+ systemUiProxy?.updateContextualEduStats(isTrackpadGesture, gestureType.name)
}
fun setHomeRotationEnabled(enabled: Boolean) =
@@ -1174,6 +1176,7 @@
homeContentInsets: Rect?,
minimizedHomeBounds: Rect?,
extras: Bundle?,
+ transitionInfo: TransitionInfo?,
) =
listener.onAnimationStart(
RecentsAnimationControllerCompat(controller),
@@ -1186,6 +1189,7 @@
// https://developer.android.com/guide/components/aidl#Bundles
classLoader = SplitBounds::class.java.classLoader
},
+ transitionInfo,
)
override fun onAnimationCanceled(taskIds: IntArray?, taskSnapshots: Array<TaskSnapshot>?) =
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index e0d4ddd..1fd7211 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -20,7 +20,6 @@
import static com.android.launcher3.Flags.enableHandleDelayedGestureCallbacks;
import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.NavigationMode.NO_BUTTON;
import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
import static com.android.quickstep.GestureState.STATE_END_TARGET_ANIMATION_FINISHED;
@@ -36,6 +35,7 @@
import android.os.SystemProperties;
import android.util.Log;
import android.view.RemoteAnimationTarget;
+import android.window.TransitionInfo;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -53,7 +53,6 @@
import com.android.quickstep.util.SystemUiFlagUtils;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
@@ -69,6 +68,7 @@
private RecentsAnimationController mController;
private RecentsAnimationCallbacks mCallbacks;
private RecentsAnimationTargets mTargets;
+ private TransitionInfo mTransitionInfo;
private RecentsAnimationDeviceState mDeviceState;
// Temporary until we can hook into gesture state events
@@ -110,16 +110,6 @@
return SystemUiProxy.INSTANCE.get(mCtx);
}
- /**
- * Preloads the recents animation.
- */
- public void preloadRecentsAnimation(Intent intent) {
- // Pass null animation handler to indicate this start is for preloading
- UI_HELPER_EXECUTOR.execute(() -> {
- ActivityManagerWrapper.getInstance().preloadRecentsActivity(intent);
- });
- }
-
boolean shouldIgnoreMotionEvents() {
return mShouldIgnoreMotionEvents;
}
@@ -166,7 +156,7 @@
mCallbacks.addListener(new RecentsAnimationCallbacks.RecentsAnimationListener() {
@Override
public void onRecentsAnimationStart(RecentsAnimationController controller,
- RecentsAnimationTargets targets) {
+ RecentsAnimationTargets targets, TransitionInfo transitionInfo) {
if (enableHandleDelayedGestureCallbacks() && mRecentsAnimationStartPending) {
ActiveGestureProtoLogProxy.logStartRecentsAnimationCallback(
"onRecentsAnimationStart");
@@ -180,6 +170,7 @@
}
mController = controller;
mTargets = targets;
+ mTransitionInfo = transitionInfo;
// TODO(b/236226779): We can probably get away w/ setting mLastAppearedTaskTargets
// to all appeared targets directly vs just looking at running ones
int[] runningTaskIds = mLastGestureState.getRunningTaskIds(targets.apps.length > 1);
@@ -448,7 +439,7 @@
public void notifyRecentsAnimationState(
RecentsAnimationCallbacks.RecentsAnimationListener listener) {
if (isRecentsAnimationRunning()) {
- listener.onRecentsAnimationStart(mController, mTargets);
+ listener.onRecentsAnimationStart(mController, mTargets, mTransitionInfo);
}
// TODO: Do we actually need to report canceled/finished?
}
@@ -488,6 +479,7 @@
mController = null;
mCallbacks = null;
mTargets = null;
+ mTransitionInfo = null;
mLastGestureState = null;
mLastAppearedTaskTargets = null;
}
diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java
deleted file mode 100644
index c4221a1..0000000
--- a/quickstep/src/com/android/quickstep/TaskIconCache.java
+++ /dev/null
@@ -1,308 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep;
-
-import static com.android.launcher3.Flags.enableOverviewIconMenu;
-import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY;
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-
-import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.app.ActivityManager.TaskDescription;
-import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.UserHandle;
-import android.text.TextUtils;
-import android.util.SparseArray;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.WorkerThread;
-
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.icons.BaseIconFactory;
-import com.android.launcher3.icons.BaseIconFactory.IconOptions;
-import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.icons.IconProvider;
-import com.android.launcher3.pm.UserCache;
-import com.android.launcher3.util.CancellableTask;
-import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
-import com.android.launcher3.util.DisplayController.Info;
-import com.android.launcher3.util.FlagOp;
-import com.android.launcher3.util.Preconditions;
-import com.android.quickstep.task.thumbnail.data.TaskIconDataSource;
-import com.android.quickstep.util.TaskKeyLruCache;
-import com.android.quickstep.util.TaskVisualsChangeListener;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.Task.TaskKey;
-import com.android.systemui.shared.system.PackageManagerWrapper;
-
-import java.util.concurrent.Executor;
-
-/**
- * Manages the caching of task icons and related data.
- */
-public class TaskIconCache implements TaskIconDataSource, DisplayInfoChangeListener {
-
- private final Executor mBgExecutor;
-
- private final Context mContext;
- private final TaskKeyLruCache<TaskCacheEntry> mIconCache;
- private final SparseArray<BitmapInfo> mDefaultIcons = new SparseArray<>();
- private BitmapInfo mDefaultIconBase = null;
-
- private final IconProvider mIconProvider;
-
- private BaseIconFactory mIconFactory;
-
- @Nullable
- public TaskVisualsChangeListener mTaskVisualsChangeListener = null;
-
- public TaskIconCache(Context context, Executor bgExecutor, IconProvider iconProvider) {
- mContext = context;
- mBgExecutor = bgExecutor;
- mIconProvider = iconProvider;
-
- Resources res = context.getResources();
- int cacheSize = res.getInteger(R.integer.recentsIconCacheSize);
-
- mIconCache = new TaskKeyLruCache<>(cacheSize);
-
- DisplayController.INSTANCE.get(mContext).addChangeListener(this);
- }
-
- @Override
- public void onDisplayInfoChanged(Context context, Info info, int flags) {
- if ((flags & CHANGE_DENSITY) != 0) {
- clearCache();
- }
- }
-
- /**
- * Asynchronously fetches the icon and other task data.
- *
- * @param task The task to fetch the data for
- * @param callback The callback to receive the task after its data has been populated.
- * @return A cancelable handle to the request
- */
- @Override
- public CancellableTask getIconInBackground(Task task, @NonNull GetTaskIconCallback callback) {
- Preconditions.assertUIThread();
- if (task.icon != null) {
- // Nothing to load, the icon is already loaded
- callback.onTaskIconReceived(task.icon, task.titleDescription, task.title);
- return null;
- }
- CancellableTask<TaskCacheEntry> request = new CancellableTask<>(
- () -> getCacheEntry(task),
- MAIN_EXECUTOR,
- result -> {
- task.icon = result.icon;
- task.titleDescription = result.contentDescription;
- task.title = result.title;
-
- callback.onTaskIconReceived(
- result.icon,
- result.contentDescription,
- result.title);
- dispatchIconUpdate(task.key.id);
- }
- );
- mBgExecutor.execute(request);
- return request;
- }
-
- /**
- * Clears the icon cache
- */
- public void clearCache() {
- mBgExecutor.execute(this::resetFactory);
- }
-
- void onTaskRemoved(TaskKey taskKey) {
- mIconCache.remove(taskKey);
- }
-
- void invalidateCacheEntries(String pkg, UserHandle handle) {
- mBgExecutor.execute(() -> mIconCache.removeAll(key ->
- pkg.equals(key.getPackageName()) && handle.getIdentifier() == key.userId));
- }
-
- @WorkerThread
- private TaskCacheEntry getCacheEntry(Task task) {
- TaskCacheEntry entry = mIconCache.getAndInvalidateIfModified(task.key);
- if (entry != null) {
- return entry;
- }
-
- TaskDescription desc = task.taskDescription;
- TaskKey key = task.key;
- ActivityInfo activityInfo = null;
-
- // Create new cache entry
- entry = new TaskCacheEntry();
-
- // Load icon
- // TODO: Load icon resource (b/143363444)
- Bitmap icon = getIcon(desc, key.userId);
- if (icon != null) {
- entry.icon = getBitmapInfo(
- new BitmapDrawable(mContext.getResources(), icon),
- key.userId,
- desc.getPrimaryColor(),
- false /* isInstantApp */).newIcon(mContext);
- } else {
- activityInfo = PackageManagerWrapper.getInstance().getActivityInfo(
- key.getComponent(), key.userId);
- if (activityInfo != null) {
- BitmapInfo bitmapInfo = getBitmapInfo(
- mIconProvider.getIcon(activityInfo),
- key.userId,
- desc.getPrimaryColor(),
- activityInfo.applicationInfo.isInstantApp());
- entry.icon = bitmapInfo.newIcon(mContext);
- } else {
- entry.icon = getDefaultIcon(key.userId);
- }
- }
-
- // Skip loading the content description if the activity no longer exists
- if (activityInfo == null) {
- activityInfo = PackageManagerWrapper.getInstance().getActivityInfo(
- key.getComponent(), key.userId);
- }
- if (activityInfo != null) {
- entry.contentDescription = getBadgedContentDescription(
- activityInfo, task.key.userId, task.taskDescription);
- if (enableOverviewIconMenu()) {
- entry.title = Utilities.trim(activityInfo.loadLabel(mContext.getPackageManager()));
- }
- }
-
- mIconCache.put(task.key, entry);
- return entry;
- }
-
- private Bitmap getIcon(ActivityManager.TaskDescription desc, int userId) {
- if (desc.getInMemoryIcon() != null) {
- return desc.getInMemoryIcon();
- }
- return ActivityManager.TaskDescription.loadTaskDescriptionIcon(
- desc.getIconFilename(), userId);
- }
-
- private String getBadgedContentDescription(ActivityInfo info, int userId, TaskDescription td) {
- PackageManager pm = mContext.getPackageManager();
- String taskLabel = td == null ? null : Utilities.trim(td.getLabel());
- if (TextUtils.isEmpty(taskLabel)) {
- taskLabel = Utilities.trim(info.loadLabel(pm));
- }
-
- String applicationLabel = Utilities.trim(info.applicationInfo.loadLabel(pm));
- String badgedApplicationLabel = userId != UserHandle.myUserId()
- ? pm.getUserBadgedLabel(applicationLabel, UserHandle.of(userId)).toString()
- : applicationLabel;
- return applicationLabel.equals(taskLabel)
- ? badgedApplicationLabel : badgedApplicationLabel + " " + taskLabel;
- }
-
- @WorkerThread
- private Drawable getDefaultIcon(int userId) {
- synchronized (mDefaultIcons) {
- if (mDefaultIconBase == null) {
- try (BaseIconFactory bif = getIconFactory()) {
- mDefaultIconBase = bif.makeDefaultIcon(mIconProvider);
- }
- }
-
- int index;
- if ((index = mDefaultIcons.indexOfKey(userId)) >= 0) {
- return mDefaultIcons.valueAt(index).newIcon(mContext);
- } else {
- BitmapInfo info = mDefaultIconBase.withFlags(
- UserCache.INSTANCE.get(mContext).getUserInfo(UserHandle.of(userId))
- .applyBitmapInfoFlags(FlagOp.NO_OP));
- mDefaultIcons.put(userId, info);
- return info.newIcon(mContext);
- }
- }
- }
-
- @WorkerThread
- private BitmapInfo getBitmapInfo(Drawable drawable, int userId,
- int primaryColor, boolean isInstantApp) {
- try (BaseIconFactory bif = getIconFactory()) {
- bif.setWrapperBackgroundColor(primaryColor);
-
- // User version code O, so that the icon is always wrapped in an adaptive icon container
- return bif.createBadgedIconBitmap(drawable,
- new IconOptions()
- .setUser(UserCache.INSTANCE.get(mContext)
- .getUserInfo(UserHandle.of(userId)))
- .setInstantApp(isInstantApp)
- .setExtractedColor(0));
- }
- }
-
- @WorkerThread
- private BaseIconFactory getIconFactory() {
- if (mIconFactory == null) {
- mIconFactory = new BaseIconFactory(mContext,
- DisplayController.INSTANCE.get(mContext).getInfo().getDensityDpi(),
- mContext.getResources().getDimensionPixelSize(
- R.dimen.task_icon_cache_default_icon_size));
- }
- return mIconFactory;
- }
-
- @WorkerThread
- private void resetFactory() {
- mIconFactory = null;
- mIconCache.evictAll();
- }
-
- private static class TaskCacheEntry {
- public Drawable icon;
- public String contentDescription = "";
- public String title = "";
- }
-
- /** Callback used when retrieving app icons from cache. */
- public interface GetTaskIconCallback {
- /** Called when task icon is retrieved. */
- void onTaskIconReceived(Drawable icon, String contentDescription, String title);
- }
-
- void registerTaskVisualsChangeListener(TaskVisualsChangeListener newListener) {
- mTaskVisualsChangeListener = newListener;
- }
-
- void removeTaskVisualsChangeListener() {
- mTaskVisualsChangeListener = null;
- }
-
- void dispatchIconUpdate(int taskId) {
- if (mTaskVisualsChangeListener != null) {
- mTaskVisualsChangeListener.onTaskIconChanged(taskId);
- }
- }
-}
diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.kt b/quickstep/src/com/android/quickstep/TaskIconCache.kt
new file mode 100644
index 0000000..bf94d41
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/TaskIconCache.kt
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep
+
+import android.app.ActivityManager
+import android.content.Context
+import android.content.pm.ActivityInfo
+import android.graphics.Bitmap
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.Drawable
+import android.os.UserHandle
+import android.util.SparseArray
+import androidx.annotation.WorkerThread
+import com.android.launcher3.Flags.enableOverviewIconMenu
+import com.android.launcher3.Flags.enableRefactorTaskThumbnail
+import com.android.launcher3.R
+import com.android.launcher3.Utilities
+import com.android.launcher3.icons.BaseIconFactory
+import com.android.launcher3.icons.BaseIconFactory.IconOptions
+import com.android.launcher3.icons.BitmapInfo
+import com.android.launcher3.icons.IconProvider
+import com.android.launcher3.pm.UserCache
+import com.android.launcher3.util.CancellableTask
+import com.android.launcher3.util.DisplayController
+import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener
+import com.android.launcher3.util.Executors
+import com.android.launcher3.util.FlagOp
+import com.android.launcher3.util.Preconditions
+import com.android.quickstep.task.thumbnail.data.TaskIconDataSource
+import com.android.quickstep.util.TaskKeyLruCache
+import com.android.quickstep.util.TaskVisualsChangeListener
+import com.android.systemui.shared.recents.model.Task
+import com.android.systemui.shared.recents.model.Task.TaskKey
+import com.android.systemui.shared.system.PackageManagerWrapper
+import java.util.concurrent.Executor
+
+/** Manages the caching of task icons and related data. */
+class TaskIconCache(
+ private val context: Context,
+ private val bgExecutor: Executor,
+ private val iconProvider: IconProvider,
+) : TaskIconDataSource, DisplayInfoChangeListener {
+ private val iconCache =
+ TaskKeyLruCache<TaskCacheEntry>(
+ context.resources.getInteger(R.integer.recentsIconCacheSize)
+ )
+ private val defaultIcons = SparseArray<BitmapInfo>()
+ private var defaultIconBase: BitmapInfo? = null
+
+ private var _iconFactory: BaseIconFactory? = null
+ @get:WorkerThread
+ private val iconFactory: BaseIconFactory
+ get() =
+ if (enableRefactorTaskThumbnail()) createIconFactory()
+ else _iconFactory ?: createIconFactory().also { _iconFactory = it }
+
+ var taskVisualsChangeListener: TaskVisualsChangeListener? = null
+
+ init {
+ DisplayController.INSTANCE.get(context).addChangeListener(this)
+ }
+
+ override fun onDisplayInfoChanged(context: Context, info: DisplayController.Info, flags: Int) {
+ if ((flags and DisplayController.CHANGE_DENSITY) != 0) {
+ clearCache()
+ }
+ }
+
+ // TODO(b/387496731): Add ensureActive() calls if they show performance benefit
+ override suspend fun getIcon(task: Task): TaskCacheEntry {
+ task.icon?.let {
+ // Nothing to load, the icon is already loaded
+ return TaskCacheEntry(it, task.titleDescription ?: "", task.title)
+ }
+
+ val entry = getCacheEntry(task)
+ task.icon = entry.icon
+ task.titleDescription = entry.contentDescription
+ task.title = entry.title
+
+ dispatchIconUpdate(task.key.id)
+ return entry
+ }
+
+ /**
+ * Asynchronously fetches the icon and other task data.
+ *
+ * @param task The task to fetch the data for
+ * @param callback The callback to receive the task after its data has been populated.
+ * @return A cancelable handle to the request
+ */
+ fun getIconInBackground(task: Task, callback: GetTaskIconCallback): CancellableTask<*>? {
+ Preconditions.assertUIThread()
+ task.icon?.let {
+ // Nothing to load, the icon is already loaded
+ callback.onTaskIconReceived(it, task.titleDescription ?: "", task.title ?: "")
+ return null
+ }
+ val request =
+ CancellableTask(
+ { getCacheEntry(task) },
+ Executors.MAIN_EXECUTOR,
+ { result: TaskCacheEntry ->
+ task.icon = result.icon
+ task.titleDescription = result.contentDescription
+ task.title = result.title
+
+ callback.onTaskIconReceived(
+ result.icon,
+ result.contentDescription,
+ result.title,
+ )
+ dispatchIconUpdate(task.key.id)
+ },
+ )
+ bgExecutor.execute(request)
+ return request
+ }
+
+ /** Clears the icon cache */
+ fun clearCache() {
+ bgExecutor.execute { resetFactory() }
+ }
+
+ fun onTaskRemoved(taskKey: TaskKey) {
+ iconCache.remove(taskKey)
+ }
+
+ fun invalidateCacheEntries(pkg: String, handle: UserHandle) {
+ bgExecutor.execute {
+ iconCache.removeAll { key: TaskKey ->
+ pkg == key.packageName && handle.identifier == key.userId
+ }
+ }
+ }
+
+ @WorkerThread
+ private fun createIconFactory() =
+ BaseIconFactory(
+ context,
+ DisplayController.INSTANCE.get(context).info.densityDpi,
+ context.resources.getDimensionPixelSize(R.dimen.task_icon_cache_default_icon_size),
+ )
+
+ @WorkerThread
+ private fun getCacheEntry(task: Task): TaskCacheEntry {
+ iconCache.getAndInvalidateIfModified(task.key)?.let {
+ return it
+ }
+
+ val desc = task.taskDescription
+ val key = task.key
+ var activityInfo: ActivityInfo? = null
+
+ // Create new cache entry
+
+ // Load icon
+ val icon = getIcon(desc, key.userId)
+ val entryIcon =
+ if (icon != null) {
+ getBitmapInfo(
+ BitmapDrawable(context.resources, icon),
+ key.userId,
+ desc.primaryColor,
+ false, /* isInstantApp */
+ )
+ .newIcon(context)
+ } else {
+ activityInfo =
+ PackageManagerWrapper.getInstance().getActivityInfo(key.component, key.userId)
+ if (activityInfo != null) {
+ val bitmapInfo =
+ getBitmapInfo(
+ iconProvider.getIcon(activityInfo),
+ key.userId,
+ desc.primaryColor,
+ activityInfo.applicationInfo.isInstantApp,
+ )
+ bitmapInfo.newIcon(context)
+ } else {
+ getDefaultIcon(key.userId)
+ }
+ }
+
+ activityInfo =
+ activityInfo
+ ?: PackageManagerWrapper.getInstance().getActivityInfo(key.component, key.userId)
+
+ return when {
+ // Skip loading the content description if the activity no longer exists
+ activityInfo == null -> TaskCacheEntry(entryIcon)
+ enableOverviewIconMenu() ->
+ TaskCacheEntry(
+ entryIcon,
+ getBadgedContentDescription(
+ activityInfo,
+ task.key.userId,
+ task.taskDescription,
+ ),
+ Utilities.trim(activityInfo.loadLabel(context.packageManager)),
+ )
+ else ->
+ TaskCacheEntry(
+ entryIcon,
+ getBadgedContentDescription(activityInfo, task.key.userId, task.taskDescription),
+ )
+ }.also { iconCache.put(task.key, it) }
+ }
+
+ private fun getIcon(desc: ActivityManager.TaskDescription, userId: Int): Bitmap? =
+ desc.inMemoryIcon
+ ?: ActivityManager.TaskDescription.loadTaskDescriptionIcon(desc.iconFilename, userId)
+
+ private fun getBadgedContentDescription(
+ info: ActivityInfo,
+ userId: Int,
+ taskDescription: ActivityManager.TaskDescription?,
+ ): String {
+ val packageManager = context.packageManager
+ var taskLabel = taskDescription?.let { Utilities.trim(it.label) }
+ if (taskLabel.isNullOrEmpty()) {
+ taskLabel = Utilities.trim(info.loadLabel(packageManager))
+ }
+
+ val applicationLabel = Utilities.trim(info.applicationInfo.loadLabel(packageManager))
+ val badgedApplicationLabel =
+ if (userId != UserHandle.myUserId())
+ packageManager
+ .getUserBadgedLabel(applicationLabel, UserHandle.of(userId))
+ .toString()
+ else applicationLabel
+ return if (applicationLabel == taskLabel) badgedApplicationLabel
+ else "$badgedApplicationLabel $taskLabel"
+ }
+
+ @WorkerThread
+ private fun getDefaultIcon(userId: Int): Drawable {
+ synchronized(defaultIcons) {
+ val defaultIconBase =
+ defaultIconBase ?: iconFactory.use { it.makeDefaultIcon(iconProvider) }
+ val index: Int = defaultIcons.indexOfKey(userId)
+ return if (index >= 0) {
+ defaultIcons.valueAt(index).newIcon(context)
+ } else {
+ val info =
+ defaultIconBase.withFlags(
+ UserCache.INSTANCE.get(context)
+ .getUserInfo(UserHandle.of(userId))
+ .applyBitmapInfoFlags(FlagOp.NO_OP)
+ )
+ defaultIcons.put(userId, info)
+ info.newIcon(context)
+ }
+ }
+ }
+
+ @WorkerThread
+ private fun getBitmapInfo(
+ drawable: Drawable,
+ userId: Int,
+ primaryColor: Int,
+ isInstantApp: Boolean,
+ ): BitmapInfo {
+ iconFactory.use { iconFactory ->
+ iconFactory.setWrapperBackgroundColor(primaryColor)
+ // User version code O, so that the icon is always wrapped in an adaptive icon container
+ return iconFactory.createBadgedIconBitmap(
+ drawable,
+ IconOptions()
+ .setUser(UserCache.INSTANCE.get(context).getUserInfo(UserHandle.of(userId)))
+ .setInstantApp(isInstantApp)
+ .setExtractedColor(0),
+ )
+ }
+ }
+
+ @WorkerThread
+ private fun resetFactory() {
+ _iconFactory = null
+ iconCache.evictAll()
+ }
+
+ data class TaskCacheEntry(
+ val icon: Drawable,
+ val contentDescription: String = "",
+ val title: String = "",
+ )
+
+ /** Callback used when retrieving app icons from cache. */
+ fun interface GetTaskIconCallback {
+ /** Called when task icon is retrieved. */
+ fun onTaskIconReceived(icon: Drawable, contentDescription: String, title: String)
+ }
+
+ fun registerTaskVisualsChangeListener(newListener: TaskVisualsChangeListener?) {
+ taskVisualsChangeListener = newListener
+ }
+
+ fun removeTaskVisualsChangeListener() {
+ taskVisualsChangeListener = null
+ }
+
+ private fun dispatchIconUpdate(taskId: Int) {
+ taskVisualsChangeListener?.onTaskIconChanged(taskId)
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
deleted file mode 100644
index 580dcc2..0000000
--- a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep;
-
-import static com.android.launcher3.Flags.enableGridOnlyOverview;
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-
-import android.content.Context;
-import android.content.res.Resources;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
-
-import com.android.launcher3.R;
-import com.android.launcher3.util.CancellableTask;
-import com.android.launcher3.util.Preconditions;
-import com.android.quickstep.recents.data.HighResLoadingStateNotifier;
-import com.android.quickstep.task.thumbnail.data.TaskThumbnailDataSource;
-import com.android.quickstep.util.TaskKeyByLastActiveTimeCache;
-import com.android.quickstep.util.TaskKeyCache;
-import com.android.quickstep.util.TaskKeyLruCache;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.Task.TaskKey;
-import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-
-import java.util.ArrayList;
-import java.util.concurrent.Executor;
-import java.util.function.Consumer;
-
-public class TaskThumbnailCache implements TaskThumbnailDataSource {
-
- private final Executor mBgExecutor;
- private final TaskKeyCache<ThumbnailData> mCache;
- private final HighResLoadingState mHighResLoadingState;
- private final boolean mEnableTaskSnapshotPreloading;
- private final Context mContext;
-
- public static class HighResLoadingState implements HighResLoadingStateNotifier {
- private boolean mForceHighResThumbnails;
- private boolean mVisible;
- private boolean mFlingingFast;
- private boolean mHighResLoadingEnabled;
- private ArrayList<HighResLoadingStateChangedCallback> mCallbacks = new ArrayList<>();
-
- public interface HighResLoadingStateChangedCallback {
- void onHighResLoadingStateChanged(boolean enabled);
- }
-
- private HighResLoadingState(Context context) {
- // If the device does not support low-res thumbnails, only attempt to load high-res
- // thumbnails
- mForceHighResThumbnails = !supportsLowResThumbnails();
- }
-
- @Override
- public void addCallback(@NonNull HighResLoadingStateChangedCallback callback) {
- mCallbacks.add(callback);
- }
-
- @Override
- public void removeCallback(@NonNull HighResLoadingStateChangedCallback callback) {
- mCallbacks.remove(callback);
- }
-
- public void setVisible(boolean visible) {
- mVisible = visible;
- updateState();
- }
-
- public void setFlingingFast(boolean flingingFast) {
- mFlingingFast = flingingFast;
- updateState();
- }
-
- public boolean isEnabled() {
- return mHighResLoadingEnabled;
- }
-
- private void updateState() {
- boolean prevState = mHighResLoadingEnabled;
- mHighResLoadingEnabled = mForceHighResThumbnails || (mVisible && !mFlingingFast);
- if (prevState != mHighResLoadingEnabled) {
- for (int i = mCallbacks.size() - 1; i >= 0; i--) {
- mCallbacks.get(i).onHighResLoadingStateChanged(mHighResLoadingEnabled);
- }
- }
- }
- }
-
- public TaskThumbnailCache(Context context, Executor bgExecutor) {
- this(context, bgExecutor,
- context.getResources().getInteger(R.integer.recentsThumbnailCacheSize));
- }
-
- private TaskThumbnailCache(Context context, Executor bgExecutor, int cacheSize) {
- this(context, bgExecutor,
- enableGridOnlyOverview() ? new TaskKeyByLastActiveTimeCache<>(cacheSize)
- : new TaskKeyLruCache<>(cacheSize));
- }
-
- @VisibleForTesting
- TaskThumbnailCache(Context context, Executor bgExecutor, TaskKeyCache<ThumbnailData> cache) {
- mBgExecutor = bgExecutor;
- mHighResLoadingState = new HighResLoadingState(context);
- mContext = context;
-
- Resources res = context.getResources();
- mEnableTaskSnapshotPreloading = res.getBoolean(R.bool.config_enableTaskSnapshotPreloading);
- mCache = cache;
- }
-
- /**
- * Synchronously fetches the thumbnail for the given task at the specified resolution level, and
- * puts it in the cache.
- */
- public void updateThumbnailInCache(Task task, boolean lowResolution) {
- if (task == null) {
- return;
- }
- Preconditions.assertUIThread();
- // Fetch the thumbnail for this task and put it in the cache
- if (task.thumbnail == null) {
- getThumbnailInBackground(task.key, lowResolution, t -> task.thumbnail = t);
- }
- }
-
- /**
- * Synchronously updates the thumbnail in the cache if it is already there.
- */
- public void updateTaskSnapShot(int taskId, ThumbnailData thumbnail) {
- Preconditions.assertUIThread();
- mCache.updateIfAlreadyInCache(taskId, thumbnail);
- }
-
- /**
- * Asynchronously fetches the thumbnail for the given {@code task}.
- *
- * @param callback The callback to receive the task after its data has been populated.
- * @return A cancelable handle to the request
- */
- @Override
- public CancellableTask<ThumbnailData> getThumbnailInBackground(
- Task task, @NonNull Consumer<ThumbnailData> callback) {
- Preconditions.assertUIThread();
-
- boolean lowResolution = !mHighResLoadingState.isEnabled();
- if (task.thumbnail != null && task.thumbnail.getThumbnail() != null
- && (!task.thumbnail.reducedResolution || lowResolution)) {
- // Nothing to load, the thumbnail is already high-resolution or matches what the
- // request, so just callback
- callback.accept(task.thumbnail);
- return null;
- }
-
- return getThumbnailInBackground(task.key, !mHighResLoadingState.isEnabled(), callback);
- }
-
- /**
- * Updates cache size and remove excess entries if current size is more than new cache size.
- *
- * @return whether cache size has increased
- */
- public boolean updateCacheSizeAndRemoveExcess() {
- int newSize = mContext.getResources().getInteger(R.integer.recentsThumbnailCacheSize);
- int oldSize = mCache.getMaxSize();
- if (newSize == oldSize) {
- // Return if no change in size
- return false;
- }
-
- mCache.updateCacheSizeAndRemoveExcess(newSize);
- return newSize > oldSize;
- }
-
- private CancellableTask<ThumbnailData> getThumbnailInBackground(TaskKey key,
- boolean lowResolution, Consumer<ThumbnailData> callback) {
- Preconditions.assertUIThread();
-
- ThumbnailData cachedThumbnail = mCache.getAndInvalidateIfModified(key);
- if (cachedThumbnail != null && cachedThumbnail.getThumbnail() != null
- && (!cachedThumbnail.reducedResolution || lowResolution)) {
- // Already cached, lets use that thumbnail
- callback.accept(cachedThumbnail);
- return null;
- }
-
- CancellableTask<ThumbnailData> request = new CancellableTask<>(
- () -> {
- ThumbnailData thumbnailData = ActivityManagerWrapper.getInstance()
- .getTaskThumbnail(key.id, lowResolution);
- return thumbnailData.getThumbnail() != null ? thumbnailData
- : ActivityManagerWrapper.getInstance().takeTaskThumbnail(key.id);
- },
- MAIN_EXECUTOR,
- result -> {
- // Avoid an async timing issue that a low res entry replaces an existing high
- // res entry in high res enabled state, so we check before putting it to cache
- if (enableGridOnlyOverview() && result.reducedResolution
- && getHighResLoadingState().isEnabled()) {
- ThumbnailData newCachedThumbnail = mCache.getAndInvalidateIfModified(key);
- if (newCachedThumbnail != null && newCachedThumbnail.getThumbnail() != null
- && !newCachedThumbnail.reducedResolution) {
- return;
- }
- }
- mCache.put(key, result);
- callback.accept(result);
- }
- );
- mBgExecutor.execute(request);
- return request;
- }
-
- /**
- * Clears the cache.
- */
- public void clear() {
- mCache.evictAll();
- }
-
- /**
- * Removes the cached thumbnail for the given task.
- */
- public void remove(Task.TaskKey key) {
- mCache.remove(key);
- }
-
- /**
- * @return The cache size.
- */
- public int getCacheSize() {
- return mCache.getMaxSize();
- }
-
- /**
- * @return The mutable high-res loading state.
- */
- public HighResLoadingState getHighResLoadingState() {
- return mHighResLoadingState;
- }
-
- /**
- * @return Whether to enable background preloading of task thumbnails.
- */
- public boolean isPreloadingEnabled() {
- return mEnableTaskSnapshotPreloading && mHighResLoadingState.mVisible;
- }
-
- /**
- * @return Whether device supports low-res thumbnails. Low-res files are an optimization
- * for faster load times of snapshots. Devices can optionally disable low-res files so that
- * they only store snapshots at high-res scale. The actual scale can be configured in
- * frameworks/base config overlay.
- */
- private static boolean supportsLowResThumbnails() {
- Resources res = Resources.getSystem();
- int resId = res.getIdentifier("config_lowResTaskSnapshotScale", "dimen", "android");
- if (resId != 0) {
- return 0 < res.getFloat(resId);
- }
- return true;
- }
-
-}
diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailCache.kt b/quickstep/src/com/android/quickstep/TaskThumbnailCache.kt
new file mode 100644
index 0000000..1d880ab
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/TaskThumbnailCache.kt
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep
+
+import android.content.Context
+import androidx.annotation.VisibleForTesting
+import androidx.annotation.WorkerThread
+import com.android.launcher3.Flags.enableGridOnlyOverview
+import com.android.launcher3.R
+import com.android.launcher3.util.CancellableTask
+import com.android.launcher3.util.Executors
+import com.android.launcher3.util.Preconditions
+import com.android.quickstep.task.thumbnail.data.TaskThumbnailDataSource
+import com.android.quickstep.util.TaskKeyByLastActiveTimeCache
+import com.android.quickstep.util.TaskKeyCache
+import com.android.quickstep.util.TaskKeyLruCache
+import com.android.systemui.shared.recents.model.Task
+import com.android.systemui.shared.recents.model.Task.TaskKey
+import com.android.systemui.shared.recents.model.ThumbnailData
+import com.android.systemui.shared.system.ActivityManagerWrapper
+import java.util.concurrent.Executor
+import java.util.function.Consumer
+
+class TaskThumbnailCache
+@VisibleForTesting
+internal constructor(
+ private val context: Context,
+ private val bgExecutor: Executor,
+ private val cache: TaskKeyCache<ThumbnailData>,
+) : TaskThumbnailDataSource {
+ val highResLoadingState = HighResLoadingState()
+ private val enableTaskSnapshotPreloading =
+ context.resources.getBoolean(R.bool.config_enableTaskSnapshotPreloading)
+
+ @JvmOverloads
+ constructor(
+ context: Context,
+ bgExecutor: Executor,
+ cacheSize: Int = context.resources.getInteger(R.integer.recentsThumbnailCacheSize),
+ ) : this(
+ context,
+ bgExecutor,
+ if (enableGridOnlyOverview()) TaskKeyByLastActiveTimeCache(cacheSize)
+ else TaskKeyLruCache(cacheSize),
+ )
+
+ /**
+ * Synchronously fetches the thumbnail for the given task at the specified resolution level, and
+ * puts it in the cache.
+ */
+ fun updateThumbnailInCache(task: Task?, lowResolution: Boolean) {
+ task ?: return
+
+ Preconditions.assertUIThread()
+ // Fetch the thumbnail for this task and put it in the cache
+ if (task.thumbnail == null) {
+ getThumbnailInBackground(task.key, lowResolution) { t: ThumbnailData? ->
+ task.thumbnail = t
+ }
+ }
+ }
+
+ /** Synchronously updates the thumbnail in the cache if it is already there. */
+ fun updateTaskSnapShot(taskId: Int, thumbnail: ThumbnailData?) {
+ Preconditions.assertUIThread()
+ cache.updateIfAlreadyInCache(taskId, thumbnail)
+ }
+
+ // TODO(b/387496731): Add ensureActive() calls if they show performance benefit
+ /**
+ * Retrieves a thumbnail for the provided `task` on the current thread. This should not be
+ * called from the main thread.
+ */
+ @WorkerThread
+ override suspend fun getThumbnail(task: Task): ThumbnailData? {
+ val lowResolution: Boolean = !highResLoadingState.isEnabled
+ // Check task for thumbnail
+ val taskThumbnail: ThumbnailData? = task.thumbnail
+ if (
+ taskThumbnail?.thumbnail != null && (!taskThumbnail.reducedResolution || lowResolution)
+ ) {
+ return taskThumbnail
+ }
+
+ // Check cache for thumbnail
+ val cachedThumbnail: ThumbnailData? = cache.getAndInvalidateIfModified(task.key)
+ if (
+ cachedThumbnail?.thumbnail != null &&
+ (!cachedThumbnail.reducedResolution || lowResolution)
+ ) {
+ return cachedThumbnail
+ }
+
+ // Get thumbnail from system
+ var thumbnailData =
+ ActivityManagerWrapper.getInstance().getTaskThumbnail(task.key.id, lowResolution)
+ if (thumbnailData.thumbnail == null) {
+ thumbnailData = ActivityManagerWrapper.getInstance().takeTaskThumbnail(task.key.id)
+ }
+
+ // Avoid an async timing issue that a low res entry replaces an existing high
+ // res entry in high res enabled state, so we check before putting it to cache
+ if (
+ enableGridOnlyOverview() &&
+ thumbnailData.reducedResolution &&
+ highResLoadingState.isEnabled
+ ) {
+ val newCachedThumbnail = cache.getAndInvalidateIfModified(task.key)
+ if (newCachedThumbnail?.thumbnail != null && !newCachedThumbnail.reducedResolution) {
+ return newCachedThumbnail
+ }
+ }
+ cache.put(task.key, thumbnailData)
+ return thumbnailData
+ }
+
+ /**
+ * Asynchronously fetches the thumbnail for the given `task`.
+ *
+ * @param callback The callback to receive the task after its data has been populated.
+ * @return a cancelable handle to the request
+ */
+ fun getThumbnailInBackground(
+ task: Task,
+ callback: Consumer<ThumbnailData>,
+ ): CancellableTask<ThumbnailData>? {
+ Preconditions.assertUIThread()
+
+ val lowResolution = !highResLoadingState.isEnabled
+ val taskThumbnail = task.thumbnail
+ if (
+ taskThumbnail?.thumbnail != null && (!taskThumbnail.reducedResolution || lowResolution)
+ ) {
+ // Nothing to load, the thumbnail is already high-resolution or matches what the
+ // request, so just callback
+ callback.accept(taskThumbnail)
+ return null
+ }
+
+ return getThumbnailInBackground(task.key, !highResLoadingState.isEnabled, callback)
+ }
+
+ /**
+ * Updates cache size and remove excess entries if current size is more than new cache size.
+ *
+ * @return whether cache size has increased
+ */
+ fun updateCacheSizeAndRemoveExcess(): Boolean {
+ val newSize = context.resources.getInteger(R.integer.recentsThumbnailCacheSize)
+ val oldSize = cache.maxSize
+ if (newSize == oldSize) {
+ // Return if no change in size
+ return false
+ }
+
+ cache.updateCacheSizeAndRemoveExcess(newSize)
+ return newSize > oldSize
+ }
+
+ private fun getThumbnailInBackground(
+ key: TaskKey,
+ lowResolution: Boolean,
+ callback: Consumer<ThumbnailData>,
+ ): CancellableTask<ThumbnailData>? {
+ Preconditions.assertUIThread()
+
+ val cachedThumbnail = cache.getAndInvalidateIfModified(key)
+ if (
+ cachedThumbnail?.thumbnail != null &&
+ (!cachedThumbnail.reducedResolution || lowResolution)
+ ) {
+ // Already cached, lets use that thumbnail
+ callback.accept(cachedThumbnail)
+ return null
+ }
+
+ val request =
+ CancellableTask(
+ {
+ val thumbnailData =
+ ActivityManagerWrapper.getInstance().getTaskThumbnail(key.id, lowResolution)
+ if (thumbnailData.thumbnail != null) thumbnailData
+ else ActivityManagerWrapper.getInstance().takeTaskThumbnail(key.id)
+ },
+ Executors.MAIN_EXECUTOR,
+ Consumer { result: ThumbnailData ->
+ // Avoid an async timing issue that a low res entry replaces an existing high
+ // res entry in high res enabled state, so we check before putting it to cache
+ if (
+ enableGridOnlyOverview() &&
+ result.reducedResolution &&
+ highResLoadingState.isEnabled
+ ) {
+ val newCachedThumbnail = cache.getAndInvalidateIfModified(key)
+ if (
+ newCachedThumbnail?.thumbnail != null &&
+ !newCachedThumbnail.reducedResolution
+ ) {
+ return@Consumer
+ }
+ }
+ cache.put(key, result)
+ callback.accept(result)
+ },
+ )
+ bgExecutor.execute(request)
+ return request
+ }
+
+ /** Clears the cache. */
+ fun clear() {
+ cache.evictAll()
+ }
+
+ /** Removes the cached thumbnail for the given task. */
+ fun remove(key: TaskKey) {
+ cache.remove(key)
+ }
+
+ /** Returns The cache size. */
+ fun getCacheSize() = cache.maxSize
+
+ /** Returns Whether to enable background preloading of task thumbnails. */
+ fun isPreloadingEnabled() = enableTaskSnapshotPreloading && highResLoadingState.visible
+}
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index dec36cf..3133907 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -22,6 +22,7 @@
import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.app.animation.Interpolators.TOUCH_RESPONSE;
import static com.android.app.animation.Interpolators.clampToProgress;
+import static com.android.launcher3.Flags.enableGridOnlyOverview;
import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
@@ -189,7 +190,8 @@
RemoteTargetGluer gluer = new RemoteTargetGluer(v.getContext(),
recentsView.getSizeStrategy(), targets, forDesktop);
if (forDesktop) {
- remoteTargetHandles = gluer.assignTargetsForDesktop(targets);
+ remoteTargetHandles =
+ gluer.assignTargetsForDesktop(targets, /* transitionInfo=*/ null);
} else if (v.containsMultipleTasks()) {
remoteTargetHandles = gluer.assignTargetsForSplitScreen(targets,
((GroupedTaskView) v).getSplitBoundsConfig());
@@ -232,7 +234,9 @@
tvsLocal.fullScreenProgress.value = 0;
tvsLocal.recentsViewScale.value = 1;
- tvsLocal.setIsGridTask(v.isGridTask());
+ if (!enableGridOnlyOverview()) {
+ tvsLocal.setIsGridTask(v.isGridTask());
+ }
tvsLocal.getOrientationState().getOrientationHandler().set(tvsLocal,
TaskViewSimulator::setTaskRectTranslation, taskRectTranslationPrimary,
taskRectTranslationSecondary);
diff --git a/quickstep/src/com/android/quickstep/TopTaskTracker.java b/quickstep/src/com/android/quickstep/TopTaskTracker.java
index 210065a..bfd6107 100644
--- a/quickstep/src/com/android/quickstep/TopTaskTracker.java
+++ b/quickstep/src/com/android/quickstep/TopTaskTracker.java
@@ -37,13 +37,16 @@
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
-import com.android.launcher3.util.MainThreadInitializedObject;
-import com.android.launcher3.util.SafeCloseable;
+import com.android.launcher3.dagger.ApplicationContext;
+import com.android.launcher3.dagger.LauncherAppSingleton;
+import com.android.launcher3.util.DaggerSingletonObject;
+import com.android.launcher3.util.DaggerSingletonTracker;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.SplitConfigurationOptions.SplitStageInfo;
import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
import com.android.launcher3.util.SplitConfigurationOptions.StageType;
import com.android.launcher3.util.TraceHelper;
+import com.android.quickstep.dagger.QuickstepBaseAppComponent;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.Task.TaskKey;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -60,15 +63,17 @@
import java.util.LinkedList;
import java.util.List;
+import javax.inject.Inject;
+
/**
* This class tracked the top-most task and some 'approximate' task history to allow faster
* system state estimation during touch interaction
*/
-public class TopTaskTracker extends ISplitScreenListener.Stub
- implements TaskStackChangeListener, SafeCloseable {
+@LauncherAppSingleton
+public class TopTaskTracker extends ISplitScreenListener.Stub implements TaskStackChangeListener {
private static final String TAG = "TopTaskTracker";
- public static MainThreadInitializedObject<TopTaskTracker> INSTANCE =
- new MainThreadInitializedObject<>(TopTaskTracker::new);
+ public static DaggerSingletonObject<TopTaskTracker> INSTANCE =
+ new DaggerSingletonObject<>(QuickstepBaseAppComponent::getTopTaskTracker);
private static final int HISTORY_SIZE = 5;
@@ -86,7 +91,9 @@
// bottom most.
private ArrayMap<Integer, ArrayList<GroupedTaskInfo>> mVisibleTasks = new ArrayMap<>();
- private TopTaskTracker(Context context) {
+ @Inject
+ public TopTaskTracker(@ApplicationContext Context context, DaggerSingletonTracker tracker,
+ SystemUiProxy systemUiProxy) {
mContext = context;
if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
@@ -98,18 +105,17 @@
mSideStagePosition.stageType = SplitConfigurationOptions.STAGE_TYPE_SIDE;
TaskStackChangeListeners.getInstance().registerTaskStackListener(this);
- SystemUiProxy.INSTANCE.get(context).registerSplitScreenListener(this);
- }
- }
-
- @Override
- public void close() {
- if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
- return;
+ systemUiProxy.registerSplitScreenListener(this);
}
- TaskStackChangeListeners.getInstance().unregisterTaskStackListener(this);
- SystemUiProxy.INSTANCE.get(mContext).unregisterSplitScreenListener(this);
+ tracker.addCloseable(() -> {
+ if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ return;
+ }
+
+ TaskStackChangeListeners.getInstance().unregisterTaskStackListener(this);
+ systemUiProxy.unregisterSplitScreenListener(this);
+ });
}
@Override
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index cb5d289..f7fb18b 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -45,13 +45,12 @@
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Region;
-import android.hardware.input.InputManager;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.IRemoteCallback;
import android.os.Looper;
+import android.os.RemoteException;
import android.os.SystemClock;
-import android.os.Trace;
-import android.util.ArraySet;
import android.util.Log;
import android.view.Choreographer;
import android.view.InputDevice;
@@ -59,7 +58,6 @@
import android.view.MotionEvent;
import androidx.annotation.BinderThread;
-import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
@@ -71,7 +69,6 @@
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.desktop.DesktopAppLaunchTransitionManager;
-import com.android.launcher3.provider.RestoreDbTask;
import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.taskbar.TaskbarActivityContext;
@@ -98,6 +95,8 @@
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.ActiveGestureLog.CompoundString;
import com.android.quickstep.util.ActiveGestureProtoLogProxy;
+import com.android.quickstep.util.ActiveTrackpadList;
+import com.android.quickstep.util.ActivityPreloadUtil;
import com.android.quickstep.util.ContextualSearchInvoker;
import com.android.quickstep.util.ContextualSearchStateManager;
import com.android.quickstep.views.RecentsViewContainer;
@@ -124,7 +123,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
-import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -186,9 +184,8 @@
recentTasks, launcherUnlockAnimationController, backAnimation, desktopMode,
unfoldTransition, dragAndDrop);
tis.initInputMonitor("TISBinder#onInitialize()");
- tis.preloadOverview(true /* fromInit */);
+ ActivityPreloadUtil.preloadOverviewForTIS(tis, true /* fromInit */);
}));
- sIsInitialized = true;
}
@BinderThread
@@ -354,16 +351,6 @@
));
}
- /**
- * Preloads the Overview activity.
- * <p>
- * This method should only be used when the All Set page of the SUW is reached to safely
- * preload the Launcher for the SUW first reveal.
- */
- public void preloadOverviewForSUWAllSet() {
- executeForTouchInteractionService(tis -> tis.preloadOverview(false, true));
- }
-
@Override
public void onRotationProposal(int rotation, boolean isValid) {
executeForTaskbarManager(taskbarManager ->
@@ -400,6 +387,20 @@
taskbarManager.onNavigationBarLumaSamplingEnabled(displayId, enable));
}
+ @Override
+ public void onUnbind(IRemoteCallback reply) {
+ // Run everything in the same main thread block to ensure the cleanup happens before
+ // sending the reply.
+ MAIN_EXECUTOR.execute(() -> {
+ executeForTaskbarManager(TaskbarManager::destroy);
+ try {
+ reply.sendResult(null);
+ } catch (RemoteException e) {
+ Log.w(TAG, "onUnbind: Failed to reply to OverviewProxyService", e);
+ }
+ });
+ }
+
private void executeForTouchInteractionService(
@NonNull Consumer<TouchInteractionService> tisConsumer) {
TouchInteractionService tis = mTis.get();
@@ -428,18 +429,6 @@
return tis.mTaskbarManager;
}
- /**
- * Returns the {@link DesktopVisibilityController}
- * <p>
- * Returns {@code null} if TouchInteractionService is not connected
- */
- @Nullable
- public DesktopVisibilityController getDesktopVisibilityController() {
- TouchInteractionService tis = mTis.get();
- if (tis == null) return null;
- return tis.mDesktopVisibilityController;
- }
-
@VisibleForTesting
public void injectFakeTrackpadForTesting() {
TouchInteractionService tis = mTis.get();
@@ -504,72 +493,8 @@
}
}
- private final InputManager.InputDeviceListener mInputDeviceListener =
- new InputManager.InputDeviceListener() {
- @Override
- public void onInputDeviceAdded(int deviceId) {
- if (isTrackpadDevice(deviceId)) {
- // This updates internal TIS state so it needs to also run on the main
- // thread.
- MAIN_EXECUTOR.execute(() -> {
- boolean wasEmpty = mTrackpadsConnected.isEmpty();
- mTrackpadsConnected.add(deviceId);
- if (wasEmpty) {
- update();
- }
- });
- }
- }
-
- @Override
- public void onInputDeviceChanged(int deviceId) {
- }
-
- @Override
- public void onInputDeviceRemoved(int deviceId) {
- // This updates internal TIS state so it needs to also run on the main
- // thread.
- MAIN_EXECUTOR.execute(() -> {
- mTrackpadsConnected.remove(deviceId);
- if (mTrackpadsConnected.isEmpty()) {
- update();
- }
- });
- }
-
- @MainThread
- private void update() {
- if (mInputMonitorCompat != null && !mTrackpadsConnected.isEmpty()) {
- // Don't destroy and reinitialize input monitor due to trackpad
- // connecting when it's already set up.
- return;
- }
- initInputMonitor("onTrackpadConnected()");
- }
-
- private boolean isTrackpadDevice(int deviceId) {
- // This is a blocking binder call that should run on a bg thread.
- InputDevice inputDevice = mInputManager.getInputDevice(deviceId);
- if (inputDevice == null) {
- return false;
- }
- return inputDevice.getSources() == (InputDevice.SOURCE_MOUSE
- | InputDevice.SOURCE_TOUCHPAD);
- }
- };
-
- private static boolean sConnected = false;
- private static boolean sIsInitialized = false;
private RotationTouchHelper mRotationTouchHelper;
- public static boolean isConnected() {
- return sConnected;
- }
-
- public static boolean isInitialized() {
- return sIsInitialized;
- }
-
private final AbsSwipeUpHandler.Factory mLauncherSwipeHandlerFactory =
this::createLauncherSwipeHandler;
private final AbsSwipeUpHandler.Factory mFallbackSwipeHandlerFactory =
@@ -618,12 +543,10 @@
private TaskbarManager mTaskbarManager;
private Function<GestureState, AnimatedFloat> mSwipeUpProxyProvider = i -> null;
private AllAppsActionManager mAllAppsActionManager;
- private InputManager mInputManager;
- private final Set<Integer> mTrackpadsConnected = new ArraySet<>();
+ private ActiveTrackpadList mTrackpadsConnected;
private NavigationMode mGestureStartNavMode = null;
- private DesktopVisibilityController mDesktopVisibilityController;
private DesktopAppLaunchTransitionManager mDesktopAppLaunchTransitionManager;
@Override
@@ -634,20 +557,20 @@
// Initialize anything here that is needed in direct boot mode.
// Everything else should be initialized in onUserUnlocked() below.
mMainChoreographer = Choreographer.getInstance();
- mDeviceState = new RecentsAnimationDeviceState(this, true);
- mRotationTouchHelper = mDeviceState.getRotationTouchHelper();
+ mDeviceState = RecentsAnimationDeviceState.INSTANCE.get(this);
+ mRotationTouchHelper = RotationTouchHelper.INSTANCE.get(this);
mAllAppsActionManager = new AllAppsActionManager(
this, UI_HELPER_EXECUTOR, this::createAllAppsPendingIntent);
- mInputManager = getSystemService(InputManager.class);
- mInputManager.registerInputDeviceListener(mInputDeviceListener,
- UI_HELPER_EXECUTOR.getHandler());
- int [] inputDevices = mInputManager.getInputDeviceIds();
- for (int inputDeviceId : inputDevices) {
- mInputDeviceListener.onInputDeviceAdded(inputDeviceId);
- }
- mDesktopVisibilityController = new DesktopVisibilityController(this);
- mTaskbarManager = new TaskbarManager(
- this, mAllAppsActionManager, mNavCallbacks, mDesktopVisibilityController);
+ mTrackpadsConnected = new ActiveTrackpadList(this, () -> {
+ if (mInputMonitorCompat != null && !mTrackpadsConnected.isEmpty()) {
+ // Don't destroy and reinitialize input monitor due to trackpad
+ // connecting when it's already set up.
+ return;
+ }
+ initInputMonitor("onTrackpadConnected()");
+ });
+
+ mTaskbarManager = new TaskbarManager(this, mAllAppsActionManager, mNavCallbacks);
mDesktopAppLaunchTransitionManager =
new DesktopAppLaunchTransitionManager(this, SystemUiProxy.INSTANCE.get(this));
mDesktopAppLaunchTransitionManager.registerTransitions();
@@ -656,8 +579,6 @@
// Call runOnUserUnlocked() before any other callbacks to ensure everything is initialized.
LockedUserState.get(this).runOnUserUnlocked(mUserUnlockedRunnable);
mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged);
- sConnected = true;
-
ScreenOnTracker.INSTANCE.get(this).addListener(mScreenOnListener);
}
@@ -793,28 +714,22 @@
public void onDestroy() {
Log.d(TAG, "onDestroy: user=" + getUserId()
+ " instance=" + System.identityHashCode(this));
- sIsInitialized = false;
if (LockedUserState.get(this).isUserUnlocked()) {
mInputConsumer.unregisterInputConsumer();
mOverviewComponentObserver.setHomeDisabled(false);
mOverviewComponentObserver.removeOverviewChangeListener(mOverviewChangeListener);
}
disposeEventHandlers("TouchInteractionService onDestroy()");
- mDeviceState.destroy();
SystemUiProxy.INSTANCE.get(this).clearProxy();
mAllAppsActionManager.onDestroy();
- mInputManager.unregisterInputDeviceListener(mInputDeviceListener);
- mTrackpadsConnected.clear();
-
+ mTrackpadsConnected.destroy();
mTaskbarManager.destroy();
if (mDesktopAppLaunchTransitionManager != null) {
mDesktopAppLaunchTransitionManager.unregisterTransitions();
}
mDesktopAppLaunchTransitionManager = null;
- mDesktopVisibilityController.onDestroy();
- sConnected = false;
LockedUserState.get(this).removeOnUserUnlockedRunnable(mUserUnlockedRunnable);
ScreenOnTracker.INSTANCE.get(this).removeListener(mScreenOnListener);
@@ -1125,47 +1040,6 @@
}
}
- private void preloadOverview(boolean fromInit) {
- Trace.beginSection("preloadOverview(fromInit=" + fromInit + ")");
- preloadOverview(fromInit, false);
- Trace.endSection();
- }
-
- private void preloadOverview(boolean fromInit, boolean forSUWAllSet) {
- if (!LockedUserState.get(this).isUserUnlocked()) {
- return;
- }
-
- if (mDeviceState.isButtonNavMode() && !mOverviewComponentObserver.isHomeAndOverviewSame()) {
- // Prevent the overview from being started before the real home on first boot.
- return;
- }
-
- if ((RestoreDbTask.isPending(this) && !forSUWAllSet)
- || !mDeviceState.isUserSetupComplete()) {
- // Preloading while a restore is pending may cause launcher to start the restore
- // too early.
- return;
- }
-
- final BaseContainerInterface containerInterface =
- mOverviewComponentObserver.getContainerInterface();
- final Intent overviewIntent = new Intent(
- mOverviewComponentObserver.getOverviewIntentIgnoreSysUiState());
- if (containerInterface.getCreatedContainer() != null && fromInit) {
- // The activity has been created before the initialization of overview service. It is
- // usually happens when booting or launcher is the top activity, so we should already
- // have the latest state.
- return;
- }
-
- // TODO(b/258022658): Remove temporary logging.
- Log.i(TAG, "preloadOverview: forSUWAllSet=" + forSUWAllSet
- + ", isHomeAndOverviewSame=" + mOverviewComponentObserver.isHomeAndOverviewSame());
- ActiveGestureProtoLogProxy.logPreloadRecentsAnimation();
- mTaskAnimationManager.preloadRecentsAnimation(overviewIntent);
- }
-
@Override
public void onConfigurationChanged(Configuration newConfig) {
if (!LockedUserState.get(this).isUserUnlocked()) {
@@ -1193,7 +1067,7 @@
return;
}
- preloadOverview(false /* fromInit */);
+ ActivityPreloadUtil.preloadOverviewForTIS(this, false /* fromInit */);
}
private static boolean isTablet(Configuration config) {
@@ -1237,7 +1111,7 @@
createdOverviewContainer.getDeviceProfile().dump(this, "", pw);
}
mTaskbarManager.dumpLogs("", pw);
- mDesktopVisibilityController.dumpLogs("", pw);
+ DesktopVisibilityController.INSTANCE.get(this).dumpLogs("", pw);
pw.println("ContextualSearchStateManager:");
ContextualSearchStateManager.INSTANCE.get(this).dump("\t", pw);
SystemUiProxy.INSTANCE.get(this).dump(pw);
@@ -1247,21 +1121,21 @@
private AbsSwipeUpHandler createLauncherSwipeHandler(
GestureState gestureState, long touchTimeMs) {
- return new LauncherSwipeHandlerV2(this, mDeviceState, mTaskAnimationManager,
+ return new LauncherSwipeHandlerV2(this, mTaskAnimationManager,
gestureState, touchTimeMs, mTaskAnimationManager.isRecentsAnimationRunning(),
mInputConsumer, MSDLPlayerWrapper.INSTANCE.get(this));
}
private AbsSwipeUpHandler createFallbackSwipeHandler(
GestureState gestureState, long touchTimeMs) {
- return new FallbackSwipeHandler(this, mDeviceState, mTaskAnimationManager,
+ return new FallbackSwipeHandler(this, mTaskAnimationManager,
gestureState, touchTimeMs, mTaskAnimationManager.isRecentsAnimationRunning(),
mInputConsumer, MSDLPlayerWrapper.INSTANCE.get(this));
}
private AbsSwipeUpHandler createRecentsWindowSwipeHandler(
GestureState gestureState, long touchTimeMs) {
- return new RecentsWindowSwipeHandler(this, mDeviceState, mTaskAnimationManager,
+ return new RecentsWindowSwipeHandler(this, mTaskAnimationManager,
gestureState, touchTimeMs, mTaskAnimationManager.isRecentsAnimationRunning(),
mInputConsumer, MSDLPlayerWrapper.INSTANCE.get(this));
}
diff --git a/quickstep/src/com/android/quickstep/contextualeducation/SystemContextualEduStatsManager.java b/quickstep/src/com/android/quickstep/contextualeducation/SystemContextualEduStatsManager.java
deleted file mode 100644
index 6a72537..0000000
--- a/quickstep/src/com/android/quickstep/contextualeducation/SystemContextualEduStatsManager.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 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.contextualeducation;
-
-import com.android.launcher3.contextualeducation.ContextualEduStatsManager;
-import com.android.launcher3.dagger.LauncherAppSingleton;
-import com.android.quickstep.SystemUiProxy;
-import com.android.systemui.contextualeducation.GestureType;
-
-import javax.inject.Inject;
-
-/**
- * A class to update contextual education data via {@link SystemUiProxy}
- */
-@LauncherAppSingleton
-public class SystemContextualEduStatsManager extends ContextualEduStatsManager {
- private final SystemUiProxy mSystemUiProxy;
-
- @Inject
- public SystemContextualEduStatsManager(SystemUiProxy systemUiProxy) {
- mSystemUiProxy = systemUiProxy;
- }
-
- @Override
- public void updateEduStats(boolean isTrackpadGesture, GestureType gestureType) {
- mSystemUiProxy.updateContextualEduStats(isTrackpadGesture,
- gestureType.name());
- }
-}
diff --git a/quickstep/src/com/android/quickstep/dagger/QuickStepModule.java b/quickstep/src/com/android/quickstep/dagger/QuickStepModule.java
deleted file mode 100644
index a6feff0..0000000
--- a/quickstep/src/com/android/quickstep/dagger/QuickStepModule.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep.dagger;
-
-import com.android.launcher3.contextualeducation.ContextualEduStatsManager;
-import com.android.launcher3.uioverrides.SystemApiWrapper;
-import com.android.launcher3.uioverrides.plugins.PluginManagerWrapperImpl;
-import com.android.launcher3.util.ApiWrapper;
-import com.android.launcher3.util.PluginManagerWrapper;
-import com.android.launcher3.util.window.WindowManagerProxy;
-import com.android.quickstep.contextualeducation.SystemContextualEduStatsManager;
-import com.android.quickstep.util.SystemWindowManagerProxy;
-
-import dagger.Binds;
-import dagger.Module;
-
-@Module
-public abstract class QuickStepModule {
-
- @Binds abstract PluginManagerWrapper bindPluginManagerWrapper(PluginManagerWrapperImpl impl);
- @Binds abstract ApiWrapper bindApiWrapper(SystemApiWrapper systemApiWrapper);
- @Binds abstract ContextualEduStatsManager bindContextualEduStatsManager(
- SystemContextualEduStatsManager manager);
- @Binds abstract WindowManagerProxy bindWindowManagerProxy(SystemWindowManagerProxy proxy);
-}
diff --git a/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java b/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java
index 20a66dd..1d40d76 100644
--- a/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java
+++ b/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java
@@ -19,8 +19,10 @@
import com.android.launcher3.dagger.LauncherAppComponent;
import com.android.launcher3.dagger.LauncherBaseAppComponent;
import com.android.launcher3.model.WellbeingModel;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.quickstep.OverviewComponentObserver;
import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.TopTaskTracker;
import com.android.quickstep.fallback.window.RecentsDisplayModel;
import com.android.quickstep.util.AsyncClockEventDelegate;
@@ -43,4 +45,8 @@
RecentsDisplayModel getRecentsDisplayModel();
OverviewComponentObserver getOverviewComponentObserver();
+
+ DesktopVisibilityController getDesktopVisibilityController();
+
+ TopTaskTracker getTopTaskTracker();
}
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
index daac9fb..44fdaec 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
@@ -136,7 +136,7 @@
setter.add(pa.buildAnim());
}
- Pair<FloatProperty<RecentsView>, FloatProperty<RecentsView>> taskViewsFloat =
+ Pair<FloatProperty<RecentsView<?, ?>>, FloatProperty<RecentsView<?, ?>>> taskViewsFloat =
mRecentsView.getPagedOrientationHandler().getSplitSelectTaskOffset(
TASK_PRIMARY_SPLIT_TRANSLATION, TASK_SECONDARY_SPLIT_TRANSLATION,
mRecentsViewContainer.getDeviceProfile());
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 9625d29..d9209bf 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -36,6 +36,7 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.desktop.DesktopRecentsTransitionController;
import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.launcher3.statemanager.StatefulContainer;
@@ -44,7 +45,6 @@
import com.android.quickstep.BaseContainerInterface;
import com.android.quickstep.FallbackActivityInterface;
import com.android.quickstep.GestureState;
-import com.android.quickstep.RotationTouchHelper;
import com.android.quickstep.fallback.window.RecentsDisplayModel;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.util.SplitSelectStateController;
@@ -113,12 +113,11 @@
* to the home task. This allows us to handle quick-switch similarly to a quick-switching
* from a foreground task.
*/
- public void onGestureAnimationStartOnHome(Task[] homeTask,
- RotationTouchHelper rotationTouchHelper) {
+ public void onGestureAnimationStartOnHome(Task[] homeTask) {
// TODO(b/195607777) General fallback love, but this might be correct
// Home task should be defined as the front-most task info I think?
mHomeTask = homeTask.length > 0 ? homeTask[0] : null;
- onGestureAnimationStart(homeTask, rotationTouchHelper);
+ onGestureAnimationStart(homeTask);
}
/**
@@ -268,9 +267,7 @@
@Override
public void onStateTransitionComplete(RecentsState finalState) {
- if (mContainer.getDesktopVisibilityController() != null) {
- mContainer.getDesktopVisibilityController().onLauncherStateChanged(finalState);
- }
+ DesktopVisibilityController.INSTANCE.get(mContainer).onLauncherStateChanged(finalState);
if (!finalState.isRecentsViewVisible()) {
// Clean-up logic that occurs when recents is no longer in use/visible.
reset();
@@ -287,11 +284,7 @@
}
if (finalState != OVERVIEW_SPLIT_SELECT) {
- if (FeatureFlags.enableSplitContextually()) {
- mSplitSelectStateController.resetState();
- } else {
- resetFromSplitSelectionState();
- }
+ mSplitSelectStateController.resetState();
}
// disabling this so app icons aren't drawn on top of recent tasks.
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt b/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt
index a9259d9..e7e9f51 100644
--- a/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt
@@ -17,13 +17,14 @@
package com.android.quickstep.fallback.window
import android.content.Context
-import android.os.Handler
import android.util.Log
import android.view.Display
import com.android.launcher3.Flags
import com.android.launcher3.dagger.ApplicationContext
import com.android.launcher3.dagger.LauncherAppSingleton
import com.android.launcher3.util.DaggerSingletonObject
+import com.android.launcher3.util.DaggerSingletonTracker
+import com.android.launcher3.util.Executors
import com.android.quickstep.DisplayModel
import com.android.quickstep.FallbackWindowInterface
import com.android.quickstep.dagger.QuickstepBaseAppComponent
@@ -31,7 +32,9 @@
import javax.inject.Inject
@LauncherAppSingleton
-class RecentsDisplayModel @Inject constructor(@ApplicationContext context: Context) :
+class RecentsDisplayModel
+@Inject
+constructor(@ApplicationContext context: Context, tracker: DaggerSingletonTracker) :
DisplayModel<RecentsDisplayResource>(context) {
companion object {
@@ -47,17 +50,36 @@
init {
if (Flags.enableFallbackOverviewInWindow() || Flags.enableLauncherOverviewInWindow()) {
- displayManager.registerDisplayListener(displayListener, Handler.getMain())
- createDisplayResource(Display.DEFAULT_DISPLAY)
+ displayManager.registerDisplayListener(displayListener, Executors.MAIN_EXECUTOR.handler)
+ // In the scenario where displays were added before this display listener was
+ // registered, we should store the RecentsDisplayResources for those displays
+ // directly.
+ displayManager.displays
+ .filter { getDisplayResource(it.displayId) == null }
+ .forEach { storeRecentsDisplayResource(it.displayId, it) }
+ tracker.addCloseable { destroy() }
}
}
override fun createDisplayResource(displayId: Int) {
- if (DEBUG) Log.d(TAG, "create: displayId=$displayId")
+ if (DEBUG) Log.d(TAG, "createDisplayResource: displayId=$displayId")
getDisplayResource(displayId)?.let {
return
}
val display = displayManager.getDisplay(displayId)
+ if (display == null) {
+ if (DEBUG)
+ Log.w(
+ TAG,
+ "createDisplayResource: could not create display for displayId=$displayId",
+ Exception(),
+ )
+ return
+ }
+ storeRecentsDisplayResource(displayId, display)
+ }
+
+ private fun storeRecentsDisplayResource(displayId: Int, display: Display) {
displayResourceArray[displayId] =
RecentsDisplayResource(displayId, context.createDisplayContext(display))
}
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt
index 9bd7a19..5d99aec 100644
--- a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt
@@ -37,12 +37,12 @@
import com.android.launcher3.LauncherAnimationRunner.RemoteAnimationFactory
import com.android.launcher3.R
import com.android.launcher3.compat.AccessibilityManagerCompat
-import com.android.launcher3.statehandlers.DesktopVisibilityController
import com.android.launcher3.statemanager.StateManager
import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory
import com.android.launcher3.statemanager.StatefulContainer
import com.android.launcher3.taskbar.TaskbarUIController
import com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL
+import com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_SPLIT_SELECT_ORDINAL
import com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_STATE_ORDINAL
import com.android.launcher3.util.ContextTracker
import com.android.launcher3.util.DisplayController
@@ -163,6 +163,7 @@
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(taskStackChangeListener)
callbacks?.removeListener(recentsAnimationListener)
recentsWindowTracker.onContextDestroyed(this)
+ recentsView?.destroy()
}
override fun startHome() {
@@ -310,10 +311,6 @@
return overviewCommandHelper == null || overviewCommandHelper.canStartHomeSafely()
}
- override fun getDesktopVisibilityController(): DesktopVisibilityController? {
- return tisBindHelper.desktopVisibilityController
- }
-
override fun setTaskbarUIController(taskbarUIController: TaskbarUIController?) {
this.taskbarUIController = taskbarUIController
}
@@ -355,17 +352,23 @@
cleanupRecentsWindow()
}
when (state) {
- HOME ->
+ HOME,
+ BG_LAUNCHER ->
AccessibilityManagerCompat.sendStateEventToTest(baseContext, NORMAL_STATE_ORDINAL)
DEFAULT ->
AccessibilityManagerCompat.sendStateEventToTest(baseContext, OVERVIEW_STATE_ORDINAL)
+ OVERVIEW_SPLIT_SELECT ->
+ AccessibilityManagerCompat.sendStateEventToTest(
+ baseContext,
+ OVERVIEW_SPLIT_SELECT_ORDINAL,
+ )
}
}
private fun getStateName(state: RecentsState?): String {
return when (state) {
null -> "NULL"
- DEFAULT -> "default"
+ DEFAULT -> "DEFAULT"
MODAL_TASK -> "MODAL_TASK"
BACKGROUND_APP -> "BACKGROUND_APP"
HOME -> "HOME"
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowSwipeHandler.java b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowSwipeHandler.java
index afc8879..973fb2f 100644
--- a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowSwipeHandler.java
@@ -49,6 +49,7 @@
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.animation.Interpolator;
+import android.window.TransitionInfo;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -66,7 +67,6 @@
import com.android.quickstep.AbsSwipeUpHandler;
import com.android.quickstep.GestureState;
import com.android.quickstep.RecentsAnimationController;
-import com.android.quickstep.RecentsAnimationDeviceState;
import com.android.quickstep.RecentsAnimationTargets;
import com.android.quickstep.TaskAnimationManager;
import com.android.quickstep.fallback.FallbackRecentsView;
@@ -110,11 +110,10 @@
private boolean mAppCanEnterPip;
- public RecentsWindowSwipeHandler(Context context, RecentsAnimationDeviceState deviceState,
- TaskAnimationManager taskAnimationManager, GestureState gestureState, long touchTimeMs,
- boolean continuingLastGesture, InputConsumerController inputConsumer,
- MSDLPlayerWrapper msdlPlayerWrapper) {
- super(context, deviceState, taskAnimationManager, gestureState, touchTimeMs,
+ public RecentsWindowSwipeHandler(Context context, TaskAnimationManager taskAnimationManager,
+ GestureState gestureState, long touchTimeMs, boolean continuingLastGesture,
+ InputConsumerController inputConsumer, MSDLPlayerWrapper msdlPlayerWrapper) {
+ super(context, taskAnimationManager, gestureState, touchTimeMs,
continuingLastGesture, inputConsumer, msdlPlayerWrapper);
mRecentsDisplayModel = RecentsDisplayModel.getINSTANCE().get(context);
@@ -126,8 +125,8 @@
@Override
public void onRecentsAnimationStart(RecentsAnimationController controller,
- RecentsAnimationTargets targets) {
- super.onRecentsAnimationStart(controller, targets);
+ RecentsAnimationTargets targets, TransitionInfo transitionInfo) {
+ super.onRecentsAnimationStart(controller, targets, transitionInfo);
initTransformParams();
}
@@ -257,8 +256,7 @@
if (mRunningOverHome) {
if (DisplayController.getNavigationMode(mContext).hasGestures) {
mRecentsView.onGestureAnimationStartOnHome(
- mGestureState.getRunningTask().getPlaceholderTasks(),
- mDeviceState.getRotationTouchHelper());
+ mGestureState.getRunningTask().getPlaceholderTasks());
}
} else {
super.notifyGestureAnimationStartToRecents();
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
index ec6efcb..4e5d037 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
@@ -29,9 +29,9 @@
import android.view.ViewConfiguration;
import com.android.launcher3.R;
-import com.android.quickstep.GestureState;
import com.android.quickstep.InputConsumer;
import com.android.quickstep.RecentsAnimationDeviceState;
+import com.android.quickstep.RotationTouchHelper;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.MotionPauseDetector;
import com.android.systemui.shared.system.InputMonitorCompat;
@@ -47,7 +47,7 @@
private final VelocityTracker mVelocityTracker;
private final MotionPauseDetector mMotionPauseDetector;
private final RecentsAnimationDeviceState mDeviceState;
- private final GestureState mGestureState;
+ private final RotationTouchHelper mRotationHelper;
private final float mMinGestureDistance;
private final float mMinFlingVelocity;
@@ -57,7 +57,7 @@
private float mTotalY;
public AccessibilityInputConsumer(Context context, RecentsAnimationDeviceState deviceState,
- GestureState gestureState, InputConsumer delegate, InputMonitorCompat inputMonitor) {
+ InputConsumer delegate, InputMonitorCompat inputMonitor) {
super(delegate, inputMonitor);
mContext = context;
mVelocityTracker = VelocityTracker.obtain();
@@ -65,7 +65,7 @@
.getDimension(R.dimen.accessibility_gesture_min_swipe_distance);
mMinFlingVelocity = ViewConfiguration.get(context).getScaledMinimumFlingVelocity();
mDeviceState = deviceState;
- mGestureState = gestureState;
+ mRotationHelper = RotationTouchHelper.INSTANCE.get(context);
mMotionPauseDetector = new MotionPauseDetector(context);
}
@@ -102,8 +102,8 @@
case ACTION_POINTER_DOWN: {
if (mState == STATE_INACTIVE) {
int pointerIndex = ev.getActionIndex();
- if (mDeviceState.getRotationTouchHelper().isInSwipeUpTouchRegion(ev,
- pointerIndex) && mDelegate.allowInterceptByParent()) {
+ if (mRotationHelper.isInSwipeUpTouchRegion(ev, pointerIndex)
+ && mDelegate.allowInterceptByParent()) {
setActive(ev);
mActivePointerId = ev.getPointerId(pointerIndex);
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/BubbleBarInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/BubbleBarInputConsumer.java
index 6b61298..b2e7015 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/BubbleBarInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/BubbleBarInputConsumer.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.graphics.PointF;
+import android.util.Log;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
@@ -38,8 +39,11 @@
/**
* Listens for touch events on the bubble bar.
*/
+// TODO(b/385928447): remove debug logs with Log.d
public class BubbleBarInputConsumer implements InputConsumer {
+ private static final String TAG = "BubbleBarInputConsumer";
+
private final BubbleStashController mBubbleStashController;
private final BubbleBarViewController mBubbleBarViewController;
@Nullable
@@ -53,7 +57,8 @@
private final int mTouchSlop;
private final PointF mDownPos = new PointF();
private final PointF mLastPos = new PointF();
- private final long mTimeForTap;
+ private long mDownTime;
+ private final long mTimeForLongPress;
private int mActivePointerId = INVALID_POINTER_ID;
public BubbleBarInputConsumer(Context context, BubbleControllers bubbleControllers,
@@ -64,7 +69,7 @@
mInputMonitorCompat = inputMonitorCompat;
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
- mTimeForTap = ViewConfiguration.getTapTimeout();
+ mTimeForLongPress = ViewConfiguration.getLongPressTimeout();
}
@Override
@@ -77,10 +82,14 @@
final int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
+ mDownTime = System.currentTimeMillis();
mActivePointerId = ev.getPointerId(0);
mDownPos.set(ev.getX(), ev.getY());
mLastPos.set(mDownPos);
mStashedOrCollapsedOnDown = mBubbleStashController.isStashed() || isCollapsed();
+ Log.d(TAG,
+ "ACTION_DOWN stashedOrCollapsed=" + mStashedOrCollapsedOnDown + " downPos="
+ + mDownPos);
if (mBubbleBarSwipeController != null) {
mBubbleBarSwipeController.start();
}
@@ -88,6 +97,7 @@
case MotionEvent.ACTION_MOVE:
int pointerIndex = ev.findPointerIndex(mActivePointerId);
if (pointerIndex == INVALID_POINTER_ID) {
+ Log.d(TAG, "ACTION_MOVE skip, invalid pointer id");
break;
}
mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
@@ -96,10 +106,14 @@
float dY = mLastPos.y - mDownPos.y;
if (!mPassedTouchSlop) {
mPassedTouchSlop = Math.abs(dY) > mTouchSlop || Math.abs(dX) > mTouchSlop;
+ if (mPassedTouchSlop) {
+ Log.d(TAG, "ACTION_MOVE passed touch slop pos=" + mLastPos);
+ }
}
if (mBubbleBarSwipeController != null) {
mBubbleBarSwipeController.swipeTo(dY);
if (!mPilfered && mBubbleBarSwipeController.isSwipeGesture()) {
+ Log.d(TAG, "ACTION_MOVE swipe gesture, pilfering");
mPilfered = true;
// Bubbles is handling the swipe so make sure no one else gets it.
TestLogging.recordEvent(TestProtocol.SEQUENCE_PILFER, "pilferPointers");
@@ -108,16 +122,28 @@
}
break;
case MotionEvent.ACTION_UP:
+ long tapTime = System.currentTimeMillis() - mDownTime;
boolean swipeUpOnBubbleHandle = mBubbleBarSwipeController != null
&& mBubbleBarSwipeController.isSwipeGesture();
- boolean isWithinTapTime = ev.getEventTime() - ev.getDownTime() <= mTimeForTap;
+ // Anything less than a long-press is a tap
+ boolean isWithinTapTime = tapTime <= mTimeForLongPress;
+ Log.d(TAG, "ACTION_UP swipeUp=" + swipeUpOnBubbleHandle + " isInTapTime="
+ + isWithinTapTime + " tapTime=" + tapTime + " passedTouchSlop="
+ + mPassedTouchSlop + " stashedOrCollapsedOnDown="
+ + mStashedOrCollapsedOnDown);
if (isWithinTapTime && !swipeUpOnBubbleHandle && !mPassedTouchSlop
&& mStashedOrCollapsedOnDown) {
+ Log.d(TAG, "ACTION_UP showing bubble bar");
// Taps on the handle / collapsed state should open the bar
mBubbleStashController.showBubbleBar(
/* expandBubbles= */ true, /* bubbleBarGesture= */ true);
+ } else {
+ Log.d(TAG, "ACTION_UP nothing to do");
}
break;
+ case MotionEvent.ACTION_CANCEL:
+ Log.d(TAG, "ACTION_CANCEL");
+ break;
}
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
cleanupAfterMotionEvent();
@@ -125,11 +151,13 @@
}
private void cleanupAfterMotionEvent() {
+ Log.d(TAG, "cleaning up passedSlop=" + mPassedTouchSlop + " pilfered=" + mPilfered);
if (mBubbleBarSwipeController != null) {
mBubbleBarSwipeController.finish();
}
mPassedTouchSlop = false;
mPilfered = false;
+ mDownTime = 0;
}
private boolean isCollapsed() {
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index b66d4cb..503b900 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -37,6 +37,7 @@
import android.view.MotionEvent;
import android.view.RemoteAnimationTarget;
import android.view.VelocityTracker;
+import android.window.TransitionInfo;
import com.android.app.animation.Interpolators;
import com.android.launcher3.R;
@@ -52,6 +53,7 @@
import com.android.quickstep.RecentsAnimationDeviceState;
import com.android.quickstep.RecentsAnimationTargets;
import com.android.quickstep.RemoteAnimationTargets;
+import com.android.quickstep.RotationTouchHelper;
import com.android.quickstep.TaskAnimationManager;
import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
import com.android.quickstep.util.TransformParams;
@@ -82,7 +84,7 @@
getFlagForIndex(1, "STATE_HANDLER_INVALIDATED");
private final Context mContext;
- private final RecentsAnimationDeviceState mDeviceState;
+ private final RotationTouchHelper mRotationTouchHelper;
private final TaskAnimationManager mTaskAnimationManager;
private final GestureState mGestureState;
private final float mTouchSlopSquared;
@@ -110,14 +112,14 @@
TaskAnimationManager taskAnimationManager, GestureState gestureState,
InputMonitorCompat inputMonitorCompat) {
mContext = context;
- mDeviceState = deviceState;
mTaskAnimationManager = taskAnimationManager;
mGestureState = gestureState;
- mTouchSlopSquared = mDeviceState.getSquaredTouchSlop();
+ mTouchSlopSquared = deviceState.getSquaredTouchSlop();
mTransformParams = new TransformParams();
mInputMonitorCompat = inputMonitorCompat;
mMaxTranslationY = context.getResources().getDimensionPixelSize(
R.dimen.device_locked_y_offset);
+ mRotationTouchHelper = RotationTouchHelper.INSTANCE.get(mContext);
// Do not use DeviceProfile as the user data might be locked
mDisplaySize = DisplayController.INSTANCE.get(context).getInfo().currentSize;
@@ -152,7 +154,7 @@
if (!mThresholdCrossed) {
// Cancel interaction in case of multi-touch interaction
int ptrIdx = ev.getActionIndex();
- if (!mDeviceState.getRotationTouchHelper().isInSwipeUpTouchRegion(ev, ptrIdx)) {
+ if (!mRotationTouchHelper.isInSwipeUpTouchRegion(ev, ptrIdx)) {
int action = ev.getAction();
ev.setAction(ACTION_CANCEL);
finishTouchTracking(ev);
@@ -248,7 +250,7 @@
@Override
public void onRecentsAnimationStart(RecentsAnimationController controller,
- RecentsAnimationTargets targets) {
+ RecentsAnimationTargets targets, TransitionInfo transitionInfo) {
mRecentsAnimationController = controller;
mTransformParams.setTargetSet(targets);
applyTransform();
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
index f5bef05e..afe988d 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
@@ -242,7 +242,7 @@
private void cancelLongPress(String reason) {
if (DEBUG_NAV_HANDLE) {
- Log.d(TAG, "cancelLongPress");
+ Log.d(TAG, "cancelLongPress: " + reason);
}
mGestureState.setIsInExtendedSlopRegion(false);
MAIN_EXECUTOR.getHandler().removeCallbacks(mTriggerLongPress);
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index c4198db..dd2b2be 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -38,6 +38,7 @@
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
+import android.window.TransitionInfo;
import androidx.annotation.UiThread;
@@ -156,8 +157,7 @@
mPassedPilferInputSlop = mPassedWindowMoveSlop = continuingPreviousGesture;
mStartDisplacement = continuingPreviousGesture ? 0 : -mTouchSlop;
mDisableHorizontalSwipe = !mPassedPilferInputSlop && disableHorizontalSwipe;
- mRotationTouchHelper = mDeviceState.getRotationTouchHelper();
-
+ mRotationTouchHelper = RotationTouchHelper.INSTANCE.get(this);
}
@Override
@@ -294,15 +294,15 @@
float upDist = -displacement;
boolean isTrackpadGesture = mGestureState.isTrackpadGesture();
float squaredHypot = squaredHypot(displacementX, displacementY);
- boolean isInExtendedSlopRegion = !mGestureState.isInExtendedSlopRegion();
+ boolean isInExtendedSlopRegion = mGestureState.isInExtendedSlopRegion();
boolean passedSlop = isTrackpadGesture
|| (squaredHypot >= mSquaredTouchSlop
- && isInExtendedSlopRegion);
+ && !isInExtendedSlopRegion);
if (DEBUG) {
Log.d(TAG, "ACTION_MOVE: passedSlop=" + passedSlop
+ " ( " + isTrackpadGesture
+ " || (" + squaredHypot + " >= " + mSquaredTouchSlop
- + " && " + isInExtendedSlopRegion + " ))");
+ + " && " + !isInExtendedSlopRegion + " ))");
}
if (!mPassedSlopOnThisGesture && passedSlop) {
@@ -574,8 +574,9 @@
private static class FinishImmediatelyHandler
implements RecentsAnimationCallbacks.RecentsAnimationListener {
+ @Override
public void onRecentsAnimationStart(RecentsAnimationController controller,
- RecentsAnimationTargets targets) {
+ RecentsAnimationTargets targets, TransitionInfo transitionInfo) {
if (DEBUG) {
Log.d(TAG, "FinishImmediatelyHandler: queuing callback");
}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java
index 6dcb7bc..c91bebe 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java
@@ -28,6 +28,7 @@
import android.content.Intent;
import android.graphics.Point;
import android.view.MotionEvent;
+import android.window.TransitionInfo;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimatorListeners;
@@ -172,7 +173,7 @@
@Override
public void onRecentsAnimationStart(RecentsAnimationController controller,
- RecentsAnimationTargets targets) {
+ RecentsAnimationTargets targets, TransitionInfo transitionInfo) {
mRecentsAnimationController = controller;
mStateCallback.setState(STATE_TARGET_RECEIVED);
}
diff --git a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
index 4995e77..c986b88 100644
--- a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
@@ -73,6 +73,7 @@
import com.android.quickstep.OverviewComponentObserver;
import com.android.quickstep.OverviewComponentObserver.OverviewChangeListener;
import com.android.quickstep.TouchInteractionService.TISBinder;
+import com.android.quickstep.util.ActivityPreloadUtil;
import com.android.quickstep.util.LottieAnimationColorUtils;
import com.android.quickstep.util.TISBindHelper;
@@ -202,6 +203,7 @@
OverviewComponentObserver.INSTANCE.get(this)
.addOverviewChangeListener(mOverviewChangeListener);
+ ActivityPreloadUtil.preloadOverviewForSUWAllSet(this);
}
private InvariantDeviceProfile getIDP() {
@@ -291,7 +293,6 @@
private void onTISConnected(TISBinder binder) {
setSetupUIVisible(isResumed());
binder.setSwipeUpProxy(isResumed() ? this::createSwipeUpProxy : null);
- binder.preloadOverviewForSUWAllSet();
TaskbarManager taskbarManager = binder.getTaskbarManager();
if (taskbarManager != null) {
mLauncherStartAnim = taskbarManager.createLauncherStartFromSuwAnim(MAX_SWIPE_DURATION);
@@ -299,10 +300,7 @@
}
private void onOverviewTargetChange(boolean isHomeAndOverviewSame) {
- TISBinder binder = mTISBindHelper.getBinder();
- if (binder != null) {
- binder.preloadOverviewForSUWAllSet();
- }
+ ActivityPreloadUtil.preloadOverviewForSUWAllSet(this);
}
@Override
diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index 1c4e7a7..e265e61 100644
--- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
@@ -50,7 +50,6 @@
import com.android.launcher3.anim.PendingAnimation;
import com.android.quickstep.GestureState;
import com.android.quickstep.OverviewComponentObserver;
-import com.android.quickstep.RecentsAnimationDeviceState;
import com.android.quickstep.RemoteTargetGluer;
import com.android.quickstep.SwipeUpAnimationLogic;
import com.android.quickstep.SwipeUpAnimationLogic.RunningWindowAnim;
@@ -85,10 +84,8 @@
SwipeUpGestureTutorialController(TutorialFragment tutorialFragment, TutorialType tutorialType) {
super(tutorialFragment, tutorialType);
- RecentsAnimationDeviceState deviceState = new RecentsAnimationDeviceState(mContext);
- mTaskViewSwipeUpAnimation = new ViewSwipeUpAnimation(mContext, deviceState,
+ mTaskViewSwipeUpAnimation = new ViewSwipeUpAnimation(mContext,
new GestureState(OverviewComponentObserver.INSTANCE.get(mContext), -1));
- deviceState.destroy();
DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext)
.getDeviceProfile(mContext)
@@ -311,9 +308,8 @@
class ViewSwipeUpAnimation extends SwipeUpAnimationLogic {
- ViewSwipeUpAnimation(Context context, RecentsAnimationDeviceState deviceState,
- GestureState gestureState) {
- super(context, deviceState, gestureState);
+ ViewSwipeUpAnimation(Context context, GestureState gestureState) {
+ super(context, gestureState);
mRemoteTargetHandles[0] = new RemoteTargetGluer.RemoteTargetHandle(
mRemoteTargetHandles[0].getTaskViewSimulator(), new FakeTransformParams());
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
index 7d14a3e..0fc95e2 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
@@ -80,8 +80,8 @@
private static final CharSequence DEFAULT_PIXEL_TIPS_APP_NAME = "Pixel Tips";
private static final int FEEDBACK_ANIMATION_MS = 133;
- private static final int RIPPLE_VISIBLE_MS = 300;
- private static final int GESTURE_ANIMATION_DELAY_MS = 1500;
+ private static final int SUBTITLE_ANNOUNCE_DELAY_MS = 3000;
+ private static final int DONE_BUTTON_ANNOUNCE_DELAY_MS = 4000;
private static final int ADVANCE_TUTORIAL_TIMEOUT_MS = 3000;
private static final long GESTURE_ANIMATION_PAUSE_DURATION_MILLIS = 1000;
protected float mExitingAppEndingCornerRadius;
@@ -124,10 +124,12 @@
// These runnables should be used when posting callbacks to their views and cleared from their
// views before posting new callbacks.
private final Runnable mTitleViewCallback;
+ private final Runnable mSubtitleViewCallback;
@Nullable private Runnable mFeedbackViewCallback;
@Nullable private Runnable mFakeTaskViewCallback;
@Nullable private Runnable mFakeTaskbarViewCallback;
private final Runnable mShowFeedbackRunnable;
+ private final AccessibilityManager mAccessibilityManager;
TutorialController(TutorialFragment tutorialFragment, TutorialType tutorialType) {
mTutorialFragment = tutorialFragment;
@@ -175,6 +177,7 @@
mFeedbackTitleView.setText(getIntroductionTitle());
mFeedbackSubtitleView.setText(getIntroductionSubtitle());
+
mExitingAppView.setClipToOutline(true);
mExitingAppView.setOutlineProvider(new ViewOutlineProvider() {
@Override
@@ -183,8 +186,16 @@
}
});
- mTitleViewCallback = () -> mFeedbackTitleView.sendAccessibilityEvent(
- AccessibilityEvent.TYPE_VIEW_FOCUSED);
+ mAccessibilityManager = AccessibilityManager.getInstance(mContext);
+ mTitleViewCallback = () -> {
+ mFeedbackTitleView.requestFocus();
+ mFeedbackTitleView.sendAccessibilityEvent(
+ AccessibilityEvent.TYPE_VIEW_FOCUSED);
+ };
+ mSubtitleViewCallback = () -> {
+ mFeedbackSubtitleView.requestFocus();
+ mFeedbackSubtitleView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+ };
mShowFeedbackRunnable = () -> {
mFeedbackView.setAlpha(0f);
mFeedbackView.setScaleX(0.95f);
@@ -203,10 +214,10 @@
mFeedbackViewCallback = mTutorialFragment::continueTutorial;
mFeedbackView.postDelayed(
mFeedbackViewCallback,
- AccessibilityManager.getInstance(mContext)
- .getRecommendedTimeoutMillis(
- ADVANCE_TUTORIAL_TIMEOUT_MS,
- AccessibilityManager.FLAG_CONTENT_TEXT));
+ mAccessibilityManager.getRecommendedTimeoutMillis(
+ ADVANCE_TUTORIAL_TIMEOUT_MS,
+ AccessibilityManager.FLAG_CONTENT_TEXT
+ | AccessibilityManager.FLAG_CONTENT_CONTROLS));
}
})
.start();
@@ -404,6 +415,7 @@
int subtitleResId,
boolean isGestureSuccessful) {
mFeedbackTitleView.removeCallbacks(mTitleViewCallback);
+ mFeedbackSubtitleView.removeCallbacks(mSubtitleViewCallback);
if (mFeedbackViewCallback != null) {
mFeedbackView.removeCallbacks(mFeedbackViewCallback);
mFeedbackViewCallback = null;
@@ -411,6 +423,15 @@
mFeedbackTitleView.setText(titleResId);
mFeedbackSubtitleView.setText(subtitleResId);
+ mFeedbackTitleView.postDelayed(mTitleViewCallback, mAccessibilityManager
+ .getRecommendedTimeoutMillis(
+ FEEDBACK_ANIMATION_MS,
+ AccessibilityManager.FLAG_CONTENT_TEXT));
+ mFeedbackSubtitleView.postDelayed(mSubtitleViewCallback, mAccessibilityManager
+ .getRecommendedTimeoutMillis(
+ SUBTITLE_ANNOUNCE_DELAY_MS,
+ AccessibilityManager.FLAG_CONTENT_TEXT));
+
if (isGestureSuccessful) {
if (mTutorialFragment.isAtFinalStep()) {
showActionButton();
@@ -467,6 +488,7 @@
mFakeTaskbarViewCallback = null;
}
mFeedbackTitleView.removeCallbacks(mTitleViewCallback);
+ mFeedbackSubtitleView.removeCallbacks(mSubtitleViewCallback);
}
private void playFeedbackAnimation() {
@@ -542,6 +564,13 @@
mSkipButton.setVisibility(GONE);
mDoneButton.setVisibility(View.VISIBLE);
mDoneButton.setOnClickListener(this::onActionButtonClicked);
+ mDoneButton.postDelayed(() -> {
+ mDoneButton.requestFocus();
+ mDoneButton.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
+ }, mAccessibilityManager
+ .getRecommendedTimeoutMillis(
+ DONE_BUTTON_ANNOUNCE_DELAY_MS,
+ AccessibilityManager.FLAG_CONTENT_CONTROLS));
}
void hideFakeTaskbar(boolean animateToHotseat) {
diff --git a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
index dd721e1..946ca2a 100644
--- a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
+++ b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
@@ -16,9 +16,10 @@
package com.android.quickstep.logging;
-import static com.android.launcher3.LauncherPrefs.THEMED_ICONS;
import static com.android.launcher3.LauncherPrefs.getDevicePrefs;
import static com.android.launcher3.LauncherPrefs.getPrefs;
+import static com.android.launcher3.graphics.ThemeManager.KEY_THEMED_ICONS;
+import static com.android.launcher3.graphics.ThemeManager.THEMED_ICONS;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_SCREEN_SUGGESTIONS_DISABLED;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_SCREEN_SUGGESTIONS_ENABLED;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NOTIFICATION_DOT_DISABLED;
@@ -29,7 +30,6 @@
import static com.android.launcher3.model.PredictionUpdateTask.LAST_PREDICTION_ENABLED;
import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
import static com.android.launcher3.util.SettingsCache.NOTIFICATION_BADGING_URI;
-import static com.android.launcher3.util.Themes.KEY_THEMED_ICONS;
import android.content.Context;
import android.content.SharedPreferences;
diff --git a/quickstep/src/com/android/quickstep/recents/data/HighResLoadingStateNotifier.kt b/quickstep/src/com/android/quickstep/recents/data/HighResLoadingStateNotifier.kt
index df546ca..ad2bd25 100644
--- a/quickstep/src/com/android/quickstep/recents/data/HighResLoadingStateNotifier.kt
+++ b/quickstep/src/com/android/quickstep/recents/data/HighResLoadingStateNotifier.kt
@@ -16,7 +16,7 @@
package com.android.quickstep.recents.data
-import com.android.quickstep.TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback
+import com.android.quickstep.HighResLoadingState.HighResLoadingStateChangedCallback
/** Notifies added callbacks that high res state has changed */
interface HighResLoadingStateNotifier {
diff --git a/quickstep/src/com/android/quickstep/recents/data/RecentsDeviceProfile.kt b/quickstep/src/com/android/quickstep/recents/data/RecentsDeviceProfile.kt
index d2cb595..0ee2bd2 100644
--- a/quickstep/src/com/android/quickstep/recents/data/RecentsDeviceProfile.kt
+++ b/quickstep/src/com/android/quickstep/recents/data/RecentsDeviceProfile.kt
@@ -20,7 +20,6 @@
* Container to hold [com.android.launcher3.DeviceProfile] related to Recents.
*
* @property isLargeScreen whether the current device posture has a large screen
+ * @property canEnterDesktopMode whether the current device can enter Desktop UI mode
*/
-data class RecentsDeviceProfile(
- val isLargeScreen: Boolean,
-)
+data class RecentsDeviceProfile(val isLargeScreen: Boolean, val canEnterDesktopMode: Boolean)
diff --git a/quickstep/src/com/android/quickstep/recents/data/RecentsDeviceProfileRepositoryImpl.kt b/quickstep/src/com/android/quickstep/recents/data/RecentsDeviceProfileRepositoryImpl.kt
index c64453d..8450f09 100644
--- a/quickstep/src/com/android/quickstep/recents/data/RecentsDeviceProfileRepositoryImpl.kt
+++ b/quickstep/src/com/android/quickstep/recents/data/RecentsDeviceProfileRepositoryImpl.kt
@@ -17,6 +17,7 @@
package com.android.quickstep.recents.data
import com.android.quickstep.views.RecentsViewContainer
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
/**
* Repository for shrink down version of [com.android.launcher3.DeviceProfile] that only contains
@@ -26,5 +27,10 @@
RecentsDeviceProfileRepository {
override fun getRecentsDeviceProfile() =
- with(container.deviceProfile) { RecentsDeviceProfile(isLargeScreen = isTablet) }
+ with(container.deviceProfile) {
+ RecentsDeviceProfile(
+ isLargeScreen = isTablet,
+ canEnterDesktopMode = DesktopModeStatus.canEnterDesktopMode(container.asContext()),
+ )
+ }
}
diff --git a/quickstep/src/com/android/quickstep/recents/data/TaskVisualsChangedDelegate.kt b/quickstep/src/com/android/quickstep/recents/data/TaskVisualsChangedDelegate.kt
index a45d194..608fafd 100644
--- a/quickstep/src/com/android/quickstep/recents/data/TaskVisualsChangedDelegate.kt
+++ b/quickstep/src/com/android/quickstep/recents/data/TaskVisualsChangedDelegate.kt
@@ -17,7 +17,7 @@
package com.android.quickstep.recents.data
import android.os.UserHandle
-import com.android.quickstep.TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback
+import com.android.quickstep.HighResLoadingState.HighResLoadingStateChangedCallback
import com.android.quickstep.recents.data.TaskVisualsChangedDelegate.TaskIconChangedCallback
import com.android.quickstep.recents.data.TaskVisualsChangedDelegate.TaskThumbnailChangedCallback
import com.android.quickstep.util.TaskVisualsChangeListener
diff --git a/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt b/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
index 8a1b211..703d631 100644
--- a/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
+++ b/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
@@ -17,6 +17,7 @@
package com.android.quickstep.recents.data
import android.graphics.drawable.Drawable
+import android.graphics.drawable.ShapeDrawable
import android.util.Log
import com.android.launcher3.util.coroutines.DispatcherProvider
import com.android.quickstep.recents.data.TaskVisualsChangedDelegate.TaskIconChangedCallback
@@ -25,16 +26,16 @@
import com.android.quickstep.task.thumbnail.data.TaskThumbnailDataSource
import com.android.systemui.shared.recents.model.Task
import com.android.systemui.shared.recents.model.ThumbnailData
-import kotlin.coroutines.resume
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
+import kotlinx.coroutines.async
+import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.distinctUntilChangedBy
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
-import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext
class TasksRepository(
@@ -112,10 +113,11 @@
taskRequests[taskId] =
Pair(
task.key,
- recentsCoroutineScope.launch(dispatcherProvider.main) {
+ recentsCoroutineScope.launch(dispatcherProvider.background) {
Log.i(TAG, "requestTaskData: $taskId")
- fetchIcon(task)
- fetchThumbnail(task)
+ val thumbnailFetchDeferred = async { fetchThumbnail(task) }
+ val iconFetchDeferred = async { fetchIcon(task) }
+ awaitAll(thumbnailFetchDeferred, iconFetchDeferred)
},
)
}
@@ -150,7 +152,7 @@
task.key,
object : TaskIconChangedCallback {
override fun onTaskIconChanged() {
- recentsCoroutineScope.launch(dispatcherProvider.main) {
+ recentsCoroutineScope.launch(dispatcherProvider.background) {
updateIcon(task.key.id, getIconFromDataSource(task))
}
}
@@ -168,7 +170,7 @@
}
override fun onHighResLoadingStateChanged() {
- recentsCoroutineScope.launch(dispatcherProvider.main) {
+ recentsCoroutineScope.launch(dispatcherProvider.background) {
updateThumbnail(task.key.id, getThumbnailFromDataSource(task))
}
}
@@ -191,34 +193,18 @@
}
private suspend fun getThumbnailFromDataSource(task: Task) =
- withContext(dispatcherProvider.main) {
- suspendCancellableCoroutine { continuation ->
- val cancellableTask =
- taskThumbnailDataSource.getThumbnailInBackground(task) {
- continuation.resume(it)
- }
- continuation.invokeOnCancellation { cancellableTask?.cancel() }
- }
- }
+ withContext(dispatcherProvider.background) { taskThumbnailDataSource.getThumbnail(task) }
private suspend fun getIconFromDataSource(task: Task) =
- withContext(dispatcherProvider.main) {
- suspendCancellableCoroutine { continuation ->
- val cancellableTask =
- taskIconDataSource.getIconInBackground(task) { icon, contentDescription, title
- ->
- icon.constantState?.let {
- continuation.resume(
- IconData(it.newDrawable().mutate(), contentDescription, title)
- )
- }
- }
- continuation.invokeOnCancellation { cancellableTask?.cancel() }
- }
+ withContext(dispatcherProvider.background) {
+ val iconCacheEntry = taskIconDataSource.getIcon(task)
+ val icon = iconCacheEntry.icon.constantState?.newDrawable()?.mutate() ?: EMPTY_DRAWABLE
+ IconData(icon, iconCacheEntry.contentDescription, iconCacheEntry.title)
}
companion object {
private const val TAG = "TasksRepository"
+ private val EMPTY_DRAWABLE = ShapeDrawable()
}
/** Helper class to support StateFlow emissions when using a Map with a MutableStateFlow. */
diff --git a/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt b/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
index 9d8fc4f..2b364f9 100644
--- a/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
+++ b/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
@@ -182,6 +182,7 @@
dispatcherProvider = inject(),
getThumbnailPositionUseCase = inject(),
tasksRepository = inject(),
+ deviceProfileRepository = inject(),
splashAlphaUseCase = inject(scopeId),
)
TaskOverlayViewModel::class.java -> {
@@ -191,6 +192,7 @@
recentsViewData = inject(),
recentTasksRepository = inject(),
getThumbnailPositionUseCase = inject(),
+ dispatcherProvider = inject(),
)
}
GetThumbnailUseCase::class.java -> GetThumbnailUseCase(taskRepository = inject())
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailUiState.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailUiState.kt
index 36a86f2..6118544 100644
--- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailUiState.kt
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailUiState.kt
@@ -24,18 +24,35 @@
sealed class TaskThumbnailUiState {
data object Uninitialized : TaskThumbnailUiState()
- data object LiveTile : TaskThumbnailUiState()
-
data class BackgroundOnly(@ColorInt val backgroundColor: Int) : TaskThumbnailUiState()
- data class SnapshotSplash(
- val snapshot: Snapshot,
- val splash: Drawable?,
- ) : TaskThumbnailUiState()
+ data class SnapshotSplash(val snapshot: Snapshot, val splash: Drawable?) :
+ TaskThumbnailUiState()
- data class Snapshot(
- val bitmap: Bitmap,
- @Surface.Rotation val thumbnailRotation: Int,
- @ColorInt val backgroundColor: Int
- )
+ sealed class LiveTile : TaskThumbnailUiState() {
+ data class WithHeader(val header: ThumbnailHeader) : LiveTile()
+
+ data object WithoutHeader : LiveTile()
+ }
+
+ sealed class Snapshot {
+ abstract val bitmap: Bitmap
+ abstract val thumbnailRotation: Int
+ abstract val backgroundColor: Int
+
+ data class WithHeader(
+ override val bitmap: Bitmap,
+ @Surface.Rotation override val thumbnailRotation: Int,
+ @ColorInt override val backgroundColor: Int,
+ val header: ThumbnailHeader,
+ ) : Snapshot()
+
+ data class WithoutHeader(
+ override val bitmap: Bitmap,
+ @Surface.Rotation override val thumbnailRotation: Int,
+ @ColorInt override val backgroundColor: Int,
+ ) : Snapshot()
+ }
+
+ data class ThumbnailHeader(val icon: Drawable, val title: String)
}
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
index a952617..639d3a7 100644
--- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
@@ -22,11 +22,13 @@
import android.graphics.Rect
import android.util.AttributeSet
import android.util.Log
+import android.view.LayoutInflater
import android.view.View
import android.view.ViewOutlineProvider
import android.widget.FrameLayout
import androidx.annotation.ColorInt
import androidx.core.view.isInvisible
+import com.android.launcher3.Flags.enableDesktopExplodedView
import com.android.launcher3.R
import com.android.launcher3.util.ViewPool
import com.android.launcher3.util.coroutines.DispatcherProvider
@@ -39,11 +41,14 @@
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Uninitialized
import com.android.quickstep.task.viewmodel.TaskThumbnailViewModel
import com.android.quickstep.views.FixedSizeImageView
+import com.android.quickstep.views.TaskThumbnailViewHeader
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
+import kotlinx.coroutines.flow.dropWhile
+import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
@@ -66,6 +71,8 @@
private val splashBackground: View by lazy { findViewById(R.id.splash_background) }
private val splashIcon: FixedSizeImageView by lazy { findViewById(R.id.splash_icon) }
+ private var taskThumbnailViewHeader: TaskThumbnailViewHeader? = null
+
private var uiState: TaskThumbnailUiState = Uninitialized
private val bounds = Rect()
@@ -86,30 +93,44 @@
defStyleAttr: Int,
) : super(context, attrs, defStyleAttr)
+ override fun onFinishInflate() {
+ super.onFinishInflate()
+
+ maybeCreateHeader()
+ }
+
override fun onAttachedToWindow() {
super.onAttachedToWindow()
viewAttachedScope =
- CoroutineScope(SupervisorJob() + Dispatchers.Main + CoroutineName("TaskThumbnailView"))
+ CoroutineScope(
+ SupervisorJob() + Dispatchers.Main.immediate + CoroutineName("TaskThumbnailView")
+ )
viewData = RecentsDependencies.get(this)
updateViewDataValues()
viewModel = RecentsDependencies.get(this)
viewModel.uiState
+ .dropWhile { it == Uninitialized }
+ .flowOn(dispatcherProvider.background)
.onEach { viewModelUiState ->
Log.d(TAG, "viewModelUiState changed from: $uiState to: $viewModelUiState")
uiState = viewModelUiState
resetViews()
when (viewModelUiState) {
is Uninitialized -> {}
- is LiveTile -> drawLiveWindow()
+ is LiveTile -> drawLiveWindow(viewModelUiState)
is SnapshotSplash -> drawSnapshotSplash(viewModelUiState)
is BackgroundOnly -> drawBackground(viewModelUiState.backgroundColor)
}
}
.launchIn(viewAttachedScope)
viewModel.dimProgress
+ .dropWhile { it == 0f }
+ .flowOn(dispatcherProvider.background)
.onEach { dimProgress -> scrimView.alpha = dimProgress }
.launchIn(viewAttachedScope)
viewModel.splashAlpha
+ .dropWhile { it == 0f }
+ .flowOn(dispatcherProvider.background)
.onEach { splashAlpha ->
splashBackground.alpha = splashAlpha
splashIcon.alpha = splashAlpha
@@ -135,6 +156,7 @@
override fun onRecycle() {
uiState = Uninitialized
+ resetViews()
}
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
@@ -177,14 +199,20 @@
splashIcon.alpha = 0f
scrimView.alpha = 0f
setBackgroundColor(Color.BLACK)
+ taskThumbnailViewHeader?.isInvisible = true
}
private fun drawBackground(@ColorInt background: Int) {
setBackgroundColor(background)
}
- private fun drawLiveWindow() {
+ private fun drawLiveWindow(liveTile: LiveTile) {
liveTileView.isInvisible = false
+
+ if (liveTile is LiveTile.WithHeader) {
+ taskThumbnailViewHeader?.isInvisible = false
+ taskThumbnailViewHeader?.setHeader(liveTile.header)
+ }
}
private fun drawSnapshotSplash(snapshotSplash: SnapshotSplash) {
@@ -195,6 +223,11 @@
}
private fun drawSnapshot(snapshot: Snapshot) {
+ if (snapshot is Snapshot.WithHeader) {
+ taskThumbnailViewHeader?.isInvisible = false
+ taskThumbnailViewHeader?.setHeader(snapshot.header)
+ }
+
drawBackground(snapshot.backgroundColor)
thumbnailView.setImageBitmap(snapshot.bitmap)
thumbnailView.isInvisible = false
@@ -208,4 +241,14 @@
private companion object {
const val TAG = "TaskThumbnailView"
}
+
+ private fun maybeCreateHeader() {
+ if (enableDesktopExplodedView() && taskThumbnailViewHeader == null) {
+ taskThumbnailViewHeader =
+ LayoutInflater.from(context)
+ .inflate(R.layout.task_thumbnail_view_header, this, false)
+ as TaskThumbnailViewHeader
+ addView(taskThumbnailViewHeader)
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/data/TaskIconDataSource.kt b/quickstep/src/com/android/quickstep/task/thumbnail/data/TaskIconDataSource.kt
index ab699c6..c45458c 100644
--- a/quickstep/src/com/android/quickstep/task/thumbnail/data/TaskIconDataSource.kt
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/data/TaskIconDataSource.kt
@@ -16,10 +16,9 @@
package com.android.quickstep.task.thumbnail.data
-import com.android.launcher3.util.CancellableTask
-import com.android.quickstep.TaskIconCache.GetTaskIconCallback
+import com.android.quickstep.TaskIconCache
import com.android.systemui.shared.recents.model.Task
interface TaskIconDataSource {
- fun getIconInBackground(task: Task, callback: GetTaskIconCallback): CancellableTask<*>?
+ suspend fun getIcon(task: Task): TaskIconCache.TaskCacheEntry
}
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/data/TaskThumbnailDataSource.kt b/quickstep/src/com/android/quickstep/task/thumbnail/data/TaskThumbnailDataSource.kt
index 986acbe..6e63ea9 100644
--- a/quickstep/src/com/android/quickstep/task/thumbnail/data/TaskThumbnailDataSource.kt
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/data/TaskThumbnailDataSource.kt
@@ -16,14 +16,9 @@
package com.android.quickstep.task.thumbnail.data
-import com.android.launcher3.util.CancellableTask
import com.android.systemui.shared.recents.model.Task
import com.android.systemui.shared.recents.model.ThumbnailData
-import java.util.function.Consumer
interface TaskThumbnailDataSource {
- fun getThumbnailInBackground(
- task: Task,
- callback: Consumer<ThumbnailData>
- ): CancellableTask<ThumbnailData>?
+ suspend fun getThumbnail(task: Task): ThumbnailData?
}
diff --git a/quickstep/src/com/android/quickstep/task/util/TaskOverlayHelper.kt b/quickstep/src/com/android/quickstep/task/util/TaskOverlayHelper.kt
index e6c8d27..f51660b 100644
--- a/quickstep/src/com/android/quickstep/task/util/TaskOverlayHelper.kt
+++ b/quickstep/src/com/android/quickstep/task/util/TaskOverlayHelper.kt
@@ -32,6 +32,8 @@
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
+import kotlinx.coroutines.flow.dropWhile
+import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
@@ -67,15 +69,20 @@
fun init() {
overlayInitializedScope =
- CoroutineScope(SupervisorJob() + Dispatchers.Main + CoroutineName("TaskOverlayHelper"))
+ CoroutineScope(
+ SupervisorJob() + Dispatchers.Main.immediate + CoroutineName("TaskOverlayHelper")
+ )
viewModel =
TaskOverlayViewModel(
task = task,
recentsViewData = RecentsDependencies.get(),
getThumbnailPositionUseCase = RecentsDependencies.get(),
recentTasksRepository = RecentsDependencies.get(),
+ dispatcherProvider = RecentsDependencies.get(),
)
viewModel.overlayState
+ .dropWhile { it == Disabled }
+ .flowOn(dispatcherProvider.background)
.onEach {
uiState = it
if (it is Enabled) {
diff --git a/quickstep/src/com/android/quickstep/task/viewmodel/TaskOverlayViewModel.kt b/quickstep/src/com/android/quickstep/task/viewmodel/TaskOverlayViewModel.kt
index 14359db..81a904b 100644
--- a/quickstep/src/com/android/quickstep/task/viewmodel/TaskOverlayViewModel.kt
+++ b/quickstep/src/com/android/quickstep/task/viewmodel/TaskOverlayViewModel.kt
@@ -17,6 +17,7 @@
package com.android.quickstep.task.viewmodel
import android.graphics.Matrix
+import com.android.launcher3.util.coroutines.DispatcherProvider
import com.android.quickstep.recents.data.RecentTasksRepository
import com.android.quickstep.recents.usecase.GetThumbnailPositionUseCase
import com.android.quickstep.recents.usecase.ThumbnailPositionState.MatrixScaling
@@ -27,6 +28,7 @@
import com.android.systemui.shared.recents.model.Task
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
/** View model for TaskOverlay */
@@ -35,11 +37,14 @@
recentsViewData: RecentsViewData,
private val getThumbnailPositionUseCase: GetThumbnailPositionUseCase,
recentTasksRepository: RecentTasksRepository,
+ dispatcherProvider: DispatcherProvider,
) {
val overlayState =
combine(
recentsViewData.overlayEnabled,
- recentsViewData.settledFullyVisibleTaskIds.map { it.contains(task.key.id) },
+ recentsViewData.settledFullyVisibleTaskIds
+ .map { it.contains(task.key.id) }
+ .distinctUntilChanged(),
recentTasksRepository.getThumbnailById(task.key.id),
) { isOverlayEnabled, isFullyVisible, thumbnailData ->
if (isOverlayEnabled && isFullyVisible) {
@@ -52,6 +57,7 @@
}
}
.distinctUntilChanged()
+ .flowOn(dispatcherProvider.background)
fun getThumbnailPositionState(width: Int, height: Int, isRtl: Boolean): ThumbnailPositionState {
val matrix: Matrix
diff --git a/quickstep/src/com/android/quickstep/task/viewmodel/TaskThumbnailViewModelImpl.kt b/quickstep/src/com/android/quickstep/task/viewmodel/TaskThumbnailViewModelImpl.kt
index b5b2fc9..a154c3c 100644
--- a/quickstep/src/com/android/quickstep/task/viewmodel/TaskThumbnailViewModelImpl.kt
+++ b/quickstep/src/com/android/quickstep/task/viewmodel/TaskThumbnailViewModelImpl.kt
@@ -18,11 +18,14 @@
import android.annotation.ColorInt
import android.app.ActivityTaskManager.INVALID_TASK_ID
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.graphics.Matrix
import android.util.Log
import androidx.core.graphics.ColorUtils
+import com.android.launcher3.Flags.enableDesktopExplodedView
import com.android.launcher3.util.coroutines.DispatcherProvider
import com.android.quickstep.recents.data.RecentTasksRepository
+import com.android.quickstep.recents.data.RecentsDeviceProfileRepository
import com.android.quickstep.recents.usecase.GetThumbnailPositionUseCase
import com.android.quickstep.recents.usecase.ThumbnailPositionState
import com.android.quickstep.recents.viewmodel.RecentsViewData
@@ -32,6 +35,7 @@
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.LiveTile
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Snapshot
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.SnapshotSplash
+import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.ThumbnailHeader
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Uninitialized
import com.android.systemui.shared.recents.model.Task
import kotlin.math.max
@@ -51,6 +55,7 @@
taskContainerData: TaskContainerData,
dispatcherProvider: DispatcherProvider,
private val tasksRepository: RecentTasksRepository,
+ private val deviceProfileRepository: RecentsDeviceProfileRepository,
private val getThumbnailPositionUseCase: GetThumbnailPositionUseCase,
private val splashAlphaUseCase: SplashAlphaUseCase,
) : TaskThumbnailViewModel {
@@ -90,7 +95,7 @@
// )
when {
taskVal == null -> Uninitialized
- isRunning -> LiveTile
+ isRunning -> createLiveTileState(taskVal)
isBackgroundOnly(taskVal) ->
BackgroundOnly(taskVal.colorBackground.removeAlpha())
isSnapshotSplashState(taskVal) ->
@@ -129,7 +134,46 @@
private fun createSnapshotState(task: Task): Snapshot {
val thumbnailData = task.thumbnail
val bitmap = thumbnailData?.thumbnail!!
- return Snapshot(bitmap, thumbnailData.rotation, task.colorBackground.removeAlpha())
+ var thumbnailHeader = maybeCreateHeader(task)
+ return if (thumbnailHeader != null)
+ Snapshot.WithHeader(
+ bitmap,
+ thumbnailData.rotation,
+ task.colorBackground.removeAlpha(),
+ thumbnailHeader,
+ )
+ else
+ Snapshot.WithoutHeader(
+ bitmap,
+ thumbnailData.rotation,
+ task.colorBackground.removeAlpha(),
+ )
+ }
+
+ private fun shouldHaveThumbnailHeader(task: Task): Boolean {
+ return deviceProfileRepository.getRecentsDeviceProfile().canEnterDesktopMode &&
+ enableDesktopExplodedView() &&
+ task.key.windowingMode == WINDOWING_MODE_FREEFORM
+ }
+
+ private fun maybeCreateHeader(task: Task): ThumbnailHeader? {
+ // Header is only needed when this task is a desktop task and Overivew exploded view is
+ // enabled.
+ if (!shouldHaveThumbnailHeader(task)) {
+ return null
+ }
+
+ // TODO(http://b/353965691): figure out what to do when `icon` or `titleDescription` is
+ // null.
+ val icon = task.icon ?: return null
+ val titleDescription = task.titleDescription ?: return null
+ return ThumbnailHeader(icon, titleDescription)
+ }
+
+ private fun createLiveTileState(task: Task): LiveTile {
+ val thumbnailHeader = maybeCreateHeader(task)
+ return if (thumbnailHeader != null) LiveTile.WithHeader(thumbnailHeader)
+ else LiveTile.WithoutHeader
}
@ColorInt private fun Int.removeAlpha(): Int = ColorUtils.setAlphaComponent(this, 0xff)
diff --git a/quickstep/src/com/android/quickstep/util/ActiveTrackpadList.kt b/quickstep/src/com/android/quickstep/util/ActiveTrackpadList.kt
new file mode 100644
index 0000000..63bd03d
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/ActiveTrackpadList.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.util
+
+import android.content.Context
+import android.hardware.input.InputManager
+import android.view.InputDevice
+import com.android.launcher3.util.Executors
+import com.android.launcher3.util.IntSet
+
+/** Utility class to maintain a list of actively connected trackpad devices */
+class ActiveTrackpadList(ctx: Context, private val updateCallback: Runnable) :
+ IntSet(), InputManager.InputDeviceListener {
+
+ private val inputManager = ctx.getSystemService(InputManager::class.java)!!
+
+ init {
+ inputManager.registerInputDeviceListener(this, Executors.UI_HELPER_EXECUTOR.handler)
+ inputManager.inputDeviceIds.forEach { deviceId -> onInputDeviceAdded(deviceId) }
+ }
+
+ override fun onInputDeviceAdded(deviceId: Int) {
+ if (isTrackpadDevice(deviceId)) {
+ // This updates internal TIS state so it needs to also run on the main
+ // thread.
+ Executors.MAIN_EXECUTOR.execute {
+ val wasEmpty = isEmpty
+ add(deviceId)
+ if (wasEmpty) update()
+ }
+ }
+ }
+
+ override fun onInputDeviceChanged(deviceId: Int) {}
+
+ override fun onInputDeviceRemoved(deviceId: Int) {
+ // This updates internal TIS state so it needs to also run on the main thread.
+ Executors.MAIN_EXECUTOR.execute {
+ remove(deviceId)
+ if (isEmpty) update()
+ }
+ }
+
+ private fun update() {
+ updateCallback.run()
+ }
+
+ fun destroy() {
+ inputManager.unregisterInputDeviceListener(this)
+ clear()
+ }
+
+ /** This is a blocking binder call that should run on a bg thread. */
+ private fun isTrackpadDevice(deviceId: Int) =
+ inputManager.getInputDevice(deviceId)?.sources ==
+ (InputDevice.SOURCE_MOUSE or InputDevice.SOURCE_TOUCHPAD)
+}
diff --git a/quickstep/src/com/android/quickstep/util/ActivityPreloadUtil.kt b/quickstep/src/com/android/quickstep/util/ActivityPreloadUtil.kt
new file mode 100644
index 0000000..47b39db
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/ActivityPreloadUtil.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.util
+
+import android.content.Context
+import android.content.Intent
+import android.os.Trace
+import com.android.launcher3.provider.RestoreDbTask
+import com.android.launcher3.util.Executors
+import com.android.launcher3.util.LockedUserState
+import com.android.quickstep.OverviewComponentObserver
+import com.android.quickstep.RecentsAnimationDeviceState
+import com.android.systemui.shared.system.ActivityManagerWrapper
+
+/** Utility class for preloading overview */
+object ActivityPreloadUtil {
+
+ @JvmStatic
+ fun preloadOverviewForSUWAllSet(ctx: Context) {
+ preloadOverview(ctx, fromInit = false, forSUWAllSet = true)
+ }
+
+ @JvmStatic
+ fun preloadOverviewForTIS(ctx: Context, fromInit: Boolean) {
+ preloadOverview(ctx, fromInit = fromInit, forSUWAllSet = false)
+ }
+
+ private fun preloadOverview(ctx: Context, fromInit: Boolean, forSUWAllSet: Boolean) {
+ Trace.beginSection("preloadOverview(fromInit=$fromInit, forSUWAllSet=$forSUWAllSet)")
+
+ try {
+ if (!LockedUserState.get(ctx).isUserUnlocked) return
+
+ val deviceState = RecentsAnimationDeviceState.INSTANCE[ctx]
+ val overviewCompObserver = OverviewComponentObserver.INSTANCE[ctx]
+
+ // Prevent the overview from being started before the real home on first boot
+ if (deviceState.isButtonNavMode && !overviewCompObserver.isHomeAndOverviewSame) return
+
+ // Preloading while a restore is pending may cause launcher to start the restore too
+ // early
+ if ((RestoreDbTask.isPending(ctx) && !forSUWAllSet) || !deviceState.isUserSetupComplete)
+ return
+
+ // The activity has been created before the initialization of overview service. It is
+ // usually happens when booting or launcher is the top activity, so we should already
+ // have the latest state.
+ if (fromInit && overviewCompObserver.containerInterface.createdContainer != null) return
+
+ ActiveGestureProtoLogProxy.logPreloadRecentsAnimation()
+ val overviewIntent = Intent(overviewCompObserver.overviewIntentIgnoreSysUiState)
+ Executors.UI_HELPER_EXECUTOR.execute {
+ ActivityManagerWrapper.getInstance().preloadRecentsActivity(overviewIntent)
+ }
+ } finally {
+ Trace.endSection()
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java b/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java
index b583a4b..37d7030 100644
--- a/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java
+++ b/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java
@@ -17,7 +17,6 @@
import static com.android.app.animation.Interpolators.DECELERATE;
import static com.android.app.animation.Interpolators.LINEAR;
-import static com.android.launcher3.Flags.enableGridOnlyOverview;
import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
@@ -50,7 +49,6 @@
private enum RecentsResistanceParams {
FROM_APP(0.75f, 0.5f, 1f, false),
FROM_APP_TABLET(1f, 0.7f, 1f, true),
- FROM_APP_TABLET_GRID_ONLY(1f, 1f, 1f, true),
FROM_OVERVIEW(1f, 0.75f, 0.5f, false);
RecentsResistanceParams(float scaleStartResist, float scaleMaxResist,
@@ -259,9 +257,7 @@
this.translationTarget = translationTarget;
this.translationProperty = translationProperty;
if (dp.isTablet) {
- resistanceParams = enableGridOnlyOverview()
- ? RecentsResistanceParams.FROM_APP_TABLET_GRID_ONLY
- : RecentsResistanceParams.FROM_APP_TABLET;
+ resistanceParams = RecentsResistanceParams.FROM_APP_TABLET;
} else {
resistanceParams = RecentsResistanceParams.FROM_APP;
}
diff --git a/quickstep/src/com/android/quickstep/util/ContextualSearchInvoker.kt b/quickstep/src/com/android/quickstep/util/ContextualSearchInvoker.kt
index 724fa40..d00a39c 100644
--- a/quickstep/src/com/android/quickstep/util/ContextualSearchInvoker.kt
+++ b/quickstep/src/com/android/quickstep/util/ContextualSearchInvoker.kt
@@ -161,7 +161,11 @@
statsLogManager.logger().log(LAUNCHER_LAUNCH_OMNI_FAILED_NOT_AVAILABLE)
return false
}
-
+ if (isFakeLandscape()) {
+ // TODO (b/383421642): Fake landscape is to be removed in 25Q3 and this entire block
+ // can be removed when that happens.
+ return false
+ }
return true
}
@@ -197,6 +201,13 @@
return true
}
+ private fun isFakeLandscape(): Boolean =
+ getRecentsContainerInterface()
+ ?.getCreatedContainer()
+ ?.getOverviewPanel<RecentsView<*, *>>()
+ ?.getPagedOrientationHandler()
+ ?.isLayoutNaturalToLauncher == false
+
private fun isInSplitscreen(): Boolean {
return topTaskTracker.getRunningSplitTaskIds().isNotEmpty()
}
diff --git a/quickstep/src/com/android/quickstep/util/DesktopTask.java b/quickstep/src/com/android/quickstep/util/DesktopTask.java
deleted file mode 100644
index fc4fc4d..0000000
--- a/quickstep/src/com/android/quickstep/util/DesktopTask.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.quickstep.util;
-
-import androidx.annotation.NonNull;
-
-import com.android.quickstep.views.TaskViewType;
-import com.android.systemui.shared.recents.model.Task;
-
-import java.util.List;
-import java.util.Objects;
-
-/**
- * A {@link Task} container that can contain N number of tasks that are part of the desktop in
- * recent tasks list.
- */
-public class DesktopTask extends GroupTask {
-
- @NonNull
- public final List<Task> tasks;
-
- public DesktopTask(@NonNull List<Task> tasks) {
- super(tasks.get(0), null, null, TaskViewType.DESKTOP);
- this.tasks = tasks;
- }
-
- @Override
- public boolean containsTask(int taskId) {
- for (Task task : tasks) {
- if (task.key.id == taskId) {
- return true;
- }
- }
- return false;
- }
-
- @Override
- public boolean hasMultipleTasks() {
- return tasks.size() > 1;
- }
-
- @Override
- public boolean supportsMultipleTasks() {
- return true;
- }
-
- @Override
- @NonNull
- public List<Task> getTasks() {
- return tasks;
- }
-
- @Override
- public DesktopTask copy() {
- return new DesktopTask(tasks);
- }
-
- @Override
- public String toString() {
- return "type=" + taskViewType + " tasks=" + tasks;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (!(o instanceof DesktopTask that)) return false;
- if (!super.equals(o)) return false;
- return Objects.equals(tasks, that.tasks);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(super.hashCode(), tasks);
- }
-}
diff --git a/quickstep/src/com/android/quickstep/util/DesktopTask.kt b/quickstep/src/com/android/quickstep/util/DesktopTask.kt
new file mode 100644
index 0000000..1cee2d2
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/DesktopTask.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.util
+
+import com.android.quickstep.views.TaskViewType
+import com.android.systemui.shared.recents.model.Task
+import java.util.Objects
+
+/**
+ * A [Task] container that can contain N number of tasks that are part of the desktop in recent
+ * tasks list.
+ */
+class DesktopTask(override val tasks: List<Task>) :
+ GroupTask(tasks[0], null, null, TaskViewType.DESKTOP) {
+
+ override fun containsTask(taskId: Int) = tasks.any { it.key.id == taskId }
+
+ override fun hasMultipleTasks() = tasks.size > 1
+
+ override fun supportsMultipleTasks() = true
+
+ override fun copy() = DesktopTask(tasks)
+
+ override fun toString() = "type=$taskViewType tasks=$tasks"
+
+ override fun equals(o: Any?): Boolean {
+ if (this === o) return true
+ if (o !is DesktopTask) return false
+ if (!super.equals(o)) return false
+ return tasks == o.tasks
+ }
+
+ override fun hashCode() = Objects.hash(super.hashCode(), tasks)
+}
diff --git a/quickstep/src/com/android/quickstep/util/GroupTask.java b/quickstep/src/com/android/quickstep/util/GroupTask.java
deleted file mode 100644
index 7aeeb2f..0000000
--- a/quickstep/src/com/android/quickstep/util/GroupTask.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.quickstep.util;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
-import com.android.quickstep.views.TaskViewType;
-import com.android.systemui.shared.recents.model.Task;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * A {@link Task} container that can contain one or two tasks, depending on if the two tasks
- * are represented as an app-pair in the recents task list.
- */
-public class GroupTask {
- @NonNull
- public final Task task1;
- @Nullable
- public final Task task2;
- @Nullable
- public final SplitBounds mSplitBounds;
- public final TaskViewType taskViewType;
-
- public GroupTask(@NonNull Task task) {
- this(task, null, null);
- }
-
- public GroupTask(@NonNull Task t1, @Nullable Task t2, @Nullable SplitBounds splitBounds) {
- this(t1, t2, splitBounds, t2 != null ? TaskViewType.GROUPED : TaskViewType.SINGLE);
- }
-
- protected GroupTask(@NonNull Task t1, @Nullable Task t2, @Nullable SplitBounds splitBounds,
- TaskViewType taskViewType) {
- task1 = t1;
- task2 = t2;
- mSplitBounds = splitBounds;
- this.taskViewType = taskViewType;
- }
-
- public boolean containsTask(int taskId) {
- return task1.key.id == taskId || (task2 != null && task2.key.id == taskId);
- }
-
- public boolean hasMultipleTasks() {
- return task2 != null;
- }
-
- /**
- * Returns whether this task supports multiple tasks or not.
- */
- public boolean supportsMultipleTasks() {
- return taskViewType == TaskViewType.GROUPED;
- }
-
- /**
- * Returns a List of all the Tasks in this GroupTask
- */
- public List<Task> getTasks() {
- if (task2 == null) {
- return Collections.singletonList(task1);
- } else {
- return Arrays.asList(task1, task2);
- }
- }
-
- /**
- * Create a copy of this instance
- */
- public GroupTask copy() {
- return new GroupTask(
- new Task(task1),
- task2 != null ? new Task(task2) : null,
- mSplitBounds);
- }
-
- @Override
- public String toString() {
- return "type=" + taskViewType + " task1=" + task1 + " task2=" + task2;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (!(o instanceof GroupTask that)) return false;
- return taskViewType == that.taskViewType && Objects.equals(task1,
- that.task1) && Objects.equals(task2, that.task2)
- && Objects.equals(mSplitBounds, that.mSplitBounds);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(task1, task2, mSplitBounds, taskViewType);
- }
-}
diff --git a/quickstep/src/com/android/quickstep/util/GroupTask.kt b/quickstep/src/com/android/quickstep/util/GroupTask.kt
new file mode 100644
index 0000000..0bee5f6
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/GroupTask.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.util
+
+import androidx.annotation.VisibleForTesting
+import com.android.launcher3.util.SplitConfigurationOptions
+import com.android.quickstep.views.TaskViewType
+import com.android.systemui.shared.recents.model.Task
+import java.util.Objects
+
+/**
+ * A [Task] container that can contain one or two tasks, depending on if the two tasks are
+ * represented as an app-pair in the recents task list.
+ */
+open class GroupTask
+@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+constructor(
+ @Deprecated("Prefer using `getTasks()` instead") @JvmField val task1: Task,
+ @Deprecated("Prefer using `getTasks()` instead") @JvmField val task2: Task?,
+ @JvmField val mSplitBounds: SplitConfigurationOptions.SplitBounds?,
+ @JvmField val taskViewType: TaskViewType,
+) {
+ constructor(task: Task) : this(task, null, null)
+
+ constructor(
+ t1: Task,
+ t2: Task?,
+ splitBounds: SplitConfigurationOptions.SplitBounds?,
+ ) : this(t1, t2, splitBounds, if (t2 != null) TaskViewType.GROUPED else TaskViewType.SINGLE)
+
+ open fun containsTask(taskId: Int) =
+ task1.key.id == taskId || (task2 != null && task2.key.id == taskId)
+
+ /**
+ * Returns true if a task in this group has a package name that matches the given `packageName`.
+ */
+ fun containsPackage(packageName: String) = tasks.any { it.key.packageName == packageName }
+
+ open fun hasMultipleTasks() = task2 != null
+
+ /** Returns whether this task supports multiple tasks or not. */
+ open fun supportsMultipleTasks() = taskViewType == TaskViewType.GROUPED
+
+ /** Returns a List of all the Tasks in this GroupTask */
+ open val tasks: List<Task>
+ get() = listOfNotNull(task1, task2)
+
+ /** Creates a copy of this instance */
+ open fun copy() = GroupTask(Task(task1), if (task2 != null) Task(task2) else null, mSplitBounds)
+
+ override fun toString() = "type=$taskViewType task1=$task1 task2=$task2"
+
+ override fun equals(o: Any?): Boolean {
+ if (this === o) return true
+ if (o !is GroupTask) return false
+ return taskViewType == o.taskViewType &&
+ task1 == o.task1 &&
+ task2 == o.task2 &&
+ mSplitBounds == o.mSplitBounds
+ }
+
+ override fun hashCode() = Objects.hash(task1, task2, mSplitBounds, taskViewType)
+}
diff --git a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
index f708f4b..9b4c772 100644
--- a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
+++ b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
@@ -486,11 +486,7 @@
pendingAnimation.addEndListener {
splitSelectStateController.launchInitialAppFullscreen {
- if (FeatureFlags.enableSplitContextually()) {
- splitSelectStateController.resetState()
- } else if (resetCallback.isPresent) {
- resetCallback.get().run()
- }
+ splitSelectStateController.resetState()
}
}
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index c524286..5f8b4d9 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -169,7 +169,7 @@
private final BackPressHandler mSplitBackHandler = new BackPressHandler() {
@Override
public boolean canHandleBack() {
- return FeatureFlags.enableSplitContextually() && isSplitSelectActive();
+ return isSplitSelectActive();
}
@Override
@@ -678,7 +678,7 @@
}
@Override
- public void startAnimation(IBinder transition, TransitionInfo info,
+ public void startAnimation(IBinder transition, TransitionInfo transitionInfo,
SurfaceControl.Transaction t,
IRemoteTransitionFinishedCallback finishedCallback) {
final Runnable finishAdapter = () -> {
@@ -708,7 +708,7 @@
null /* nonApps */,
mStateManager,
mDepthController,
- info, t, () -> {
+ transitionInfo, t, () -> {
finishAdapter.run();
cleanup(true /*success*/);
},
@@ -739,8 +739,7 @@
}
/**
- * To be called whenever we exit split selection state. If
- * {@link FeatureFlags#enableSplitContextually()} is set, this should be the
+ * To be called whenever we exit split selection state. This should be the
* central way split is getting reset, which should then go through the callbacks to reset
* other state.
*/
@@ -920,7 +919,7 @@
@Override
public void onRecentsAnimationStart(RecentsAnimationController controller,
- RecentsAnimationTargets targets) {
+ RecentsAnimationTargets targets, TransitionInfo transitionInfo) {
StatsLogManager.LauncherEvent launcherDesktopSplitEvent =
mSplitPosition == STAGE_POSITION_BOTTOM_OR_RIGHT ?
LAUNCHER_DESKTOP_MODE_SPLIT_RIGHT_BOTTOM :
diff --git a/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
index 86090d5..d3390b4 100644
--- a/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
@@ -219,6 +219,6 @@
}
private boolean shouldIgnoreSecondSplitLaunch() {
- return !FeatureFlags.enableSplitContextually() || !mController.isSplitSelectActive();
+ return !mController.isSplitSelectActive();
}
}
diff --git a/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
index 0ba4083..0e27139 100644
--- a/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
@@ -16,7 +16,6 @@
package com.android.quickstep.util;
-import static com.android.launcher3.config.FeatureFlags.enableSplitContextually;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_KEYBOARD_SHORTCUT_SPLIT_LEFT_TOP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_KEYBOARD_SHORTCUT_SPLIT_RIGHT_BOTTOM;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
@@ -30,6 +29,7 @@
import android.app.ActivityOptions;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.window.TransitionInfo;
import androidx.annotation.BinderThread;
@@ -40,7 +40,6 @@
import com.android.quickstep.OverviewComponentObserver;
import com.android.quickstep.RecentsAnimationCallbacks;
import com.android.quickstep.RecentsAnimationController;
-import com.android.quickstep.RecentsAnimationDeviceState;
import com.android.quickstep.RecentsAnimationTargets;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.SystemUiProxy;
@@ -55,18 +54,15 @@
private final QuickstepLauncher mLauncher;
private final SplitSelectStateController mController;
- private final RecentsAnimationDeviceState mDeviceState;
private final OverviewComponentObserver mOverviewComponentObserver;
private final int mSplitPlaceholderSize;
private final int mSplitPlaceholderInset;
- public SplitWithKeyboardShortcutController(QuickstepLauncher launcher,
- SplitSelectStateController controller,
- RecentsAnimationDeviceState deviceState) {
+ public SplitWithKeyboardShortcutController(
+ QuickstepLauncher launcher, SplitSelectStateController controller) {
mLauncher = launcher;
mController = controller;
- mDeviceState = deviceState;
mOverviewComponentObserver = OverviewComponentObserver.INSTANCE.get(launcher);
mSplitPlaceholderSize = mLauncher.getResources().getDimensionPixelSize(
@@ -77,9 +73,8 @@
@BinderThread
public void enterStageSplit(boolean leftOrTop) {
- if (!enableSplitContextually() ||
- // Do not enter stage split from keyboard shortcuts if the user is already in split
- TopTaskTracker.INSTANCE.get(mLauncher).getRunningSplitTaskIds().length == 2) {
+ if (TopTaskTracker.INSTANCE.get(mLauncher).getRunningSplitTaskIds().length == 2) {
+ // Do not enter stage split from keyboard shortcuts if the user is already in split
return;
}
RecentsAnimationCallbacks callbacks = new RecentsAnimationCallbacks(
@@ -104,10 +99,6 @@
});
}
- public void onDestroy() {
- mDeviceState.destroy();
- }
-
private class SplitWithKeyboardShortcutRecentsAnimationListener implements
RecentsAnimationCallbacks.RecentsAnimationListener {
@@ -122,7 +113,7 @@
@Override
public void onRecentsAnimationStart(RecentsAnimationController controller,
- RecentsAnimationTargets targets) {
+ RecentsAnimationTargets targets, TransitionInfo transitionInfo) {
mController.setInitialTaskSelect(mRunningTaskInfo,
mLeftOrTop ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT,
null /* itemInfo */,
diff --git a/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java b/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java
index 7fadc7d..4d56c63 100644
--- a/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java
+++ b/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java
@@ -27,10 +27,8 @@
import android.view.WindowMetrics;
import com.android.internal.policy.SystemBarUtils;
-import com.android.launcher3.dagger.ApplicationContext;
import com.android.launcher3.dagger.LauncherAppSingleton;
import com.android.launcher3.statehandlers.DesktopVisibilityController;
-import com.android.launcher3.util.DaggerSingletonTracker;
import com.android.launcher3.util.WindowBounds;
import com.android.launcher3.util.window.CachedDisplayInfo;
import com.android.launcher3.util.window.WindowManagerProxy;
@@ -48,14 +46,13 @@
@LauncherAppSingleton
public class SystemWindowManagerProxy extends WindowManagerProxy {
- private final TISBindHelper mTISBindHelper;
+ private final DesktopVisibilityController mDesktopVisibilityController;
+
@Inject
- public SystemWindowManagerProxy(@ApplicationContext Context context,
- DaggerSingletonTracker lifecycleTracker) {
+ public SystemWindowManagerProxy(DesktopVisibilityController desktopVisibilityController) {
super(true);
- mTISBindHelper = new TISBindHelper(context, binder -> {});
- lifecycleTracker.addCloseable(mTISBindHelper::onDestroy);
+ mDesktopVisibilityController = desktopVisibilityController;
}
@Override
@@ -65,10 +62,18 @@
}
@Override
+ public void registerDesktopVisibilityListener(DesktopVisibilityListener listener) {
+ mDesktopVisibilityController.registerDesktopVisibilityListener(listener);
+ }
+
+ @Override
+ public void unregisterDesktopVisibilityListener(DesktopVisibilityListener listener) {
+ mDesktopVisibilityController.unregisterDesktopVisibilityListener(listener);
+ }
+
+ @Override
public boolean isInDesktopMode() {
- DesktopVisibilityController desktopController =
- mTISBindHelper.getDesktopVisibilityController();
- return desktopController != null && desktopController.areDesktopTasksVisible();
+ return mDesktopVisibilityController.areDesktopTasksVisible();
}
@Override
diff --git a/quickstep/src/com/android/quickstep/util/TISBindHelper.java b/quickstep/src/com/android/quickstep/util/TISBindHelper.java
index b238dec..027dc08 100644
--- a/quickstep/src/com/android/quickstep/util/TISBindHelper.java
+++ b/quickstep/src/com/android/quickstep/util/TISBindHelper.java
@@ -26,7 +26,6 @@
import androidx.annotation.Nullable;
-import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.taskbar.TaskbarManager;
import com.android.quickstep.OverviewCommandHelper;
import com.android.quickstep.TouchInteractionService;
@@ -110,11 +109,6 @@
return mBinder == null ? null : mBinder.getTaskbarManager();
}
- @Nullable
- public DesktopVisibilityController getDesktopVisibilityController() {
- return mBinder == null ? null : mBinder.getDesktopVisibilityController();
- }
-
/**
* Sets flag whether a predictive back-to-home animation is in progress
*/
diff --git a/quickstep/src/com/android/quickstep/util/TaskKeyByLastActiveTimeCache.java b/quickstep/src/com/android/quickstep/util/TaskKeyByLastActiveTimeCache.java
index 69137cc..43ef39c 100644
--- a/quickstep/src/com/android/quickstep/util/TaskKeyByLastActiveTimeCache.java
+++ b/quickstep/src/com/android/quickstep/util/TaskKeyByLastActiveTimeCache.java
@@ -17,6 +17,7 @@
import android.util.Log;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.systemui.shared.recents.model.Task;
@@ -94,6 +95,7 @@
* Gets the entry if it is still valid
*/
@Override
+ @Nullable
public synchronized V getAndInvalidateIfModified(Task.TaskKey key) {
Entry<V> entry = mMap.get(key.id);
if (entry != null && entry.mKey.windowingMode == key.windowingMode
diff --git a/quickstep/src/com/android/quickstep/util/TaskKeyCache.java b/quickstep/src/com/android/quickstep/util/TaskKeyCache.java
index 8ee78ab..9df0993 100644
--- a/quickstep/src/com/android/quickstep/util/TaskKeyCache.java
+++ b/quickstep/src/com/android/quickstep/util/TaskKeyCache.java
@@ -15,6 +15,8 @@
*/
package com.android.quickstep.util;
+import androidx.annotation.Nullable;
+
import com.android.systemui.shared.recents.model.Task;
import java.util.function.Predicate;
@@ -44,6 +46,7 @@
/**
* Gets the entry if it is still valid.
*/
+ @Nullable
V getAndInvalidateIfModified(Task.TaskKey key);
/**
diff --git a/quickstep/src/com/android/quickstep/util/TaskKeyLruCache.java b/quickstep/src/com/android/quickstep/util/TaskKeyLruCache.java
index 89f5d41..9fe8cc9 100644
--- a/quickstep/src/com/android/quickstep/util/TaskKeyLruCache.java
+++ b/quickstep/src/com/android/quickstep/util/TaskKeyLruCache.java
@@ -17,6 +17,8 @@
import android.util.Log;
+import androidx.annotation.Nullable;
+
import com.android.systemui.shared.recents.model.Task.TaskKey;
import java.util.LinkedHashMap;
@@ -59,6 +61,7 @@
/**
* Gets the entry if it is still valid
*/
+ @Nullable
public synchronized V getAndInvalidateIfModified(TaskKey key) {
Entry<V> entry = mMap.get(key.id);
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index 706cfe4..a1e55fb 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -41,7 +41,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.app.animation.Interpolators;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatedFloat;
@@ -101,8 +100,6 @@
// Carousel properties
public final AnimatedFloat carouselScale = new AnimatedFloat();
- public final AnimatedFloat carouselPrimaryTranslation = new AnimatedFloat();
- public final AnimatedFloat carouselSecondaryTranslation = new AnimatedFloat();
// RecentsView properties
public final AnimatedFloat recentsViewScale = new AnimatedFloat();
@@ -118,7 +115,7 @@
private Boolean mDrawsBelowRecents = null;
private boolean mIsGridTask;
private final boolean mIsDesktopTask;
- private boolean mScaleToCarouselTaskSize = false;
+ private boolean mIsAnimatingToCarousel = false;
private int mTaskRectTranslationX;
private int mTaskRectTranslationY;
private int mDesktopTaskIndex = 0;
@@ -149,6 +146,9 @@
mDp = dp;
mLayoutValid = false;
mOrientationState.setDeviceProfile(dp);
+ if (enableGridOnlyOverview()) {
+ mIsGridTask = dp.isTablet && !mIsDesktopTask;
+ }
calculateTaskSize();
}
@@ -160,14 +160,16 @@
if (mIsGridTask) {
mSizeStrategy.calculateGridTaskSize(mContext, mDp, mFullTaskSize,
mOrientationState.getOrientationHandler());
+ if (enableGridOnlyOverview()) {
+ mSizeStrategy.calculateTaskSize(mContext, mDp, mCarouselTaskSize,
+ mOrientationState.getOrientationHandler());
+ }
} else {
mSizeStrategy.calculateTaskSize(mContext, mDp, mFullTaskSize,
mOrientationState.getOrientationHandler());
- }
-
- if (enableGridOnlyOverview()) {
- mSizeStrategy.calculateCarouselTaskSize(mContext, mDp, mCarouselTaskSize,
- mOrientationState.getOrientationHandler());
+ if (enableGridOnlyOverview()) {
+ mCarouselTaskSize.set(mFullTaskSize);
+ }
}
if (mSplitBounds != null) {
@@ -222,12 +224,7 @@
}
// Copy mFullTaskSize instead of updating it directly so it could be reused next time
// without recalculating
- Rect scaleRect = new Rect();
- if (mScaleToCarouselTaskSize) {
- scaleRect.set(mCarouselTaskSize);
- } else {
- scaleRect.set(mFullTaskSize);
- }
+ Rect scaleRect = new Rect(mIsAnimatingToCarousel ? mCarouselTaskSize : mFullTaskSize);
scaleRect.offset(mTaskRectTranslationX, mTaskRectTranslationY);
float scale = mOrientationState.getFullScreenScaleAndPivot(scaleRect, mDp, mPivot);
if (mPivotOverride != null) {
@@ -313,66 +310,16 @@
}
/**
- * Adds animation for all the components corresponding to transition from an app to overview.
+ * Adds animation for all the components corresponding to transition from an app to carousel.
*/
- public void addAppToOverviewAnim(PendingAnimation pa, Interpolator interpolator) {
+ public void addAppToCarouselAnim(PendingAnimation pa, Interpolator interpolator) {
pa.addFloat(fullScreenProgress, AnimatedFloat.VALUE, 1, 0, interpolator);
- float fullScreenScale;
if (enableGridOnlyOverview() && mDp.isTablet && mDp.isGestureMode) {
- // Move pivot to top right edge of the screen, to avoid task scaling down in opposite
- // direction of app window movement, otherwise the animation will wiggle left and right.
- // Also translate the app window to top right edge of the screen to simplify
- // calculations.
- taskPrimaryTranslation.value = mIsRecentsRtl
- ? mDp.widthPx - mFullTaskSize.right
- : -mFullTaskSize.left;
- taskSecondaryTranslation.value = -mFullTaskSize.top;
- mPivotOverride = new PointF(mIsRecentsRtl ? mDp.widthPx : 0, 0);
-
- // Scale down to the carousel and use the carousel Rect to calculate fullScreenScale.
- mScaleToCarouselTaskSize = true;
+ mIsAnimatingToCarousel = true;
carouselScale.value = mCarouselTaskSize.width() / (float) mFullTaskSize.width();
- fullScreenScale = getFullScreenScale();
-
- float carouselPrimaryTranslationTarget = mIsRecentsRtl
- ? mCarouselTaskSize.right - mDp.widthPx
- : mCarouselTaskSize.left;
- float carouselSecondaryTranslationTarget = mCarouselTaskSize.top;
-
- // Expected carousel position's center is in the middle, and invariant of
- // recentsViewScale.
- float exceptedCarouselCenterX = mCarouselTaskSize.centerX();
- // Animating carousel translations linearly will result in a curved path, therefore
- // we'll need to calculate the expected translation at each recentsView scale. Luckily
- // primary and secondary follow the same translation, and primary is used here due to
- // it being simpler.
- Interpolator carouselTranslationInterpolator = t -> {
- // recentsViewScale is calculated rather than using recentsViewScale.value, so that
- // this interpolator works independently even if recentsViewScale don't animate.
- float recentsViewScale =
- Utilities.mapToRange(t, 0, 1, fullScreenScale, 1, Interpolators.LINEAR);
- // Without the translation, the app window will animate from fullscreen into top
- // right corner.
- float expectedTaskCenterX = mIsRecentsRtl
- ? mDp.widthPx - mCarouselTaskSize.width() * recentsViewScale / 2f
- : mCarouselTaskSize.width() * recentsViewScale / 2f;
- // Calculate the expected translation, then work back the animatedFraction that
- // results in this value.
- float carouselPrimaryTranslation =
- (exceptedCarouselCenterX - expectedTaskCenterX) / recentsViewScale;
- return carouselPrimaryTranslation / carouselPrimaryTranslationTarget;
- };
-
- // Use addAnimatedFloat so this animation can later be canceled and animate to a
- // different value in RecentsView.onPrepareGestureEndAnimation.
- pa.addAnimatedFloat(carouselPrimaryTranslation, 0, carouselPrimaryTranslationTarget,
- carouselTranslationInterpolator);
- pa.addAnimatedFloat(carouselSecondaryTranslation, 0, carouselSecondaryTranslationTarget,
- carouselTranslationInterpolator);
- } else {
- fullScreenScale = getFullScreenScale();
}
- pa.addFloat(recentsViewScale, AnimatedFloat.VALUE, fullScreenScale, 1, interpolator);
+ pa.addFloat(recentsViewScale, AnimatedFloat.VALUE, getFullScreenScale(), 1,
+ interpolator);
}
/**
@@ -484,11 +431,9 @@
mOrientationState.getOrientationHandler().setSecondary(mMatrix, MATRIX_POST_TRANSLATE,
taskSecondaryTranslation.value);
- mMatrix.postScale(carouselScale.value, carouselScale.value, mPivot.x, mPivot.y);
- mOrientationState.getOrientationHandler().setPrimary(mMatrix, MATRIX_POST_TRANSLATE,
- carouselPrimaryTranslation.value);
- mOrientationState.getOrientationHandler().setSecondary(mMatrix, MATRIX_POST_TRANSLATE,
- carouselSecondaryTranslation.value);
+ mMatrix.postScale(carouselScale.value, carouselScale.value,
+ mIsRecentsRtl ? mCarouselTaskSize.right : mCarouselTaskSize.left,
+ mCarouselTaskSize.top);
mOrientationState.getOrientationHandler().setPrimary(
mMatrix, MATRIX_POST_TRANSLATE, recentsViewScroll.value);
@@ -524,8 +469,6 @@
+ " taskRect: " + mTaskRect
+ " taskPrimaryT: " + taskPrimaryTranslation.value
+ " taskSecondaryT: " + taskSecondaryTranslation.value
- + " carouselPrimaryT: " + carouselPrimaryTranslation.value
- + " carouselSecondaryT: " + carouselSecondaryTranslation.value
+ " recentsPrimaryT: " + recentsViewPrimaryTranslation.value
+ " recentsSecondaryT: " + recentsViewSecondaryTranslation.value
+ " recentsScroll: " + recentsViewScroll.value
diff --git a/quickstep/src/com/android/quickstep/util/TransformParams.java b/quickstep/src/com/android/quickstep/util/TransformParams.java
index 401eccc..bb88818 100644
--- a/quickstep/src/com/android/quickstep/util/TransformParams.java
+++ b/quickstep/src/com/android/quickstep/util/TransformParams.java
@@ -19,9 +19,12 @@
import android.util.FloatProperty;
import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
+import android.window.TransitionInfo;
import com.android.quickstep.RemoteAnimationTargets;
import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
+import com.android.window.flags.Flags;
public class TransformParams {
@@ -56,6 +59,7 @@
private float mTargetAlpha;
private float mCornerRadius;
private RemoteAnimationTargets mTargetSet;
+ private TransitionInfo mTransitionInfo;
private SurfaceTransactionApplier mSyncTransactionApplier;
private BuilderProxy mHomeBuilderProxy = BuilderProxy.ALWAYS_VISIBLE;
@@ -107,6 +111,14 @@
}
/**
+ * Provides the {@code TransitionInfo} of the transition that this transformation stems from.
+ */
+ public TransformParams setTransitionInfo(TransitionInfo transitionInfo) {
+ mTransitionInfo = transitionInfo;
+ return this;
+ }
+
+ /**
* Sets the SyncRtSurfaceTransactionApplierCompat that will apply the SurfaceParams that
* are computed based on these TransformParams.
*/
@@ -152,6 +164,9 @@
builder.setAlpha(getTargetAlpha());
}
targetProxy.onBuildTargetParams(builder, app, this);
+ // Override the corner radius for {@code app} with the leash used by Shell, so that it
+ // doesn't interfere with the window clip and corner radius applied here.
+ overrideChangeLeashCornerRadiusToZero(app, transaction.getTransaction());
}
// always put wallpaper layer to bottom.
@@ -163,6 +178,28 @@
return transaction;
}
+ private void overrideChangeLeashCornerRadiusToZero(
+ RemoteAnimationTarget app, SurfaceControl.Transaction transaction) {
+ if (!Flags.enableDesktopRecentsTransitionsCornersBugfix()) {
+ return;
+ }
+ SurfaceControl changeLeash = getChangeLeashForApp(app);
+ if (changeLeash != null) {
+ transaction.setCornerRadius(changeLeash, 0);
+ }
+ }
+
+ private SurfaceControl getChangeLeashForApp(RemoteAnimationTarget app) {
+ if (mTransitionInfo == null) return null;
+ for (TransitionInfo.Change change : mTransitionInfo.getChanges()) {
+ if (change.getTaskInfo() == null) continue;
+ if (change.getTaskInfo().taskId == app.taskId) {
+ return change.getLeash();
+ }
+ }
+ return null;
+ }
+
// Pubic getters so outside packages can read the values.
public float getProgress() {
diff --git a/quickstep/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
index c3efc3c..2426697 100644
--- a/quickstep/src/com/android/quickstep/views/ClearAllButton.java
+++ b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
@@ -16,7 +16,6 @@
package com.android.quickstep.views;
-import static com.android.launcher3.Flags.enableGridOnlyOverview;
import static com.android.quickstep.util.BorderAnimator.DEFAULT_BORDER_COLOR;
import android.content.Context;
@@ -31,7 +30,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Flags;
import com.android.launcher3.R;
import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
@@ -67,7 +65,6 @@
}
};
- private final RecentsViewContainer mContainer;
private float mScrollAlpha = 1;
private float mContentAlpha = 1;
private float mVisibilityAlpha = 1;
@@ -79,6 +76,7 @@
private float mNormalTranslationPrimary;
private float mFullscreenTranslationPrimary;
private float mGridTranslationPrimary;
+ private float mTaskAlignmentTranslationY;
private float mGridScrollOffset;
private float mScrollOffsetPrimary;
@@ -91,7 +89,6 @@
public ClearAllButton(Context context, AttributeSet attrs) {
super(context, attrs);
mIsRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
- mContainer = RecentsViewContainer.containerFromContext(context);
if (Flags.enableFocusOutline()) {
TypedArray styledAttrs = context.obtainStyledAttributes(attrs,
@@ -242,6 +239,15 @@
applyPrimaryTranslation();
}
+ /**
+ * Sets `mTaskAlignmentTranslationY` to the given `value`. In order to put the button at the
+ * middle in the secondary coordinate.
+ */
+ public void setTaskAlignmentTranslationY(float value) {
+ mTaskAlignmentTranslationY = value;
+ applySecondaryTranslation();
+ }
+
public void setGridTranslationPrimary(float gridTranslationPrimary) {
mGridTranslationPrimary = gridTranslationPrimary;
applyPrimaryTranslation();
@@ -300,7 +306,7 @@
RecentsPagedOrientationHandler orientationHandler =
recentsView.getPagedOrientationHandler();
orientationHandler.getPrimaryViewTranslate().set(this,
- orientationHandler.getPrimaryValue(0f, getOriginalTranslationY())
+ orientationHandler.getPrimaryValue(0f, mTaskAlignmentTranslationY)
+ mNormalTranslationPrimary + getFullscreenTrans(
mFullscreenTranslationPrimary) + getGridTrans(mGridTranslationPrimary));
}
@@ -314,7 +320,7 @@
RecentsPagedOrientationHandler orientationHandler =
recentsView.getPagedOrientationHandler();
orientationHandler.getSecondaryViewTranslate().set(this,
- orientationHandler.getSecondaryValue(0f, getOriginalTranslationY()));
+ orientationHandler.getSecondaryValue(0f, mTaskAlignmentTranslationY));
}
private float getFullscreenTrans(float endTranslation) {
@@ -324,21 +330,4 @@
private float getGridTrans(float endTranslation) {
return mGridProgress > 0 ? endTranslation : 0;
}
-
- /**
- * Get the Y translation that is set in the original layout position, before scrolling.
- */
- private float getOriginalTranslationY() {
- DeviceProfile deviceProfile = mContainer.getDeviceProfile();
- if (deviceProfile.isTablet) {
- if (enableGridOnlyOverview()) {
- return (getRecentsView().getLastComputedTaskSize().height()
- + deviceProfile.overviewTaskThumbnailTopMarginPx) / 2.0f
- + deviceProfile.overviewRowSpacing;
- } else {
- return deviceProfile.overviewRowSpacing;
- }
- }
- return deviceProfile.overviewTaskThumbnailTopMarginPx / 2.0f;
- }
}
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
index 7d04451..471313a 100644
--- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
@@ -174,16 +174,8 @@
}
@SuppressLint("RtlHardcoded")
- override fun updateTaskSize(
- lastComputedTaskSize: Rect,
- lastComputedGridTaskSize: Rect,
- lastComputedCarouselTaskSize: Rect,
- ) {
- super.updateTaskSize(
- lastComputedTaskSize,
- lastComputedGridTaskSize,
- lastComputedCarouselTaskSize,
- )
+ override fun updateTaskSize(lastComputedTaskSize: Rect, lastComputedGridTaskSize: Rect) {
+ super.updateTaskSize(lastComputedTaskSize, lastComputedGridTaskSize)
if (taskContainers.isEmpty()) {
return
}
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index b9f44fe..9be462c 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -50,7 +50,6 @@
import com.android.quickstep.BaseContainerInterface;
import com.android.quickstep.GestureState;
import com.android.quickstep.LauncherActivityInterface;
-import com.android.quickstep.RotationTouchHelper;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.AnimUtils;
import com.android.quickstep.util.SplitSelectStateController;
@@ -168,9 +167,7 @@
@Override
public void onStateTransitionComplete(LauncherState finalState) {
- if (mContainer.getDesktopVisibilityController() != null) {
- mContainer.getDesktopVisibilityController().onLauncherStateChanged(finalState);
- }
+ DesktopVisibilityController.INSTANCE.get(mContainer).onLauncherStateChanged(finalState);
if (!finalState.isRecentsViewVisible) {
// Clean-up logic that occurs when recents is no longer in use/visible.
@@ -258,45 +255,32 @@
@Override
public boolean canLaunchFullscreenTask() {
- if (FeatureFlags.enableSplitContextually()) {
- return !mSplitSelectStateController.isSplitSelectActive();
- } else {
- return !mContainer.isInState(OVERVIEW_SPLIT_SELECT);
- }
+ return !mSplitSelectStateController.isSplitSelectActive();
}
@Override
- public void onGestureAnimationStart(Task[] runningTasks,
- RotationTouchHelper rotationTouchHelper) {
- super.onGestureAnimationStart(runningTasks, rotationTouchHelper);
- DesktopVisibilityController desktopVisibilityController =
- mContainer.getDesktopVisibilityController();
- if (!ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()
- && desktopVisibilityController != null) {
+ public void onGestureAnimationStart(Task[] runningTasks) {
+ super.onGestureAnimationStart(runningTasks);
+ if (!ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()) {
// TODO: b/333533253 - Remove after flag rollout
- desktopVisibilityController.setRecentsGestureStart();
+ DesktopVisibilityController.INSTANCE.get(mContainer).setRecentsGestureStart();
}
}
@Override
public void onGestureAnimationEnd() {
- DesktopVisibilityController desktopVisibilityController =
- mContainer.getDesktopVisibilityController();
+ final DesktopVisibilityController desktopVisibilityController =
+ DesktopVisibilityController.INSTANCE.get(mContainer);
boolean showDesktopApps = false;
- GestureState.GestureEndTarget endTarget = null;
- if (desktopVisibilityController != null) {
- desktopVisibilityController = mContainer.getDesktopVisibilityController();
- endTarget = mCurrentGestureEndTarget;
- if (endTarget == GestureState.GestureEndTarget.LAST_TASK
- && desktopVisibilityController.areDesktopTasksVisibleAndNotInOverview()) {
- // Recents gesture was cancelled and we are returning to the previous task.
- // After super class has handled clean up, show desktop apps on top again
- showDesktopApps = true;
- }
+ GestureState.GestureEndTarget endTarget = mCurrentGestureEndTarget;
+ if (endTarget == GestureState.GestureEndTarget.LAST_TASK
+ && desktopVisibilityController.areDesktopTasksVisibleAndNotInOverview()) {
+ // Recents gesture was cancelled and we are returning to the previous task.
+ // After super class has handled clean up, show desktop apps on top again
+ showDesktopApps = true;
}
super.onGestureAnimationEnd();
- if (!ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()
- && desktopVisibilityController != null) {
+ if (!ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()) {
// TODO: b/333533253 - Remove after flag rollout
desktopVisibilityController.setRecentsGestureEnd(endTarget);
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 4660c51..cfad303 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -182,6 +182,7 @@
import com.android.launcher3.util.coroutines.DispatcherProvider;
import com.android.quickstep.BaseContainerInterface;
import com.android.quickstep.GestureState;
+import com.android.quickstep.HighResLoadingState;
import com.android.quickstep.OverviewCommandHelper;
import com.android.quickstep.RecentsAnimationController;
import com.android.quickstep.RecentsAnimationTargets;
@@ -194,7 +195,6 @@
import com.android.quickstep.SplitSelectionListener;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskOverlayFactory;
-import com.android.quickstep.TaskThumbnailCache;
import com.android.quickstep.TaskViewUtils;
import com.android.quickstep.TopTaskTracker;
import com.android.quickstep.ViewUtils;
@@ -209,7 +209,6 @@
import com.android.quickstep.recents.viewmodel.RecentsViewModel;
import com.android.quickstep.util.ActiveGestureProtoLogProxy;
import com.android.quickstep.util.AnimUtils;
-import com.android.quickstep.util.DesktopTask;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.util.RecentsAtomicAnimationFactory;
@@ -248,7 +247,6 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
@@ -263,14 +261,14 @@
public abstract class RecentsView<
CONTAINER_TYPE extends Context & RecentsViewContainer & StatefulContainer<STATE_TYPE>,
STATE_TYPE extends BaseState<STATE_TYPE>> extends PagedView implements Insettable,
- TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback,
+ HighResLoadingState.HighResLoadingStateChangedCallback,
TaskVisualsChangeListener {
private static final String TAG = "RecentsView";
private static final boolean DEBUG = false;
- public static final FloatProperty<RecentsView> CONTENT_ALPHA =
- new FloatProperty<RecentsView>("contentAlpha") {
+ public static final FloatProperty<RecentsView<?, ?>> CONTENT_ALPHA =
+ new FloatProperty<>("contentAlpha") {
@Override
public void setValue(RecentsView view, float v) {
view.setContentAlpha(v);
@@ -282,8 +280,8 @@
}
};
- public static final FloatProperty<RecentsView> FULLSCREEN_PROGRESS =
- new FloatProperty<RecentsView>("fullscreenProgress") {
+ public static final FloatProperty<RecentsView<?, ?>> FULLSCREEN_PROGRESS =
+ new FloatProperty<>("fullscreenProgress") {
@Override
public void setValue(RecentsView recentsView, float v) {
recentsView.setFullscreenProgress(v);
@@ -295,8 +293,8 @@
}
};
- public static final FloatProperty<RecentsView> TASK_MODALNESS =
- new FloatProperty<RecentsView>("taskModalness") {
+ public static final FloatProperty<RecentsView<?, ?>> TASK_MODALNESS =
+ new FloatProperty<>("taskModalness") {
@Override
public void setValue(RecentsView recentsView, float v) {
recentsView.setTaskModalness(v);
@@ -308,8 +306,8 @@
}
};
- public static final FloatProperty<RecentsView> ADJACENT_PAGE_HORIZONTAL_OFFSET =
- new FloatProperty<RecentsView>("adjacentPageHorizontalOffset") {
+ public static final FloatProperty<RecentsView<?, ?>> ADJACENT_PAGE_HORIZONTAL_OFFSET =
+ new FloatProperty<>("adjacentPageHorizontalOffset") {
@Override
public void setValue(RecentsView recentsView, float v) {
if (recentsView.mAdjacentPageHorizontalOffset != v) {
@@ -324,8 +322,8 @@
}
};
- public static final FloatProperty<RecentsView> RUNNING_TASK_ATTACH_ALPHA =
- new FloatProperty<RecentsView>("runningTaskAttachAlpha") {
+ public static final FloatProperty<RecentsView<?, ?>> RUNNING_TASK_ATTACH_ALPHA =
+ new FloatProperty<>("runningTaskAttachAlpha") {
@Override
public void setValue(RecentsView recentsView, float v) {
recentsView.mRunningTaskAttachAlpha = v;
@@ -349,8 +347,8 @@
* Can be used to tint the color of the RecentsView to simulate a scrim that can views
* excluded from. Really should be a proper scrim.
*/
- private static final FloatProperty<RecentsView> COLOR_TINT =
- new FloatProperty<RecentsView>("colorTint") {
+ private static final FloatProperty<RecentsView<?, ?>> COLOR_TINT =
+ new FloatProperty<>("colorTint") {
@Override
public void setValue(RecentsView recentsView, float v) {
recentsView.setColorTint(v);
@@ -368,8 +366,8 @@
* more specific, we'd want to create a similar FloatProperty just for a TaskView's
* offsetX/Y property
*/
- public static final FloatProperty<RecentsView> TASK_SECONDARY_TRANSLATION =
- new FloatProperty<RecentsView>("taskSecondaryTranslation") {
+ public static final FloatProperty<RecentsView<?, ?>> TASK_SECONDARY_TRANSLATION =
+ new FloatProperty<>("taskSecondaryTranslation") {
@Override
public void setValue(RecentsView recentsView, float v) {
recentsView.setTaskViewsResistanceTranslation(v);
@@ -387,8 +385,8 @@
* more specific, we'd want to create a similar FloatProperty just for a TaskView's
* offsetX/Y property
*/
- public static final FloatProperty<RecentsView> TASK_PRIMARY_SPLIT_TRANSLATION =
- new FloatProperty<RecentsView>("taskPrimarySplitTranslation") {
+ public static final FloatProperty<RecentsView<?, ?>> TASK_PRIMARY_SPLIT_TRANSLATION =
+ new FloatProperty<>("taskPrimarySplitTranslation") {
@Override
public void setValue(RecentsView recentsView, float v) {
recentsView.setTaskViewsPrimarySplitTranslation(v);
@@ -400,8 +398,8 @@
}
};
- public static final FloatProperty<RecentsView> TASK_SECONDARY_SPLIT_TRANSLATION =
- new FloatProperty<RecentsView>("taskSecondarySplitTranslation") {
+ public static final FloatProperty<RecentsView<?, ?>> TASK_SECONDARY_SPLIT_TRANSLATION =
+ new FloatProperty<>("taskSecondarySplitTranslation") {
@Override
public void setValue(RecentsView recentsView, float v) {
recentsView.setTaskViewsSecondarySplitTranslation(v);
@@ -414,8 +412,8 @@
};
/** Same as normal SCALE_PROPERTY, but also updates page offsets that depend on this scale. */
- public static final FloatProperty<RecentsView> RECENTS_SCALE_PROPERTY =
- new FloatProperty<RecentsView>("recentsScale") {
+ public static final FloatProperty<RecentsView<?, ?>> RECENTS_SCALE_PROPERTY =
+ new FloatProperty<>("recentsScale") {
@Override
public void setValue(RecentsView view, float scale) {
view.setScaleX(scale);
@@ -444,8 +442,8 @@
* Progress of Recents view from carousel layout to grid layout. If Recents is not shown as a
* grid, then the value remains 0.
*/
- public static final FloatProperty<RecentsView> RECENTS_GRID_PROGRESS =
- new FloatProperty<RecentsView>("recentsGrid") {
+ public static final FloatProperty<RecentsView<?, ?>> RECENTS_GRID_PROGRESS =
+ new FloatProperty<>("recentsGrid") {
@Override
public void setValue(RecentsView view, float gridProgress) {
view.setGridProgress(gridProgress);
@@ -457,7 +455,7 @@
}
};
- public static final FloatProperty<RecentsView> DESKTOP_CAROUSEL_DETACH_PROGRESS =
+ public static final FloatProperty<RecentsView<?, ?>> DESKTOP_CAROUSEL_DETACH_PROGRESS =
new FloatProperty<>("desktopCarouselDetachProgress") {
@Override
public void setValue(RecentsView view, float offset) {
@@ -476,8 +474,8 @@
* Alpha of the task thumbnail splash, where being in BackgroundAppState has a value of 1, and
* being in any other state has a value of 0.
*/
- public static final FloatProperty<RecentsView> TASK_THUMBNAIL_SPLASH_ALPHA =
- new FloatProperty<RecentsView>("taskThumbnailSplashAlpha") {
+ public static final FloatProperty<RecentsView<?, ?>> TASK_THUMBNAIL_SPLASH_ALPHA =
+ new FloatProperty<>("taskThumbnailSplashAlpha") {
@Override
public void setValue(RecentsView view, float taskThumbnailSplashAlpha) {
view.setTaskThumbnailSplashAlpha(taskThumbnailSplashAlpha);
@@ -522,7 +520,6 @@
@Nullable
protected RemoteTargetHandle[] mRemoteTargetHandles;
- protected final Rect mLastComputedCarouselTaskSize = new Rect();
protected final Rect mLastComputedTaskSize = new Rect();
protected final Rect mLastComputedGridSize = new Rect();
protected final Rect mLastComputedGridTaskSize = new Rect();
@@ -552,6 +549,8 @@
private final ClearAllButton mClearAllButton;
private final Rect mClearAllButtonDeadZoneRect = new Rect();
private final Rect mTaskViewDeadZoneRect = new Rect();
+ private final Rect mTopRowDeadZoneRect = new Rect();
+ private final Rect mBottomRowDeadZoneRect = new Rect();
/**
* Reflects if Recents is currently in the middle of a gesture, and if so, which tasks are
* running. If a gesture is not in progress, this will be null.
@@ -928,8 +927,11 @@
mEmptyMessagePaint.setColor(Themes.getAttrColor(context, android.R.attr.textColorPrimary));
mEmptyMessagePaint.setTextSize(getResources()
.getDimension(R.dimen.recents_empty_message_text_size));
- mEmptyMessagePaint.setTypeface(Typeface.create(Themes.getDefaultBodyFont(context),
- Typeface.NORMAL));
+ Typeface typeface = Typeface.create(
+ Typeface.create(Themes.getDefaultBodyFont(context), Typeface.NORMAL),
+ getFontWeight(),
+ false);
+ mEmptyMessagePaint.setTypeface(typeface);
mEmptyMessagePaint.setAntiAlias(true);
mEmptyMessagePadding = getResources()
.getDimensionPixelSize(R.dimen.recents_empty_message_text_padding);
@@ -1215,9 +1217,7 @@
mIPipAnimationListener);
mOrientationState.initListeners();
mTaskOverlayFactory.initListeners();
- if (FeatureFlags.enableSplitContextually()) {
- mSplitSelectStateController.registerSplitListener(mSplitSelectionListener);
- }
+ mSplitSelectStateController.registerSplitListener(mSplitSelectionListener);
}
@Override
@@ -1237,12 +1237,17 @@
mIPipAnimationListener.setActivityAndRecentsView(null, null);
mOrientationState.destroyListeners();
mTaskOverlayFactory.removeListeners();
- if (FeatureFlags.enableSplitContextually()) {
- mSplitSelectStateController.unregisterSplitListener(mSplitSelectionListener);
- }
+ mSplitSelectStateController.unregisterSplitListener(mSplitSelectionListener);
reset();
+ }
+
+ /**
+ * Execute clean-up logic needed when the view is destroyed.
+ */
+ public void destroy() {
+ Log.d(TAG, "destroy");
if (enableRefactorTaskThumbnail()) {
- mHelper.onDetachedFromWindow();
+ mHelper.onDestroy();
RecentsDependencies.destroy();
}
}
@@ -1250,7 +1255,6 @@
@Override
public void onViewRemoved(View child) {
super.onViewRemoved(child);
-
// Clear the task data for the removed child if it was visible unless:
// - It's the initial taskview for entering split screen, we only pretend to dismiss the
// task
@@ -1258,22 +1262,25 @@
if (child instanceof TaskView) {
mTaskViewCount = Math.max(0, --mTaskViewCount);
if (child != mSplitHiddenTaskView && child != mMovingTaskView) {
- TaskView taskView = (TaskView) child;
- for (int i : taskView.getTaskIds()) {
- mHasVisibleTaskData.delete(i);
- }
- if (child instanceof GroupedTaskView) {
- mGroupedTaskViewPool.recycle((GroupedTaskView) taskView);
- } else if (child instanceof DesktopTaskView) {
- mDesktopTaskViewPool.recycle((DesktopTaskView) taskView);
- } else {
- mTaskViewPool.recycle(taskView);
- }
- mActionsView.updateHiddenFlags(HIDDEN_NO_TASKS, !hasTaskViews());
+ clearAndRecycleTaskView((TaskView) child);
}
}
}
+ private void clearAndRecycleTaskView(TaskView taskView) {
+ for (int i : taskView.getTaskIds()) {
+ mHasVisibleTaskData.delete(i);
+ }
+ if (taskView instanceof GroupedTaskView) {
+ mGroupedTaskViewPool.recycle((GroupedTaskView) taskView);
+ } else if (taskView instanceof DesktopTaskView) {
+ mDesktopTaskViewPool.recycle((DesktopTaskView) taskView);
+ } else {
+ mTaskViewPool.recycle(taskView);
+ }
+ mActionsView.updateHiddenFlags(HIDDEN_NO_TASKS, !hasTaskViews());
+ }
+
@Override
public void onViewAdded(View child) {
super.onViewAdded(child);
@@ -1711,8 +1718,11 @@
mClearAllButton.getAlpha() == 1
&& mClearAllButtonDeadZoneRect.contains(x, y);
final boolean cameFromNavBar = (ev.getEdgeFlags() & EDGE_NAV_BAR) != 0;
+ int adjustedX = x + getScrollX();
if (!clearAllButtonDeadZoneConsumed && !cameFromNavBar
- && !mTaskViewDeadZoneRect.contains(x + getScrollX(), y)) {
+ && !mTaskViewDeadZoneRect.contains(adjustedX, y)
+ && !mTopRowDeadZoneRect.contains(adjustedX, y)
+ && !mBottomRowDeadZoneRect.contains(adjustedX, y)) {
mTouchDownToStartHome = true;
}
}
@@ -1824,20 +1834,23 @@
}
private int getRunningTaskExpectedIndex(TaskView runningTaskView) {
+ int firstTaskViewIndex = indexOfChild(getFirstTaskView());
if (mContainer.getDeviceProfile().isTablet) {
if (runningTaskView instanceof DesktopTaskView) {
- return 0; // Desktop running task is always in front.
+ return firstTaskViewIndex; // Desktop running task is always in front.
} else if (enableLargeDesktopWindowingTile()) {
- return getDesktopTaskViewCount(); // Other running task is behind desktop tasks.
+ // Other running task is behind desktop tasks.
+ return getDesktopTaskViewCount() + firstTaskViewIndex;
} else {
- return 0;
+ return firstTaskViewIndex;
}
} else {
int currentIndex = indexOfChild(runningTaskView);
if (currentIndex != -1) {
return currentIndex; // Keep the position if running task already in layout.
} else {
- return 0; // New running task are added to the front to begin with.
+ // New running task are added to the front to begin with.
+ return firstTaskViewIndex;
}
}
}
@@ -1960,7 +1973,7 @@
} else if (taskView instanceof DesktopTaskView) {
// Minimized tasks should not be shown in Overview
List<Task> nonMinimizedTasks =
- ((DesktopTask) groupTask).tasks.stream()
+ groupTask.getTasks().stream()
.filter(task -> !task.isMinimized)
.toList();
((DesktopTaskView) taskView).bind(nonMinimizedTasks, mOrientationState,
@@ -1978,9 +1991,7 @@
}
}
- if (!taskGroups.isEmpty()) {
- addView(mClearAllButton);
- }
+ addView(mClearAllButton);
// Keep same previous focused task
TaskView newFocusedTaskView = null;
@@ -2083,14 +2094,14 @@
CollectionsKt
.filter(getTaskViews(), taskView -> !isGestureActive() || !taskView.isRunningTask())
.forEach(this::removeView);
- if (!hasTaskViews() && indexOfChild(mClearAllButton) != -1) {
+ if (!hasTaskViews()) {
removeView(mClearAllButton);
}
}
/** Returns true if there are at least one TaskView has been added to the RecentsView. */
public boolean hasTaskViews() {
- return CollectionsKt.any(getTaskViews());
+ return mUtils.hasTaskViews();
}
public int getTaskViewCount() {
@@ -2281,11 +2292,6 @@
mSizeStrategy.calculateGridTaskSize(mContainer, dp, mLastComputedGridTaskSize,
getPagedOrientationHandler());
- if (enableGridOnlyOverview()) {
- mSizeStrategy.calculateCarouselTaskSize(mContainer, dp, mLastComputedCarouselTaskSize,
- getPagedOrientationHandler());
- }
-
mTaskGridVerticalDiff = mLastComputedGridTaskSize.top - mLastComputedTaskSize.top;
mTopBottomRowHeightDiff =
mLastComputedGridTaskSize.height() + dp.overviewTaskThumbnailTopMarginPx
@@ -2305,17 +2311,9 @@
}
float accumulatedTranslationX = 0;
- float translateXToMiddle = 0;
- if (enableGridOnlyOverview() && mContainer.getDeviceProfile().isTablet) {
- translateXToMiddle = mIsRtl
- ? mLastComputedCarouselTaskSize.right - mLastComputedTaskSize.right
- : mLastComputedCarouselTaskSize.left - mLastComputedTaskSize.left;
- }
for (TaskView taskView : getTaskViews()) {
- taskView.updateTaskSize(mLastComputedTaskSize, mLastComputedGridTaskSize,
- mLastComputedCarouselTaskSize);
+ taskView.updateTaskSize(mLastComputedTaskSize, mLastComputedGridTaskSize);
taskView.setNonGridTranslationX(accumulatedTranslationX);
- taskView.setNonGridPivotTranslationX(translateXToMiddle);
// Compensate space caused by TaskView scaling.
float widthDiff =
taskView.getLayoutParams().width * (1 - taskView.getNonGridScale());
@@ -2324,6 +2322,9 @@
mClearAllButton.setFullscreenTranslationPrimary(accumulatedTranslationX);
+ float taskAlignmentTranslationY = getTaskAlignmentTranslationY();
+ mClearAllButton.setTaskAlignmentTranslationY(taskAlignmentTranslationY);
+
updateGridProperties();
}
@@ -2344,17 +2345,32 @@
*/
public Rect getSelectedTaskBounds() {
if (mSelectedTask == null) {
- return mLastComputedTaskSize;
+ return enableGridOnlyOverview() && mContainer.getDeviceProfile().isTablet
+ ? mLastComputedGridTaskSize : mLastComputedTaskSize;
}
return getTaskBounds(mSelectedTask);
}
+ /**
+ * Get the Y translation that should be applied to the non-TaskView item inside the RecentsView
+ * (ClearAllButton and AddDesktopButton) in the original layout position, before scrolling. This
+ * is done to make sure the button is aligned to the middle of Task thumbnail in y coordinate.
+ */
+ private float getTaskAlignmentTranslationY() {
+ DeviceProfile deviceProfile = mContainer.getDeviceProfile();
+ if (deviceProfile.isTablet) {
+ return deviceProfile.overviewRowSpacing;
+ }
+ return deviceProfile.overviewTaskThumbnailTopMarginPx / 2.0f;
+ }
+
private Rect getTaskBounds(TaskView taskView) {
int selectedPage = indexOfChild(taskView);
int primaryScroll = getPagedOrientationHandler().getPrimaryScroll(this);
int selectedPageScroll = getScrollForPage(selectedPage);
- boolean isTopRow = taskView != null && mTopRowIdSet.contains(taskView.getTaskViewId());
- Rect outRect = new Rect(mLastComputedTaskSize);
+ boolean isTopRow = mTopRowIdSet.contains(taskView.getTaskViewId());
+ Rect outRect = new Rect(
+ taskView.isGridTask() ? mLastComputedGridTaskSize : mLastComputedTaskSize);
outRect.offset(
-(primaryScroll - (selectedPageScroll + getOffsetFromScrollPosition(selectedPage))),
(int) (showAsGrid() && enableGridOnlyOverview() && !isTopRow
@@ -2371,10 +2387,6 @@
return mLastComputedGridTaskSize;
}
- public Rect getLastComputedCarouselTaskSize() {
- return mLastComputedCarouselTaskSize;
- }
-
/** Gets the task size for modal state. */
public void getModalTaskSize(Rect outRect) {
mSizeStrategy.calculateModalTaskSize(mContainer, mContainer.getDeviceProfile(), outRect,
@@ -2630,7 +2642,6 @@
setFocusedTaskViewId(INVALID_TASK_ID);
mAnyTaskHasBeenDismissed = false;
-
if (enableRefactorTaskThumbnail()) {
// TODO(b/353917593): RecentsView is never destroyed, so its dependencies need to
// be cleaned up during the reset, but re-created when RecentsView is "resumed".
@@ -2652,9 +2663,6 @@
}
runActionOnRemoteHandles(remoteTargetHandle ->
remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(false));
- if (!FeatureFlags.enableSplitContextually()) {
- resetFromSplitSelectionState();
- }
// These are relatively expensive and don't need to be done this frame (RecentsView isn't
// visible anyway), so defer by a frame to get off the critical path, e.g. app to home.
@@ -2664,6 +2672,7 @@
private void onReset() {
if (enableRefactorTaskThumbnail()) {
mRecentsViewModel.onReset();
+ removeAllViews();
}
unloadVisibleTaskData(TaskView.FLAG_UPDATE_ALL);
setCurrentPage(0);
@@ -2700,7 +2709,7 @@
}
@Nullable
- private TaskView getTaskViewFromTaskViewId(int taskViewId) {
+ TaskView getTaskViewFromTaskViewId(int taskViewId) {
if (taskViewId == -1) {
return null;
}
@@ -2778,14 +2787,12 @@
/**
* Called when a gesture from an app is starting.
*/
- public void onGestureAnimationStart(
- Task[] runningTasks, RotationTouchHelper rotationTouchHelper) {
+ public void onGestureAnimationStart(Task[] runningTasks) {
Log.d(TAG, "onGestureAnimationStart - runningTasks: " + Arrays.toString(runningTasks));
mActiveGestureRunningTasks = runningTasks;
// This needs to be called before the other states are set since it can create the task view
if (mOrientationState.setGestureActive(true)) {
- setLayoutRotation(rotationTouchHelper.getCurrentActiveRotation(),
- rotationTouchHelper.getDisplayRotation());
+ reapplyActiveRotation();
// Force update to ensure the initial task size is computed even if the orientation has
// not changed.
updateSizeAndPadding();
@@ -2902,8 +2909,6 @@
} else {
animatorSet.play(ObjectAnimator.ofFloat(this, RECENTS_GRID_PROGRESS, 1));
animatorSet.play(tvs.carouselScale.animateToValue(1));
- animatorSet.play(tvs.carouselPrimaryTranslation.animateToValue(0));
- animatorSet.play(tvs.carouselSecondaryTranslation.animateToValue(0));
animatorSet.play(tvs.taskPrimaryTranslation.animateToValue(
runningTaskPrimaryGridTranslation));
animatorSet.play(tvs.taskSecondaryTranslation.animateToValue(
@@ -3630,11 +3635,7 @@
InteractionJankMonitorWrapper.end(Cuj.CUJ_SPLIT_SCREEN_ENTER);
} else {
// If transition to split select was interrupted, clean up to prevent glitches
- if (FeatureFlags.enableSplitContextually()) {
- mSplitSelectStateController.resetState();
- } else {
- resetFromSplitSelectionState();
- }
+ mSplitSelectStateController.resetState();
InteractionJankMonitorWrapper.cancel(Cuj.CUJ_SPLIT_SCREEN_ENTER);
}
@@ -4015,6 +4016,10 @@
dismissedTaskView.setTranslationZ(0.1f);
}
loadVisibleTaskData(TaskView.FLAG_UPDATE_ALL);
+ if (!dismissingForSplitSelection) {
+ anim.addStartListener(() -> InteractionJankMonitorWrapper.begin(this,
+ Cuj.CUJ_LAUNCHER_OVERVIEW_TASK_DISMISS));
+ }
mPendingAnimation = anim;
final TaskView finalNextFocusedTaskView = nextFocusedTaskView;
final boolean finalCloseGapBetweenClearAll = closeGapBetweenClearAll;
@@ -4047,8 +4052,6 @@
} else {
removeTaskInternal(dismissedTaskView);
}
- announceForAccessibility(
- getResources().getString(R.string.task_view_closed));
mContainer.getStatsLogManager().logger()
.withItemInfo(dismissedTaskView.getFirstItemInfo())
.log(LAUNCHER_TASK_DISMISS_SWIPE_UP);
@@ -4066,7 +4069,7 @@
pageToSnapTo = indexOfChild(mClearAllButton);
} else if (isClearAllHidden) {
// Snap to focused task if clear all is hidden.
- pageToSnapTo = 0;
+ pageToSnapTo = indexOfChild(getFirstTaskView());
}
} else {
// Get the id of the task view we will snap to based on the current
@@ -4084,7 +4087,7 @@
} else {
// Won't focus next task in split select, so snap to the
// first task.
- pageToSnapTo = 0;
+ pageToSnapTo = indexOfChild(getFirstTaskView());
calculateScrollDiff = false;
}
} else {
@@ -4155,16 +4158,15 @@
if (showAsGrid) {
// Rebalance tasks in the grid
- int highestVisibleTaskIndex = getHighestVisibleTaskIndex();
- if (highestVisibleTaskIndex < Integer.MAX_VALUE) {
- final TaskView taskView = requireTaskViewAt(
- highestVisibleTaskIndex);
-
+ TaskView highestVisibleTaskView = getHighestVisibleTaskView();
+ if (highestVisibleTaskView != null) {
boolean shouldRebalance;
int screenStart = getPagedOrientationHandler().getPrimaryScroll(
RecentsView.this);
- int taskStart = getPagedOrientationHandler().getChildStart(taskView)
- + (int) taskView.getOffsetAdjustment(/*gridEnabled=*/ true);
+ int taskStart = getPagedOrientationHandler().getChildStart(
+ highestVisibleTaskView)
+ + (int) highestVisibleTaskView.getOffsetAdjustment(
+ /*gridEnabled=*/true);
// Rebalance only if there is a maximum gap between the task and the
// screen's edge; this ensures that rebalanced tasks are outside the
@@ -4177,7 +4179,7 @@
RecentsView.this);
int taskSize = (int) (
getPagedOrientationHandler().getMeasuredSize(
- taskView) * taskView
+ highestVisibleTaskView) * highestVisibleTaskView
.getSizeAdjustment(/*fullscreenEnabled=*/
false));
int taskEnd = taskStart + taskSize;
@@ -4186,7 +4188,7 @@
}
if (shouldRebalance) {
- updateGridProperties(taskView);
+ updateGridProperties(highestVisibleTaskView);
updateScrollSynchronously();
}
}
@@ -4233,6 +4235,12 @@
onDismissAnimationEnds();
mPendingAnimation = null;
mTaskViewsDismissPrimaryTranslations.clear();
+
+ if (!dismissingForSplitSelection && success) {
+ InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_OVERVIEW_TASK_DISMISS);
+ } else if (!dismissingForSplitSelection) {
+ InteractionJankMonitorWrapper.cancel(Cuj.CUJ_LAUNCHER_OVERVIEW_TASK_DISMISS);
+ }
}
});
}
@@ -4358,7 +4366,7 @@
/**
* Returns all the tasks in the top row, without the focused task
*/
- private IntArray getTopRowIdArray() {
+ IntArray getTopRowIdArray() {
if (mTopRowIdSet.isEmpty()) {
return new IntArray(0);
}
@@ -4375,7 +4383,7 @@
/**
* Returns all the tasks in the bottom row, without the focused task
*/
- private IntArray getBottomRowIdArray() {
+ IntArray getBottomRowIdArray() {
int bottomRowIdArraySize = getBottomRowTaskCountForTablet();
if (bottomRowIdArraySize <= 0) {
return new IntArray(0);
@@ -4394,12 +4402,12 @@
* Iterate the grid by columns instead of by TaskView index, starting after the focused task and
* up to the last balanced column.
*
- * @return the highest visible TaskView index between both rows
+ * @return the highest visible TaskView between both rows
*/
- private int getHighestVisibleTaskIndex() {
- if (mTopRowIdSet.isEmpty()) return Integer.MAX_VALUE; // return earlier
+ private TaskView getHighestVisibleTaskView() {
+ if (mTopRowIdSet.isEmpty()) return null; // return earlier
- int lastVisibleIndex = Integer.MAX_VALUE;
+ TaskView lastVisibleTaskView = null;
IntArray topRowIdArray = getTopRowIdArray();
IntArray bottomRowIdArray = getBottomRowIdArray();
int balancedColumns = Math.min(bottomRowIdArray.size(), topRowIdArray.size());
@@ -4409,13 +4417,14 @@
if (isTaskViewVisible(topTask)) {
TaskView bottomTask = getTaskViewFromTaskViewId(bottomRowIdArray.get(i));
- lastVisibleIndex = Math.max(indexOfChild(topTask), indexOfChild(bottomTask));
- } else if (lastVisibleIndex < Integer.MAX_VALUE) {
+ lastVisibleTaskView =
+ indexOfChild(topTask) > indexOfChild(bottomTask) ? topTask : bottomTask;
+ } else if (lastVisibleTaskView != null) {
break;
}
}
- return lastVisibleIndex;
+ return lastVisibleTaskView;
}
private void removeTaskInternal(@NonNull TaskView dismissedTaskView) {
@@ -4673,6 +4682,12 @@
}
}
+ public void reapplyActiveRotation() {
+ RotationTouchHelper rotationTouchHelper = RotationTouchHelper.INSTANCE.get(getContext());
+ setLayoutRotation(rotationTouchHelper.getCurrentActiveRotation(),
+ rotationTouchHelper.getDisplayRotation());
+ }
+
public void setLayoutRotation(int touchRotation, int displayRotation) {
if (mOrientationState.update(touchRotation, displayRotation)) {
updateOrientationHandler();
@@ -4731,14 +4746,6 @@
}
/**
- * A version of {@link #getTaskViewAt} when the caller is sure about the input index.
- */
- @NonNull
- private TaskView requireTaskViewAt(int index) {
- return Objects.requireNonNull(getTaskViewAt(index));
- }
-
- /**
* Returns iterable [TaskView] children.
*/
public RecentsViewUtils.TaskViewsIterable getTaskViews() {
@@ -4803,14 +4810,7 @@
mTempPointF.set(mLastComputedTaskSize.centerX(), mLastComputedTaskSize.bottom);
}
} else {
- // Only update pivot when it is tablet and not in grid yet, so the pivot is correct
- // for non-current tasks when swiping up to overview
- if (enableGridOnlyOverview() && mContainer.getDeviceProfile().isTablet
- && !mOverviewGridEnabled) {
- mTempRect.set(mLastComputedCarouselTaskSize);
- } else {
- mTempRect.set(mLastComputedTaskSize);
- }
+ mTempRect.set(mLastComputedTaskSize);
getPagedViewOrientedState().getFullScreenScaleAndPivot(mTempRect,
mContainer.getDeviceProfile(), mTempPointF);
}
@@ -4921,10 +4921,12 @@
&& child instanceof DesktopTaskView;
float totalTranslationX = (skipTranslationOffset ? 0f : translation) + modalTranslation
+ carouselHiddenOffsetSize;
- FloatProperty translationPropertyX = child instanceof TaskView
- ? ((TaskView) child).getPrimaryTaskOffsetTranslationProperty()
- : getPagedOrientationHandler().getPrimaryViewTranslate();
- translationPropertyX.set(child, totalTranslationX);
+ if (child instanceof TaskView taskView) {
+ taskView.getPrimaryTaskOffsetTranslationProperty().set(taskView, totalTranslationX);
+ } else {
+ getPagedOrientationHandler().getPrimaryViewTranslate().set(child,
+ totalTranslationX);
+ }
if (mEnableDrawingLiveTile && i == getRunningTaskIndex()) {
runActionOnRemoteHandles(
remoteTargetHandle -> remoteTargetHandle.getTaskViewSimulator()
@@ -4932,11 +4934,11 @@
redrawLiveTile();
}
- if (showAsGrid && enableGridOnlyOverview() && child instanceof TaskView) {
- float totalTranslationY = getVerticalOffsetSize(i, modalOffset);
- FloatProperty translationPropertyY =
- ((TaskView) child).getSecondaryTaskOffsetTranslationProperty();
- translationPropertyY.set(child, totalTranslationY);
+ if (showAsGrid && enableGridOnlyOverview() && child instanceof TaskView taskView) {
+ float totalTranslationY = getVerticalOffsetSize(taskView, modalOffset);
+ FloatProperty<TaskView> translationPropertyY =
+ taskView.getSecondaryTaskOffsetTranslationProperty();
+ translationPropertyY.set(taskView, totalTranslationY);
}
}
updateCurveProperties();
@@ -5052,7 +5054,7 @@
*
* @param offsetProgress From 0 to 1 where 0 means no offset and 1 means offset offscreen.
*/
- private float getVerticalOffsetSize(int childIndex, float offsetProgress) {
+ private float getVerticalOffsetSize(TaskView taskView, float offsetProgress) {
if (offsetProgress == 0 || !(showAsGrid() && enableGridOnlyOverview())
|| mSelectedTask == null) {
// Don't bother calculating everything below if we won't offset vertically.
@@ -5060,11 +5062,10 @@
}
// First, get the position of the task relative to the top row.
- TaskView child = getTaskViewAt(childIndex);
- Rect taskPosition = getTaskBounds(child);
+ Rect taskPosition = getTaskBounds(taskView);
boolean isSelectedTaskTopRow = mTopRowIdSet.contains(mSelectedTask.getTaskViewId());
- boolean isChildTopRow = mTopRowIdSet.contains(child.getTaskViewId());
+ boolean isChildTopRow = mTopRowIdSet.contains(taskView.getTaskViewId());
// Whether the task should be shifted to the top.
boolean isTopShift = !isSelectedTaskTopRow && isChildTopRow;
boolean isBottomShift = isSelectedTaskTopRow && !isChildTopRow;
@@ -5362,11 +5363,7 @@
mSplitSelectStateController.launchSplitTasks(
aBoolean1 -> {
InteractionJankMonitorWrapper.end(Cuj.CUJ_SPLIT_SCREEN_ENTER);
- if (FeatureFlags.enableSplitContextually()) {
- mSplitSelectStateController.resetState();
- } else {
- resetFromSplitSelectionState();
- }
+ mSplitSelectStateController.resetState();
});
});
@@ -5388,17 +5385,14 @@
@SuppressLint("WrongCall")
protected void resetFromSplitSelectionState() {
- if (mSplitSelectSource != null || mSplitHiddenTaskViewIndex != -1 ||
- FeatureFlags.enableSplitContextually()) {
- safeRemoveDragLayerView(mSplitSelectStateController.getFirstFloatingTaskView());
- safeRemoveDragLayerView(mSecondFloatingTaskView);
- safeRemoveDragLayerView(mSplitSelectStateController.getSplitInstructionsView());
- safeRemoveDragLayerView(mSplitScrim);
- mSecondFloatingTaskView = null;
- mSplitSelectSource = null;
- mSplitSelectStateController.getSplitAnimationController()
- .removeSplitInstructionsView(mContainer);
- }
+ safeRemoveDragLayerView(mSplitSelectStateController.getFirstFloatingTaskView());
+ safeRemoveDragLayerView(mSecondFloatingTaskView);
+ safeRemoveDragLayerView(mSplitSelectStateController.getSplitInstructionsView());
+ safeRemoveDragLayerView(mSplitScrim);
+ mSecondFloatingTaskView = null;
+ mSplitSelectSource = null;
+ mSplitSelectStateController.getSplitAnimationController()
+ .removeSplitInstructionsView(mContainer);
if (mSecondSplitHiddenView != null) {
mSecondSplitHiddenView.setThumbnailVisibility(VISIBLE, INVALID_TASK_ID);
@@ -5410,11 +5404,6 @@
setTaskViewsPrimarySplitTranslation(0);
setTaskViewsSecondarySplitTranslation(0);
- if (!FeatureFlags.enableSplitContextually()) {
- // When flag is on, this method gets called from resetState() call below, let's avoid
- // infinite recursion today
- mSplitSelectStateController.resetState();
- }
if (mSplitHiddenTaskViewIndex == -1) {
return;
}
@@ -5433,6 +5422,13 @@
mSplitHiddenTaskViewIndex = -1;
if (mSplitHiddenTaskView != null) {
mSplitHiddenTaskView.setThumbnailVisibility(VISIBLE, INVALID_TASK_ID);
+ // mSplitHiddenTaskView is set when split select animation starts. The TaskView is only
+ // removed when when the animation finishes. So in the case of overview being dismissed
+ // during the animation, we should not call clearAndRecycleTaskView() because it has
+ // not been removed yet.
+ if (mSplitHiddenTaskView.getParent() == null) {
+ clearAndRecycleTaskView(mSplitHiddenTaskView);
+ }
mSplitHiddenTaskView = null;
}
}
@@ -5484,7 +5480,7 @@
firstFloatingTaskView.update(mTempRectF, /*progress=*/1f);
RecentsPagedOrientationHandler orientationHandler = getPagedOrientationHandler();
- Pair<FloatProperty<RecentsView>, FloatProperty<RecentsView>> taskViewsFloat =
+ Pair<FloatProperty<RecentsView<?, ?>>, FloatProperty<RecentsView<?, ?>>> taskViewsFloat =
orientationHandler.getSplitSelectTaskOffset(
TASK_PRIMARY_SPLIT_TRANSLATION, TASK_SECONDARY_SPLIT_TRANSLATION,
mContainer.getDeviceProfile());
@@ -5506,15 +5502,8 @@
mClearAllButtonDeadZoneRect.inset(-getPaddingRight() / 2, -verticalMargin);
}
- // Get the deadzone rect between the task views
- mTaskViewDeadZoneRect.setEmpty();
- if (hasTaskViews()) {
- final View firstTaskView = getFirstTaskView();
- mUtils.getLastTaskView().getHitRect(mTaskViewDeadZoneRect);
- mTaskViewDeadZoneRect.union(firstTaskView.getLeft(), firstTaskView.getTop(),
- firstTaskView.getRight(),
- firstTaskView.getBottom());
- }
+ mUtils.updateTaskViewDeadZoneRect(mTaskViewDeadZoneRect, mTopRowDeadZoneRect,
+ mBottomRowDeadZoneRect);
}
private void updateEmptyStateUi(boolean sizeChanged) {
@@ -5654,19 +5643,10 @@
* Returns the scale up required on the view, so that it coves the screen completely
*/
public float getMaxScaleForFullScreen() {
- if (enableGridOnlyOverview() && mContainer.getDeviceProfile().isTablet
- && !mOverviewGridEnabled) {
- if (mLastComputedCarouselTaskSize.isEmpty()) {
- mSizeStrategy.calculateCarouselTaskSize(mContainer, mContainer.getDeviceProfile(),
- mLastComputedCarouselTaskSize, getPagedOrientationHandler());
- }
- mTempRect.set(mLastComputedCarouselTaskSize);
- } else {
- if (mLastComputedTaskSize.isEmpty()) {
- getTaskSize(mLastComputedTaskSize);
- }
- mTempRect.set(mLastComputedTaskSize);
+ if (mLastComputedTaskSize.isEmpty()) {
+ getTaskSize(mLastComputedTaskSize);
}
+ mTempRect.set(mLastComputedTaskSize);
return getPagedViewOrientedState().getFullScreenScaleAndPivot(
mTempRect, mContainer.getDeviceProfile(), mTempPointF);
}
@@ -5857,7 +5837,8 @@
if (recentsAnimationTargets.hasDesktopTasks(mContext)) {
gluer = new RemoteTargetGluer(getContext(), getSizeStrategy(), recentsAnimationTargets,
true /* forDesktop */);
- mRemoteTargetHandles = gluer.assignTargetsForDesktop(recentsAnimationTargets);
+ mRemoteTargetHandles = gluer.assignTargetsForDesktop(
+ recentsAnimationTargets, /* transitionInfo= */ null);
} else {
gluer = new RemoteTargetGluer(getContext(), getSizeStrategy(), recentsAnimationTargets,
false);
@@ -6804,6 +6785,8 @@
}
mDesktopRecentsTransitionController.moveToDesktop(taskContainer, transitionSource);
+ // TODO(b/387471509): Invoke successCallback after actual transition completion of
+ // overview menu to desktop
successCallback.run();
}
@@ -6845,6 +6828,14 @@
}
}
+ private int getFontWeight() {
+ int fontWeightAdjustment = getResources().getConfiguration().fontWeightAdjustment;
+ if (fontWeightAdjustment != Configuration.FONT_WEIGHT_ADJUSTMENT_UNDEFINED) {
+ return Typeface.Builder.NORMAL_WEIGHT + fontWeightAdjustment;
+ }
+ return Typeface.Builder.NORMAL_WEIGHT;
+ }
+
public interface TaskLaunchListener {
void onTaskLaunched();
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java b/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
index a1d22fe..b1a4808 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
@@ -29,7 +29,6 @@
import com.android.launcher3.BaseActivity;
import com.android.launcher3.logger.LauncherAtom;
-import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.taskbar.TaskbarUIController;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.ScrimView;
@@ -209,9 +208,6 @@
.build());
}
- @Nullable
- DesktopVisibilityController getDesktopVisibilityController();
-
void setTaskbarUIController(@Nullable TaskbarUIController taskbarUIController);
@Nullable TaskbarUIController getTaskbarUIController();
diff --git a/quickstep/src/com/android/quickstep/views/RecentsViewModelHelper.kt b/quickstep/src/com/android/quickstep/views/RecentsViewModelHelper.kt
index 87771c6..ff711da 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsViewModelHelper.kt
+++ b/quickstep/src/com/android/quickstep/views/RecentsViewModelHelper.kt
@@ -32,8 +32,8 @@
private val recentsCoroutineScope: CoroutineScope,
private val dispatcherProvider: DispatcherProvider,
) {
- fun onDetachedFromWindow() {
- recentsCoroutineScope.cancel("RecentsView detaching from window")
+ fun onDestroy() {
+ recentsCoroutineScope.cancel("RecentsView is being destroyed")
}
fun switchToScreenshot(
@@ -44,10 +44,12 @@
// Update recentsViewModel and apply the thumbnailOverride ASAP, before waiting inside
// viewAttachedScope.
recentsViewModel.setRunningTaskShowScreenshot(true)
- recentsCoroutineScope.launch(dispatcherProvider.main) {
+ recentsCoroutineScope.launch(dispatcherProvider.background) {
recentsViewModel.waitForRunningTaskShowScreenshotToUpdate()
recentsViewModel.waitForThumbnailsToUpdate(updatedThumbnails)
- withContext(Dispatchers.Main) { ViewUtils.postFrameDrawn(taskView, onFinishRunnable) }
+ withContext(Dispatchers.Main.immediate) {
+ ViewUtils.postFrameDrawn(taskView, onFinishRunnable)
+ }
}
}
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt b/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt
index 3ac3349..dcb954a 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt
+++ b/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt
@@ -16,9 +16,11 @@
package com.android.quickstep.views
+import android.graphics.Rect
import android.view.View
import androidx.core.view.children
import com.android.launcher3.Flags.enableLargeDesktopWindowingTile
+import com.android.launcher3.util.IntArray
import com.android.quickstep.util.GroupTask
import com.android.quickstep.views.RecentsView.RUNNING_TASK_ATTACH_ALPHA
import com.android.systemui.shared.recents.model.ThumbnailData
@@ -169,4 +171,78 @@
FULL_SCREEN,
DESKTOP,
}
+
+ /** Returns true if there are at least one TaskView has been added to the RecentsView. */
+ fun hasTaskViews() = taskViews.any()
+
+ private fun getRowRect(firstView: View?, lastView: View?, outRowRect: Rect) {
+ outRowRect.setEmpty()
+ firstView?.let {
+ it.getHitRect(TEMP_RECT)
+ outRowRect.union(TEMP_RECT)
+ }
+ lastView?.let {
+ it.getHitRect(TEMP_RECT)
+ outRowRect.union(TEMP_RECT)
+ }
+ }
+
+ private fun getRowRect(rowTaskViewIds: IntArray, outRowRect: Rect) {
+ if (rowTaskViewIds.isEmpty) {
+ outRowRect.setEmpty()
+ return
+ }
+ getRowRect(
+ recentsView.getTaskViewFromTaskViewId(rowTaskViewIds.get(0)),
+ recentsView.getTaskViewFromTaskViewId(rowTaskViewIds.get(rowTaskViewIds.size() - 1)),
+ outRowRect,
+ )
+ }
+
+ fun updateTaskViewDeadZoneRect(
+ outTaskViewRowRect: Rect,
+ outTopRowRect: Rect,
+ outBottomRowRect: Rect,
+ ) {
+ if (!(recentsView.mContainer as RecentsViewContainer).deviceProfile.isTablet) {
+ getRowRect(getFirstTaskView(), getLastTaskView(), outTaskViewRowRect)
+ return
+ }
+ getRowRect(getFirstLargeTaskView(), getLastLargeTaskView(), outTaskViewRowRect)
+ getRowRect(recentsView.getTopRowIdArray(), outTopRowRect)
+ getRowRect(recentsView.getBottomRowIdArray(), outBottomRowRect)
+
+ // Expand large tile Rect to include space between top/bottom row.
+ val nonEmptyRowRect =
+ when {
+ !outTopRowRect.isEmpty -> outTopRowRect
+ !outBottomRowRect.isEmpty -> outBottomRowRect
+ else -> return
+ }
+ if (recentsView.isRtl) {
+ if (outTaskViewRowRect.left > nonEmptyRowRect.right) {
+ outTaskViewRowRect.left = nonEmptyRowRect.right
+ }
+ } else {
+ if (outTaskViewRowRect.right < nonEmptyRowRect.left) {
+ outTaskViewRowRect.right = nonEmptyRowRect.left
+ }
+ }
+
+ // Expand the shorter row Rect to include the space between the 2 rows.
+ if (outTopRowRect.isEmpty || outBottomRowRect.isEmpty) return
+ if (outTopRowRect.width() <= outBottomRowRect.width()) {
+ if (outTopRowRect.bottom < outBottomRowRect.top) {
+ outTopRowRect.bottom = outBottomRowRect.top
+ }
+ } else {
+ if (outBottomRowRect.top > outTopRowRect.bottom) {
+ outBottomRowRect.top = outTopRowRect.bottom
+ }
+ }
+ }
+
+ companion object {
+ val TEMP_RECT = Rect()
+ }
}
diff --git a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
index f6393e4..0d80f6a 100644
--- a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
+++ b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
@@ -126,33 +126,31 @@
TextView cancelTextView = findViewById(R.id.split_instructions_text_cancel);
TextView instructionTextView = findViewById(R.id.split_instructions_text);
- if (FeatureFlags.enableSplitContextually()) {
- cancelTextView.setVisibility(VISIBLE);
- cancelTextView.setOnClickListener((v) -> exitSplitSelection());
- instructionTextView.setText(R.string.toast_contextual_split_select_app);
+ cancelTextView.setVisibility(VISIBLE);
+ cancelTextView.setOnClickListener((v) -> exitSplitSelection());
+ instructionTextView.setText(R.string.toast_contextual_split_select_app);
- // After layout, expand touch target of cancel button to meet minimum a11y measurements.
- post(() -> {
- int minTouchSize = getResources()
- .getDimensionPixelSize(settingslib_preferred_minimum_touch_target);
- Rect r = new Rect();
- cancelTextView.getHitRect(r);
+ // After layout, expand touch target of cancel button to meet minimum a11y measurements.
+ post(() -> {
+ int minTouchSize = getResources()
+ .getDimensionPixelSize(settingslib_preferred_minimum_touch_target);
+ Rect r = new Rect();
+ cancelTextView.getHitRect(r);
- if (r.width() < minTouchSize) {
- // add 1 to ensure ceiling on int division
- int expandAmount = (minTouchSize + 1 - r.width()) / 2;
- r.left -= expandAmount;
- r.right += expandAmount;
- }
- if (r.height() < minTouchSize) {
- int expandAmount = (minTouchSize + 1 - r.height()) / 2;
- r.top -= expandAmount;
- r.bottom += expandAmount;
- }
+ if (r.width() < minTouchSize) {
+ // add 1 to ensure ceiling on int division
+ int expandAmount = (minTouchSize + 1 - r.width()) / 2;
+ r.left -= expandAmount;
+ r.right += expandAmount;
+ }
+ if (r.height() < minTouchSize) {
+ int expandAmount = (minTouchSize + 1 - r.height()) / 2;
+ r.top -= expandAmount;
+ r.bottom += expandAmount;
+ }
- setTouchDelegate(new TouchDelegate(r, cancelTextView));
- });
- }
+ setTouchDelegate(new TouchDelegate(r, cancelTextView));
+ });
// Set accessibility title, will be announced by a11y tools.
instructionTextView.setAccessibilityPaneTitle(instructionTextView.getText());
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailViewHeader.kt b/quickstep/src/com/android/quickstep/views/TaskThumbnailViewHeader.kt
new file mode 100644
index 0000000..9eb294a
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailViewHeader.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.views
+
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.FrameLayout
+import android.widget.ImageView
+import android.widget.TextView
+import com.android.launcher3.R
+import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.ThumbnailHeader
+
+class TaskThumbnailViewHeader
+@JvmOverloads
+constructor(context: Context, attrs: AttributeSet? = null) : FrameLayout(context, attrs) {
+
+ private val headerTitleView: TextView by lazy { findViewById(R.id.header_app_title) }
+ private val headerIconView: ImageView by lazy { findViewById(R.id.header_app_icon) }
+
+ fun setHeader(header: ThumbnailHeader) {
+ headerTitleView.setText(header.title)
+ headerIconView.setImageDrawable(header.icon)
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.kt b/quickstep/src/com/android/quickstep/views/TaskView.kt
index 0dbad70..dc849f3 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskView.kt
@@ -165,10 +165,7 @@
* Returns addition of translationX that is persistent (e.g. fullscreen and grid), and does
* not change according to a temporary state (e.g. task offset).
*/
- get() =
- (getNonGridTrans(nonGridTranslationX) +
- getGridTrans(this.gridTranslationX) +
- getNonGridTrans(nonGridPivotTranslationX))
+ get() = (getNonGridTrans(nonGridTranslationX) + getGridTrans(this.gridTranslationX))
protected val persistentTranslationY: Float
/**
@@ -352,12 +349,6 @@
applyTranslationX()
}
- protected var nonGridPivotTranslationX = 0f
- set(value) {
- field = value
- applyTranslationX()
- }
-
// Used when in SplitScreenSelectState
private var splitSelectTranslationY = 0f
set(value) {
@@ -633,7 +624,11 @@
with(info) {
// Only make actions available if the app icon menu is visible to the user.
// When modalness is >0, the user is in select mode and the icon menu is hidden.
- if (modalness == 0f) {
+ // When split selection is active, they should only be able to select the app and not
+ // take any other action.
+ val shouldPopulateAccessibilityMenu =
+ modalness == 0f && recentsView?.isSplitSelectionActive == false
+ if (shouldPopulateAccessibilityMenu) {
addAction(
AccessibilityAction(
R.id.action_close,
@@ -797,11 +792,7 @@
* Updates TaskView scaling and translation required to support variable width if enabled, while
* ensuring TaskView fits into screen in fullscreen.
*/
- open fun updateTaskSize(
- lastComputedTaskSize: Rect,
- lastComputedGridTaskSize: Rect,
- lastComputedCarouselTaskSize: Rect,
- ) {
+ open fun updateTaskSize(lastComputedTaskSize: Rect, lastComputedGridTaskSize: Rect) {
val thumbnailPadding = container.deviceProfile.overviewTaskThumbnailTopMarginPx
val taskWidth = lastComputedTaskSize.width()
val taskHeight = lastComputedTaskSize.height()
@@ -829,12 +820,7 @@
expectedHeight = boxHeight + thumbnailPadding
// Scale to to fit task Rect.
- nonGridScale =
- if (enableGridOnlyOverview()) {
- lastComputedCarouselTaskSize.width() / taskWidth.toFloat()
- } else {
- taskWidth / boxWidth.toFloat()
- }
+ nonGridScale = taskWidth / boxWidth.toFloat()
// Align to top of task Rect.
boxTranslationY = (expectedHeight - thumbnailPadding - taskHeight) / 2.0f
@@ -1587,7 +1573,6 @@
gridTranslationX = 0f
gridTranslationY = 0f
boxTranslationY = 0f
- nonGridPivotTranslationX = 0f
taskContainers.forEach {
it.snapshotView.translationX = 0f
it.snapshotView.translationY = 0f
diff --git a/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewScreenshotTest.kt b/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewScreenshotTest.kt
index 49fe614..a76f83c 100644
--- a/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewScreenshotTest.kt
+++ b/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewScreenshotTest.kt
@@ -20,6 +20,8 @@
import android.view.LayoutInflater
import com.android.launcher3.R
import com.android.quickstep.recents.di.RecentsDependencies
+import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.BackgroundOnly
+import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Uninitialized
import com.android.quickstep.task.viewmodel.TaskThumbnailViewModel
import com.google.android.apps.nexuslauncher.imagecomparison.goldenpathmanager.ViewScreenshotGoldenPathManager
import org.junit.Rule
@@ -46,7 +48,7 @@
private val taskThumbnailViewModel = FakeTaskThumbnailViewModel()
@Test
- fun taskThumbnailView_uninitialized() {
+ fun taskThumbnailView_uninitializedByDefault() {
screenshotRule.screenshotTest("taskThumbnailView_uninitialized") { activity ->
activity.actionBar?.hide()
createTaskThumbnailView(activity)
@@ -54,10 +56,39 @@
}
@Test
+ fun taskThumbnailView_resetsToUninitialized() {
+ screenshotRule.screenshotTest("taskThumbnailView_uninitialized") { activity ->
+ activity.actionBar?.hide()
+ val taskThumbnailView = createTaskThumbnailView(activity)
+ taskThumbnailViewModel.uiState.value = BackgroundOnly(Color.YELLOW)
+ taskThumbnailViewModel.uiState.value = Uninitialized
+ taskThumbnailView
+ }
+ }
+
+ @Test
+ fun taskThumbnailView_recyclesToUninitialized() {
+ screenshotRule.screenshotTest(
+ "taskThumbnailView_uninitialized",
+ viewProvider = { activity ->
+ activity.actionBar?.hide()
+ val taskThumbnailView = createTaskThumbnailView(activity)
+ taskThumbnailViewModel.uiState.value = BackgroundOnly(Color.YELLOW)
+ taskThumbnailView
+ },
+ checkView = { _, taskThumbnailView ->
+ // Call onRecycle() after View is attached (end of block above)
+ (taskThumbnailView as TaskThumbnailView).onRecycle()
+ false
+ },
+ )
+ }
+
+ @Test
fun taskThumbnailView_backgroundOnly() {
screenshotRule.screenshotTest("taskThumbnailView_backgroundOnly") { activity ->
activity.actionBar?.hide()
- taskThumbnailViewModel.uiState.value = TaskThumbnailUiState.BackgroundOnly(Color.YELLOW)
+ taskThumbnailViewModel.uiState.value = BackgroundOnly(Color.YELLOW)
createTaskThumbnailView(activity)
}
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarControllerTestUtil.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarControllerTestUtil.kt
index 6e2f74a..0e066cd 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarControllerTestUtil.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarControllerTestUtil.kt
@@ -17,15 +17,16 @@
package com.android.launcher3.taskbar
import android.content.Context
-import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import com.android.launcher3.ConstantItem
import com.android.launcher3.LauncherPrefs
+import com.android.launcher3.util.Executors.MAIN_EXECUTOR
+import com.android.launcher3.util.TestUtil
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
object TaskbarControllerTestUtil {
inline fun runOnMainSync(crossinline runTest: () -> Unit) {
- getInstrumentation().runOnMainSync { runTest() }
+ TestUtil.runOnExecutorSync(MAIN_EXECUTOR) { runTest() }
}
/** Returns a property to read/write the value of a [ConstantItem]. */
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
index c682990..a8f3500 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
@@ -40,7 +40,6 @@
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
-import com.android.launcher3.contextualeducation.ContextualEduStatsManager;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarNavButtonCallbacks;
import com.android.quickstep.SystemUiProxy;
@@ -65,9 +64,6 @@
SystemUiProxy mockSystemUiProxy;
@Mock
- ContextualEduStatsManager mockContextualEduStatsManager;
-
- @Mock
TouchInteractionService mockService;
@Mock
Handler mockHandler;
@@ -118,7 +114,6 @@
mockService,
mCallbacks,
mockSystemUiProxy,
- mockContextualEduStatsManager,
mockHandler,
mockContextualSearchInvoker);
}
@@ -132,8 +127,8 @@
@Test
public void testPressBack_updateContextualEduData() {
mNavButtonController.onButtonClick(BUTTON_BACK, mockView);
- verify(mockContextualEduStatsManager, times(1))
- .updateEduStats(/* isTrackpad= */ eq(false), eq(GestureType.BACK));
+ verify(mockSystemUiProxy, times(1))
+ .updateContextualEduStats(/* isTrackpad= */ eq(false), eq(GestureType.BACK));
}
@Test
@@ -223,8 +218,8 @@
@Test
public void testPressHome_updateContextualEduData() {
mNavButtonController.onButtonClick(BUTTON_HOME, mockView);
- verify(mockContextualEduStatsManager, times(1))
- .updateEduStats(/* isTrackpad= */ eq(false), eq(GestureType.HOME));
+ verify(mockSystemUiProxy, times(1))
+ .updateContextualEduStats(/* isTrackpad= */ eq(false), eq(GestureType.HOME));
}
@Test
@@ -236,8 +231,8 @@
@Test
public void testPressRecents_updateContextualEduData() {
mNavButtonController.onButtonClick(BUTTON_RECENTS, mockView);
- verify(mockContextualEduStatsManager, times(1))
- .updateEduStats(/* isTrackpad= */ eq(false), eq(GestureType.OVERVIEW));
+ verify(mockSystemUiProxy, times(1))
+ .updateContextualEduStats(/* isTrackpad= */ eq(false), eq(GestureType.OVERVIEW));
}
@Test
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt
index 3121c56..9ca8a1b 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt
@@ -88,7 +88,8 @@
@get:Rule(order = 4) val animatorTestRule = AnimatorTestRule(this)
- @get:Rule(order = 5) val taskbarUnitTestRule = TaskbarUnitTestRule(this, context)
+ @get:Rule(order = 5)
+ val taskbarUnitTestRule = TaskbarUnitTestRule(this, context, this::onControllersInitialized)
@InjectController lateinit var taskbarViewController: TaskbarViewController
@InjectController lateinit var recentAppsController: TaskbarRecentAppsController
@@ -97,18 +98,29 @@
private var desktopTaskListener: IDesktopTaskListener? = null
- @Before
- fun ensureRunningAppsShowing() {
+ private var currentControllerInitCallback: () -> Unit = {}
+ set(value) {
+ runOnMainSync { value.invoke() }
+ field = value
+ }
+
+ private fun onControllersInitialized() {
runOnMainSync {
if (!recentAppsController.canShowRunningApps) {
recentAppsController.onDestroy()
recentAppsController.canShowRunningApps = true
recentAppsController.init(taskbarUnitTestRule.activityContext.controllers)
}
- recentsModel.resolvePendingTaskRequests()
+
+ currentControllerInitCallback.invoke()
}
}
+ @Before
+ fun ensureRunningAppsShowing() {
+ runOnMainSync { recentsModel.resolvePendingTaskRequests() }
+ }
+
@Test
@TaskbarMode(PINNED)
fun testTaskbarWithMaxNumIcons_pinned() {
@@ -199,7 +211,7 @@
var initialMaxNumIconViews = maxNumberOfTaskbarIcons
assertThat(initialMaxNumIconViews).isGreaterThan(0)
- runOnMainSync { bubbleBarViewController.setHiddenForBubbles(false) }
+ currentControllerInitCallback = { bubbleBarViewController.setHiddenForBubbles(false) }
val maxNumIconViews = addRunningAppsAndVerifyOverflowState(2)
assertThat(maxNumIconViews).isLessThan(initialMaxNumIconViews)
@@ -213,7 +225,7 @@
var initialMaxNumIconViews = maxNumberOfTaskbarIcons
assertThat(initialMaxNumIconViews).isGreaterThan(0)
- runOnMainSync { bubbleBarViewController.setHiddenForBubbles(false) }
+ currentControllerInitCallback = { bubbleBarViewController.setHiddenForBubbles(false) }
val maxNumIconViews = addRunningAppsAndVerifyOverflowState(2)
assertThat(maxNumIconViews).isLessThan(initialMaxNumIconViews)
@@ -231,7 +243,7 @@
fun testBubbleBarReducesTaskbarMaxNumIcons_transientBubbleInitiallyStashed() {
var initialMaxNumIconViews = maxNumberOfTaskbarIcons
assertThat(initialMaxNumIconViews).isGreaterThan(0)
- runOnMainSync {
+ currentControllerInitCallback = {
bubbleStashController.stashBubbleBarImmediate()
bubbleBarViewController.setHiddenForBubbles(false)
}
@@ -250,7 +262,7 @@
@Test
@TaskbarMode(TRANSIENT)
fun testStashingBubbleBarMaintainsMaxNumIcons_transient() {
- runOnMainSync { bubbleBarViewController.setHiddenForBubbles(false) }
+ currentControllerInitCallback = { bubbleBarViewController.setHiddenForBubbles(false) }
val initialNumIcons = currentNumberOfTaskbarIcons
val maxNumIconViews = addRunningAppsAndVerifyOverflowState(2)
@@ -264,15 +276,13 @@
@Test
@TaskbarMode(PINNED)
fun testHidingBubbleBarIncreasesMaxNumIcons_pinned() {
- runOnMainSync { bubbleBarViewController.setHiddenForBubbles(false) }
+ currentControllerInitCallback = { bubbleBarViewController.setHiddenForBubbles(false) }
val initialNumIcons = currentNumberOfTaskbarIcons
val initialMaxNumIconViews = addRunningAppsAndVerifyOverflowState(5)
- runOnMainSync {
- bubbleBarViewController.setHiddenForBubbles(true)
- animatorTestRule.advanceTimeBy(150)
- }
+ currentControllerInitCallback = { bubbleBarViewController.setHiddenForBubbles(true) }
+ runOnMainSync { animatorTestRule.advanceTimeBy(150) }
val maxNumIconViews = maxNumberOfTaskbarIcons
assertThat(maxNumIconViews).isGreaterThan(initialMaxNumIconViews)
@@ -285,15 +295,13 @@
@Test
@TaskbarMode(TRANSIENT)
fun testHidingBubbleBarIncreasesMaxNumIcons_transient() {
- runOnMainSync { bubbleBarViewController.setHiddenForBubbles(false) }
+ currentControllerInitCallback = { bubbleBarViewController.setHiddenForBubbles(false) }
val initialNumIcons = currentNumberOfTaskbarIcons
val initialMaxNumIconViews = addRunningAppsAndVerifyOverflowState(5)
- runOnMainSync {
- bubbleBarViewController.setHiddenForBubbles(true)
- animatorTestRule.advanceTimeBy(150)
- }
+ currentControllerInitCallback = { bubbleBarViewController.setHiddenForBubbles(true) }
+ runOnMainSync { animatorTestRule.advanceTimeBy(150) }
val maxNumIconViews = maxNumberOfTaskbarIcons
assertThat(maxNumIconViews).isGreaterThan(initialMaxNumIconViews)
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleAnimatorTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleAnimatorTest.kt
index eae181f..da362bd 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleAnimatorTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleAnimatorTest.kt
@@ -44,7 +44,7 @@
)
val listener = TestBubbleAnimatorListener()
InstrumentationRegistry.getInstrumentation().runOnMainSync {
- bubbleAnimator.animateNewBubble(selectedBubbleIndex = 2, listener)
+ bubbleAnimator.animateNewBubble(selectedBubbleIndex = 2, listener = listener)
}
assertThat(bubbleAnimator.isRunning).isTrue()
@@ -94,7 +94,9 @@
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubbleAnimator.animateNewAndRemoveOld(
selectedBubbleIndex = 3,
- removedBubbleIndex = 2,
+ newlySelectedBubbleIndex = 2,
+ removedBubbleIndex = 1,
+ addedBubbleIndex = 3,
listener,
)
}
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 2f773b3..46b5659 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
@@ -245,9 +245,11 @@
animator.onStashStateChangingWhileAnimating()
}
- // The physics animation test util posts the cancellation to the looper thread, so we have
- // to wait again and let it finish.
- InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+ // wait for the animation to cancel
+ PhysicsAnimatorTestUtils.blockUntilAnimationsEnd(
+ handleAnimator,
+ DynamicAnimation.TRANSLATION_Y,
+ )
// verify that the hide animation was canceled
assertThat(animatorScheduler.delayedBlock).isNull()
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt
index f642345..b24926b 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt
@@ -507,6 +507,45 @@
assertThat(height).isEqualTo(BUBBLE_BAR_HEIGHT)
}
+ @Test
+ fun getHandleViewAlpha_stashedHasBubbles_alphaPropertyReturned() {
+ // Given BubbleBar is stashed and has bubbles
+ whenever(bubbleBarViewController.hasBubbles()).thenReturn(true)
+ mTransientBubbleStashController.isStashed = true
+
+ // When handle view alpha property
+ val alphaProperty = mTransientBubbleStashController.getHandleViewAlpha()
+
+ // Then the stash handle alpha property should not be null
+ assertThat(alphaProperty).isNotNull()
+ }
+
+ @Test
+ fun getHandleViewAlpha_stashedHasNoBubblesBar_alphaPropertyIsNull() {
+ // Given BubbleBar is stashed and has no bubbles
+ whenever(bubbleBarViewController.hasBubbles()).thenReturn(false)
+ mTransientBubbleStashController.isStashed = true
+
+ // When handle view alpha property
+ val alphaProperty = mTransientBubbleStashController.getHandleViewAlpha()
+
+ // Then the stash handle alpha property should be null
+ assertThat(alphaProperty).isNull()
+ }
+
+ @Test
+ fun getHandleViewAlpha_unstashedHasBubbles_alphaPropertyIsNull() {
+ // Given BubbleBar is not stashed and has bubbles
+ whenever(bubbleBarViewController.hasBubbles()).thenReturn(true)
+ mTransientBubbleStashController.isStashed = false
+
+ // When handle view alpha property
+ val alphaProperty = mTransientBubbleStashController.getHandleViewAlpha()
+
+ // Then the stash handle alpha property should be null
+ assertThat(alphaProperty).isNull()
+ }
+
private fun advanceTimeBy(advanceMs: Long) {
// Advance animator for on-device tests
getInstrumentation().runOnMainSync { animatorTestRule.advanceTimeBy(advanceMs) }
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarModeRule.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarModeRule.kt
index 74b154a..3cf912c 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarModeRule.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarModeRule.kt
@@ -61,11 +61,10 @@
val mode = taskbarMode.mode
getInstrumentation().runOnMainSync {
- context.putObject(
- DisplayController.INSTANCE,
- object : DisplayController(context) {
- override fun getInfo(): Info {
- return spy(super.getInfo()) {
+ DisplayController.INSTANCE[context].let {
+ if (it is DisplayControllerSpy) {
+ it.infoModifier = { info ->
+ spy(info) {
on { isTransientTaskbar } doReturn (mode == Mode.TRANSIENT)
on { isPinnedTaskbar } doReturn (mode == Mode.PINNED)
on { navigationMode } doReturn
@@ -76,8 +75,8 @@
}
}
}
- },
- )
+ }
+ }
}
base.evaluate()
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt
index 07b32af..e150568 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt
@@ -24,7 +24,6 @@
import android.provider.Settings.Secure.getUriFor
import androidx.test.platform.app.InstrumentationRegistry
import com.android.launcher3.LauncherAppState
-import com.android.launcher3.statehandlers.DesktopVisibilityController
import com.android.launcher3.taskbar.TaskbarActivityContext
import com.android.launcher3.taskbar.TaskbarControllers
import com.android.launcher3.taskbar.TaskbarManager
@@ -72,6 +71,7 @@
class TaskbarUnitTestRule(
private val testInstance: Any,
private val context: TaskbarWindowSandboxContext,
+ private val controllerInjectionCallback: () -> Unit = {},
) : TestRule {
private val instrumentation = InstrumentationRegistry.getInstrumentation()
@@ -110,11 +110,13 @@
PendingIntent(IIntentSender.Default())
},
object : TaskbarNavButtonCallbacks {},
- DesktopVisibilityController(context),
) {
override fun recreateTaskbar() {
super.recreateTaskbar()
- if (currentActivityContext != null) injectControllers()
+ if (currentActivityContext != null) {
+ injectControllers()
+ controllerInjectionCallback.invoke()
+ }
}
}
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarWindowSandboxContext.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarWindowSandboxContext.kt
index 0b94dfd..e6dc2a2 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarWindowSandboxContext.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarWindowSandboxContext.kt
@@ -22,18 +22,25 @@
import android.hardware.display.VirtualDisplay
import android.view.Display.DEFAULT_DISPLAY
import androidx.test.core.app.ApplicationProvider
-import com.android.launcher3.FakeLauncherPrefs
import com.android.launcher3.LauncherPrefs
+import com.android.launcher3.dagger.ApplicationContext
import com.android.launcher3.dagger.LauncherAppComponent
-import com.android.launcher3.dagger.LauncherAppModule
import com.android.launcher3.dagger.LauncherAppSingleton
+import com.android.launcher3.util.AllModulesForTest
+import com.android.launcher3.util.DaggerSingletonTracker
+import com.android.launcher3.util.DisplayController
+import com.android.launcher3.util.FakePrefsModule
import com.android.launcher3.util.MainThreadInitializedObject.ObjectSandbox
import com.android.launcher3.util.SandboxApplication
import com.android.launcher3.util.SettingsCache
import com.android.launcher3.util.SettingsCacheSandbox
+import com.android.launcher3.util.window.WindowManagerProxy
import com.android.quickstep.SystemUiProxy
+import dagger.Binds
import dagger.BindsInstance
import dagger.Component
+import dagger.Module
+import javax.inject.Inject
import org.junit.rules.ExternalResource
import org.junit.rules.RuleChain
import org.junit.rules.TestRule
@@ -74,8 +81,6 @@
.bindSettingsCache(settingsCacheSandbox.cache)
componentBinder?.invoke(context, builder)
base.initDaggerComponent(builder)
-
- putObject(LauncherPrefs.INSTANCE, FakeLauncherPrefs(context))
}
}
@@ -116,9 +121,33 @@
}
}
+/** A wrapper over display controller which allows modifying the underlying info */
@LauncherAppSingleton
-@Component(modules = [LauncherAppModule::class])
+class DisplayControllerSpy
+@Inject
+constructor(
+ @ApplicationContext context: Context,
+ wmProxy: WindowManagerProxy,
+ prefs: LauncherPrefs,
+ lifecycle: DaggerSingletonTracker,
+) : DisplayController(context, wmProxy, prefs, lifecycle) {
+
+ var infoModifier: ((Info) -> Info)? = null
+
+ override fun getInfo(): Info = infoModifier?.invoke(super.getInfo()) ?: super.getInfo()
+}
+
+@Module
+abstract class DisplayControllerModule {
+ @Binds abstract fun bindDisplayController(controller: DisplayControllerSpy): DisplayController
+}
+
+@LauncherAppSingleton
+@Component(
+ modules = [AllModulesForTest::class, FakePrefsModule::class, DisplayControllerModule::class]
+)
interface TaskbarSandboxComponent : LauncherAppComponent {
+
@Component.Builder
interface Builder : LauncherAppComponent.Builder {
@BindsInstance fun bindSystemUiProxy(proxy: SystemUiProxy): Builder
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java
index c334552..542eb64 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java
@@ -118,7 +118,6 @@
protected RecentsAnimationTargets mRecentsAnimationTargets;
protected TaskAnimationManager mTaskAnimationManager;
- protected RecentsAnimationDeviceState mRecentsAnimationDeviceState;
@Mock protected CONTAINER_INTERFACE mActivityInterface;
@Mock protected ContextInitListener<?> mContextInitListener;
@@ -180,7 +179,8 @@
@Before
public void setUpRecentsContainer() {
- mTaskAnimationManager = new TaskAnimationManager(mContext, mRecentsAnimationDeviceState);
+ mTaskAnimationManager = new TaskAnimationManager(mContext,
+ RecentsAnimationDeviceState.INSTANCE.get(mContext));
RecentsViewContainer recentsContainer = getRecentsContainer();
RECENTS_VIEW recentsView = getRecentsView();
@@ -198,12 +198,6 @@
}).when(recentsContainer).runOnBindToTouchInteractionService(any());
}
- @Before
- public void setUpRecentsAnimationDeviceState() {
- runOnMainSync(() ->
- mRecentsAnimationDeviceState = new RecentsAnimationDeviceState(mContext, true));
- }
-
@Test
public void testInitWhenReady_registersActivityInitListener() {
String reasonString = "because i said so";
@@ -370,7 +364,7 @@
private void onRecentsAnimationStart(SWIPE_HANDLER absSwipeUpHandler) {
runOnMainSync(() -> absSwipeUpHandler.onRecentsAnimationStart(
- mRecentsAnimationController, mRecentsAnimationTargets));
+ mRecentsAnimationController, mRecentsAnimationTargets, /* transitionInfo= */null));
}
protected static void runOnMainSync(Runnable runnable) {
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/FallbackSwipeHandlerTestCase.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/FallbackSwipeHandlerTestCase.java
index d4eb8e2..3489519 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/FallbackSwipeHandlerTestCase.java
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/FallbackSwipeHandlerTestCase.java
@@ -44,7 +44,6 @@
long touchTimeMs, boolean continuingLastGesture) {
return new FallbackSwipeHandler(
mContext,
- mRecentsAnimationDeviceState,
mTaskAnimationManager,
mGestureState,
touchTimeMs,
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2Test.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2Test.kt
index 41877c9..f05b422 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2Test.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2Test.kt
@@ -17,6 +17,12 @@
package com.android.quickstep
import android.graphics.PointF
+import android.hardware.display.DisplayManager
+import android.hardware.display.DisplayManagerGlobal
+import android.view.Display
+import android.view.Display.DEFAULT_DISPLAY
+import android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS
+import android.view.DisplayInfo
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.launcher3.R
@@ -25,7 +31,6 @@
import com.android.launcher3.dagger.LauncherAppSingleton
import com.android.launcher3.util.LauncherModelHelper
import com.android.launcher3.util.MSDLPlayerWrapper
-import com.android.quickstep.fallback.window.RecentsDisplayModel
import com.android.systemui.contextualeducation.GestureType
import com.android.systemui.shared.system.InputConsumerController
import dagger.BindsInstance
@@ -53,8 +58,6 @@
@Mock private lateinit var systemUiProxy: SystemUiProxy
- @Mock private lateinit var recentsDisplayModel: RecentsDisplayModel
-
@Mock private lateinit var msdlPlayerWrapper: MSDLPlayerWrapper
private lateinit var underTest: LauncherSwipeHandlerV2
@@ -67,22 +70,38 @@
private val flingSpeed =
-(sandboxContext.resources.getDimension(R.dimen.quickstep_fling_threshold_speed) + 1)
+ private val displayManager: DisplayManager =
+ sandboxContext.spyService(DisplayManager::class.java)
+
@Before
fun setup() {
+ val display =
+ Display(
+ DisplayManagerGlobal.getInstance(),
+ DEFAULT_DISPLAY,
+ DisplayInfo(),
+ DEFAULT_DISPLAY_ADJUSTMENTS,
+ )
+ whenever(displayManager.getDisplay(eq(DEFAULT_DISPLAY))).thenReturn(display)
+ whenever(displayManager.displays).thenReturn(arrayOf(display))
+
sandboxContext.initDaggerComponent(
- DaggerTestComponent.builder()
- .bindSystemUiProxy(systemUiProxy)
- .bindRecentsDisplayModel(recentsDisplayModel)
+ DaggerTestComponent.builder().bindSystemUiProxy(systemUiProxy)
+ )
+ sandboxContext.putObject(
+ RotationTouchHelper.INSTANCE,
+ mock(RotationTouchHelper::class.java),
+ )
+ sandboxContext.putObject(
+ RecentsAnimationDeviceState.INSTANCE,
+ mock(RecentsAnimationDeviceState::class.java),
)
- val deviceState = mock(RecentsAnimationDeviceState::class.java)
- whenever(deviceState.rotationTouchHelper).thenReturn(mock(RotationTouchHelper::class.java))
gestureState = spy(GestureState(OverviewComponentObserver.INSTANCE.get(sandboxContext), 0))
underTest =
LauncherSwipeHandlerV2(
sandboxContext,
- deviceState,
taskAnimationManager,
gestureState,
0,
@@ -100,7 +119,7 @@
verify(systemUiProxy)
.updateContextualEduStats(
/* isTrackpadGesture= */ eq(true),
- eq(GestureType.HOME.toString()),
+ eq(GestureType.HOME),
)
}
@@ -110,7 +129,7 @@
verify(systemUiProxy)
.updateContextualEduStats(
/* isTrackpadGesture= */ eq(false),
- eq(GestureType.HOME.toString()),
+ eq(GestureType.HOME),
)
}
}
@@ -122,8 +141,6 @@
interface Builder : LauncherAppComponent.Builder {
@BindsInstance fun bindSystemUiProxy(proxy: SystemUiProxy): Builder
- @BindsInstance fun bindRecentsDisplayModel(model: RecentsDisplayModel): Builder
-
override fun build(): TestComponent
}
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2TestCase.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2TestCase.java
index fc6acfd..e6c5a6c 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2TestCase.java
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2TestCase.java
@@ -73,7 +73,6 @@
long touchTimeMs, boolean continuingLastGesture) {
return new LauncherSwipeHandlerV2(
mContext,
- mRecentsAnimationDeviceState,
mTaskAnimationManager,
mGestureState,
touchTimeMs,
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentsWindowSwipeHandlerTestCase.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentsWindowSwipeHandlerTestCase.java
index 40fefae..c1be1ce 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentsWindowSwipeHandlerTestCase.java
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentsWindowSwipeHandlerTestCase.java
@@ -21,8 +21,8 @@
import com.android.launcher3.dagger.LauncherAppComponent;
import com.android.launcher3.dagger.LauncherAppSingleton;
+import com.android.launcher3.util.AllModulesForTest;
import com.android.launcher3.util.LauncherMultivalentJUnit;
-import com.android.quickstep.dagger.QuickStepModule;
import com.android.quickstep.fallback.FallbackRecentsView;
import com.android.quickstep.fallback.RecentsState;
import com.android.quickstep.fallback.window.RecentsDisplayModel;
@@ -30,13 +30,13 @@
import com.android.quickstep.fallback.window.RecentsWindowSwipeHandler;
import com.android.quickstep.views.RecentsViewContainer;
+import dagger.BindsInstance;
+import dagger.Component;
+
import org.junit.Before;
import org.junit.runner.RunWith;
import org.mockito.Mock;
-import dagger.BindsInstance;
-import dagger.Component;
-
@SmallTest
@RunWith(LauncherMultivalentJUnit.class)
public class RecentsWindowSwipeHandlerTestCase extends AbsSwipeUpHandlerTestCase<
@@ -62,7 +62,6 @@
boolean continuingLastGesture) {
return new RecentsWindowSwipeHandler(
mContext,
- mRecentsAnimationDeviceState,
mTaskAnimationManager,
mGestureState,
touchTimeMs,
@@ -84,7 +83,7 @@
}
@LauncherAppSingleton
- @Component(modules = { QuickStepModule.class })
+ @Component(modules = {AllModulesForTest.class})
interface TestComponent extends LauncherAppComponent {
@Component.Builder
interface Builder extends LauncherAppComponent.Builder {
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumerTest.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumerTest.java
index 98a3607..7776351 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumerTest.java
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumerTest.java
@@ -45,6 +45,9 @@
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.launcher3.dagger.LauncherAppComponent;
+import com.android.launcher3.dagger.LauncherAppSingleton;
+import com.android.launcher3.util.AllModulesForTest;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext;
import com.android.quickstep.DeviceConfigWrapper;
@@ -56,6 +59,9 @@
import com.android.quickstep.util.TestExtensions;
import com.android.systemui.shared.system.InputMonitorCompat;
+import dagger.BindsInstance;
+import dagger.Component;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -423,7 +429,10 @@
mContext.onDestroy();
}
mContext = new SandboxContext(getApplicationContext());
- mContext.putObject(TopTaskTracker.INSTANCE, mTopTaskTracker);
+ mContext.initDaggerComponent(
+ DaggerNavHandleLongPressInputConsumerTest_TopTaskTrackerComponent
+ .builder()
+ .bindTopTaskTracker(mTopTaskTracker));
mScreenWidth = DisplayController.INSTANCE.get(mContext).getInfo().currentSize.x;
mUnderTest = new NavHandleLongPressInputConsumer(mContext, mDelegate, mInputMonitor,
mDeviceState, mNavHandle, mGestureState);
@@ -450,4 +459,16 @@
value,
() -> DeviceConfigWrapper.get().getEnableLpnhTwoStages());
}
+
+ @LauncherAppSingleton
+ @Component(modules = AllModulesForTest.class)
+ public interface TopTaskTrackerComponent extends LauncherAppComponent {
+ @Component.Builder
+ interface Builder extends LauncherAppComponent.Builder {
+ @BindsInstance Builder bindTopTaskTracker(TopTaskTracker topTaskTracker);
+
+ @Override
+ TopTaskTrackerComponent build();
+ }
+ }
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/logging/SettingsChangeLoggerTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/logging/SettingsChangeLoggerTest.kt
index 7c48ea4..cf59f44 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/logging/SettingsChangeLoggerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/logging/SettingsChangeLoggerTest.kt
@@ -21,8 +21,8 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.launcher3.LauncherPrefs
import com.android.launcher3.LauncherPrefs.Companion.ALLOW_ROTATION
-import com.android.launcher3.LauncherPrefs.Companion.THEMED_ICONS
import com.android.launcher3.SessionCommitReceiver.ADD_ICON_PREFERENCE_KEY
+import com.android.launcher3.graphics.ThemeManager
import com.android.launcher3.logging.InstanceId
import com.android.launcher3.logging.StatsLogManager
import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ADD_NEW_APPS_TO_HOME_SCREEN_ENABLED
@@ -66,16 +66,19 @@
private var mDefaultThemedIcons = false
private var mDefaultAllowRotation = false
+ private val themeManager: ThemeManager
+ get() = ThemeManager.INSTANCE.get(mContext)
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
whenever(mStatsLogManager.logger()).doReturn(mMockLogger)
whenever(mStatsLogManager.logger().withInstanceId(any())).doReturn(mMockLogger)
- mDefaultThemedIcons = LauncherPrefs.get(mContext).get(THEMED_ICONS)
+ mDefaultThemedIcons = themeManager.isMonoThemeEnabled
mDefaultAllowRotation = LauncherPrefs.get(mContext).get(ALLOW_ROTATION)
// To match the default value of THEMED_ICONS
- LauncherPrefs.get(mContext).put(THEMED_ICONS, false)
+ themeManager.isMonoThemeEnabled = false
// To match the default value of ALLOW_ROTATION
LauncherPrefs.get(mContext).put(item = ALLOW_ROTATION, value = false)
@@ -84,7 +87,7 @@
@After
fun tearDown() {
- LauncherPrefs.get(mContext).put(THEMED_ICONS, mDefaultThemedIcons)
+ themeManager.isMonoThemeEnabled = mDefaultThemedIcons
LauncherPrefs.get(mContext).put(ALLOW_ROTATION, mDefaultAllowRotation)
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeHighResLoadingStateNotifier.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeHighResLoadingStateNotifier.kt
index 7d09efd..4adf01e 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeHighResLoadingStateNotifier.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeHighResLoadingStateNotifier.kt
@@ -16,7 +16,7 @@
package com.android.quickstep.recents.data
-import com.android.quickstep.TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback
+import com.android.quickstep.HighResLoadingState.HighResLoadingStateChangedCallback
class FakeHighResLoadingStateNotifier : HighResLoadingStateNotifier {
val listeners = mutableListOf<HighResLoadingStateChangedCallback>()
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeRecentsDeviceProfileRepository.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeRecentsDeviceProfileRepository.kt
index fc2f029..4e90903 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeRecentsDeviceProfileRepository.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeRecentsDeviceProfileRepository.kt
@@ -18,9 +18,7 @@
class FakeRecentsDeviceProfileRepository : RecentsDeviceProfileRepository {
private var recentsDeviceProfile =
- RecentsDeviceProfile(
- isLargeScreen = false,
- )
+ RecentsDeviceProfile(isLargeScreen = false, canEnterDesktopMode = false)
override fun getRecentsDeviceProfile() = recentsDeviceProfile
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskIconDataSource.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskIconDataSource.kt
index 5de876a..f6f158f 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskIconDataSource.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskIconDataSource.kt
@@ -17,11 +17,11 @@
package com.android.quickstep.recents.data
import android.graphics.drawable.Drawable
-import com.android.launcher3.util.CancellableTask
-import com.android.quickstep.TaskIconCache
+import com.android.quickstep.TaskIconCache.TaskCacheEntry
import com.android.quickstep.task.thumbnail.data.TaskIconDataSource
import com.android.systemui.shared.recents.model.Task
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.yield
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
@@ -29,28 +29,30 @@
val taskIdToDrawable: MutableMap<Int, Drawable> =
(0..10).associateWith { mockCopyableDrawable() }.toMutableMap()
-
- val taskIdToUpdatingTask: MutableMap<Int, () -> Unit> = mutableMapOf()
- var shouldLoadSynchronously: Boolean = true
+ private val completionPrevented: MutableSet<Int> = mutableSetOf()
/** Retrieves and sets an icon on [task] from [taskIdToDrawable]. */
- override fun getIconInBackground(
- task: Task,
- callback: TaskIconCache.GetTaskIconCallback
- ): CancellableTask<*>? {
- val wrappedCallback = {
- callback.onTaskIconReceived(
- taskIdToDrawable.getValue(task.key.id),
- "content desc ${task.key.id}",
- "title ${task.key.id}"
- )
+ override suspend fun getIcon(task: Task): TaskCacheEntry {
+ while (task.key.id in completionPrevented) {
+ yield()
}
- if (shouldLoadSynchronously) {
- wrappedCallback()
- } else {
- taskIdToUpdatingTask[task.key.id] = wrappedCallback
- }
- return null
+ return TaskCacheEntry(
+ taskIdToDrawable.getValue(task.key.id),
+ "content desc ${task.key.id}",
+ "title ${task.key.id}",
+ )
+ }
+
+ fun preventIconLoad(taskId: Int) {
+ completionPrevented.add(taskId)
+ }
+
+ fun completeLoadingForTask(taskId: Int) {
+ completionPrevented.remove(taskId)
+ }
+
+ fun completeLoading() {
+ completionPrevented.clear()
}
companion object {
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskThumbnailDataSource.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskThumbnailDataSource.kt
index d12c0b0..e10afc4 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskThumbnailDataSource.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskThumbnailDataSource.kt
@@ -17,11 +17,10 @@
package com.android.quickstep.recents.data
import android.graphics.Bitmap
-import com.android.launcher3.util.CancellableTask
import com.android.quickstep.task.thumbnail.data.TaskThumbnailDataSource
import com.android.systemui.shared.recents.model.Task
import com.android.systemui.shared.recents.model.ThumbnailData
-import java.util.function.Consumer
+import kotlinx.coroutines.yield
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
@@ -29,25 +28,28 @@
val taskIdToBitmap: MutableMap<Int, Bitmap> =
(0..10).associateWith { mock<Bitmap>() }.toMutableMap()
- val taskIdToUpdatingTask: MutableMap<Int, () -> Unit> = mutableMapOf()
- var shouldLoadSynchronously: Boolean = true
+ private val completionPrevented: MutableSet<Int> = mutableSetOf()
+ private val getThumbnailCalls = mutableMapOf<Int, Int>()
/** Retrieves and sets a thumbnail on [task] from [taskIdToBitmap]. */
- override fun getThumbnailInBackground(
- task: Task,
- callback: Consumer<ThumbnailData>
- ): CancellableTask<ThumbnailData>? {
- val thumbnailData = mock<ThumbnailData>()
- whenever(thumbnailData.thumbnail).thenReturn(taskIdToBitmap[task.key.id])
- val wrappedCallback = {
- task.thumbnail = thumbnailData
- callback.accept(thumbnailData)
+ override suspend fun getThumbnail(task: Task): ThumbnailData {
+ getThumbnailCalls[task.key.id] = (getThumbnailCalls[task.key.id] ?: 0) + 1
+
+ while (task.key.id in completionPrevented) {
+ yield()
}
- if (shouldLoadSynchronously) {
- wrappedCallback()
- } else {
- taskIdToUpdatingTask[task.key.id] = wrappedCallback
+ return mock<ThumbnailData>().also {
+ whenever(it.thumbnail).thenReturn(taskIdToBitmap[task.key.id])
}
- return null
+ }
+
+ fun getNumberOfGetThumbnailCalls(taskId: Int): Int = getThumbnailCalls[taskId] ?: 0
+
+ fun preventThumbnailLoad(taskId: Int) {
+ completionPrevented.add(taskId)
+ }
+
+ fun completeLoadingForTask(taskId: Int) {
+ completionPrevented.remove(taskId)
}
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/RecentsDeviceProfileRepositoryImplTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/RecentsDeviceProfileRepositoryImplTest.kt
deleted file mode 100644
index abe4142..0000000
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/RecentsDeviceProfileRepositoryImplTest.kt
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.quickstep.recents.data
-
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.launcher3.FakeInvariantDeviceProfileTest
-import com.android.quickstep.views.RecentsViewContainer
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.kotlin.mock
-import org.mockito.kotlin.whenever
-
-/** Test for [RecentsDeviceProfileRepositoryImpl] */
-@RunWith(AndroidJUnit4::class)
-class RecentsDeviceProfileRepositoryImplTest : FakeInvariantDeviceProfileTest() {
- private val recentsViewContainer = mock<RecentsViewContainer>()
-
- private val systemUnderTest = RecentsDeviceProfileRepositoryImpl(recentsViewContainer)
-
- @Test
- fun deviceProfileMappedCorrectly() {
- initializeVarsForTablet()
- val tabletDeviceProfile = newDP()
- whenever(recentsViewContainer.deviceProfile).thenReturn(tabletDeviceProfile)
-
- assertThat(systemUnderTest.getRecentsDeviceProfile())
- .isEqualTo(RecentsDeviceProfile(isLargeScreen = true))
- }
-}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt
index ee1ec6e..b6cf5bd 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt
@@ -214,7 +214,7 @@
.isEqualTo(bitmap2)
// Prevent new loading of Bitmaps
- taskThumbnailDataSource.shouldLoadSynchronously = false
+ taskThumbnailDataSource.preventThumbnailLoad(2)
systemUnderTest.setVisibleTasks(setOf(2, 3))
assertThat(systemUnderTest.getTaskDataById(2).first()!!.thumbnail!!.thumbnail)
@@ -235,7 +235,7 @@
.assertHasIconDataFromSource(taskIconDataSource)
// Prevent new loading of Drawables
- taskThumbnailDataSource.shouldLoadSynchronously = false
+ taskIconDataSource.preventIconLoad(2)
systemUnderTest.setVisibleTasks(setOf(2, 3))
systemUnderTest
@@ -257,9 +257,6 @@
assertThat(task2.thumbnail!!.thumbnail).isEqualTo(bitmap2)
task2.assertHasIconDataFromSource(taskIconDataSource)
- // Prevent new loading of Bitmaps
- taskThumbnailDataSource.shouldLoadSynchronously = false
- taskIconDataSource.shouldLoadSynchronously = false
systemUnderTest.setVisibleTasks(setOf(0, 1))
val task2AfterVisibleTasksChanged = systemUnderTest.getTaskDataById(2).first()!!
@@ -275,21 +272,22 @@
// Setup fakes
recentsModel.seedTasks(defaultTaskList)
val bitmap2 = taskThumbnailDataSource.taskIdToBitmap[2]
- taskThumbnailDataSource.shouldLoadSynchronously = false
// Setup TasksRepository
systemUnderTest.getAllTaskData(forceRefresh = true)
- systemUnderTest.setVisibleTasks(setOf(1, 2))
- // Assert there is no bitmap in first emission
- assertThat(systemUnderTest.getTaskDataById(2).first()!!.thumbnail).isNull()
+ val task2DataFlow = systemUnderTest.getTaskDataById(2)
+ val task2BitmapValues = mutableListOf<Bitmap?>()
+ testScope.backgroundScope.launch {
+ task2DataFlow.map { it?.thumbnail?.thumbnail }.toList(task2BitmapValues)
+ }
- // Simulate bitmap loading after first emission
- taskThumbnailDataSource.taskIdToUpdatingTask.getValue(2).invoke()
+ // Check for first emission
+ assertThat(task2BitmapValues.single()).isNull()
+ systemUnderTest.setVisibleTasks(setOf(2))
// Check for second emission
- assertThat(systemUnderTest.getTaskDataById(2).first()!!.thumbnail!!.thumbnail)
- .isEqualTo(bitmap2)
+ assertThat(task2BitmapValues).isEqualTo(listOf(null, bitmap2))
}
@Test
@@ -365,7 +363,6 @@
testScope.runTest {
recentsModel.seedTasks(defaultTaskList)
systemUnderTest.getAllTaskData(forceRefresh = true)
- taskThumbnailDataSource.shouldLoadSynchronously = false
val taskDataFlow = systemUnderTest.getTaskDataById(1)
val task1IconValues = mutableListOf<Drawable?>()
@@ -374,14 +371,10 @@
}
systemUnderTest.setVisibleTasks(setOf(1))
- val task1UpdatingTaskOld = taskThumbnailDataSource.taskIdToUpdatingTask[1]
- println(task1UpdatingTaskOld)
+ assertThat(taskThumbnailDataSource.getNumberOfGetThumbnailCalls(1)).isEqualTo(1)
systemUnderTest.setVisibleTasks(setOf(1, 2))
- val task1UpdatingTaskNew = taskThumbnailDataSource.taskIdToUpdatingTask[1]
- println(task1UpdatingTaskNew)
-
- assertThat(task1UpdatingTaskNew).isEqualTo(task1UpdatingTaskOld)
+ assertThat(taskThumbnailDataSource.getNumberOfGetThumbnailCalls(1)).isEqualTo(1)
}
private fun createTaskWithId(taskId: Int) =
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/window/RecentsDisplayModelTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/window/RecentsDisplayModelTest.kt
index d2aa6ac..44ea73e 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/window/RecentsDisplayModelTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/window/RecentsDisplayModelTest.kt
@@ -28,14 +28,9 @@
import androidx.test.platform.app.InstrumentationRegistry
import com.android.launcher3.Flags.FLAG_ENABLE_FALLBACK_OVERVIEW_IN_WINDOW
import com.android.launcher3.Flags.FLAG_ENABLE_LAUNCHER_OVERVIEW_IN_WINDOW
-import com.android.launcher3.dagger.LauncherAppComponent
-import com.android.launcher3.dagger.LauncherAppModule
-import com.android.launcher3.dagger.LauncherAppSingleton
import com.android.launcher3.util.LauncherModelHelper
import com.android.launcher3.util.window.CachedDisplayInfo
import com.android.quickstep.fallback.window.RecentsDisplayModel
-import dagger.BindsInstance
-import dagger.Component
import org.junit.Assert
import org.junit.Before
import org.junit.Rule
@@ -75,10 +70,6 @@
whenever(displayManager.getDisplay(anyInt())).thenReturn(display)
runOnMainSync { recentsDisplayModel = RecentsDisplayModel.INSTANCE.get(context) }
- context.initDaggerComponent(
- DaggerRecentsDisplayModelComponent.builder()
- .bindRecentsDisplayModel(recentsDisplayModel)
- )
}
@Test
@@ -125,14 +116,3 @@
InstrumentationRegistry.getInstrumentation().runOnMainSync { f.run() }
}
}
-
-@LauncherAppSingleton
-@Component(modules = [LauncherAppModule::class])
-interface RecentsDisplayModelComponent : LauncherAppComponent {
- @Component.Builder
- interface Builder : LauncherAppComponent.Builder {
- @BindsInstance fun bindRecentsDisplayModel(model: RecentsDisplayModel): Builder
-
- override fun build(): RecentsDisplayModelComponent
- }
-}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModelImplTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModelImplTest.kt
index a777bd4..a956c9c 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModelImplTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModelImplTest.kt
@@ -16,16 +16,23 @@
package com.android.quickstep.task.thumbnail
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
import android.content.ComponentName
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.Matrix
import android.graphics.drawable.Drawable
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
import android.view.Surface
import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.launcher3.Flags
import com.android.launcher3.util.TestDispatcherProvider
+import com.android.quickstep.recents.data.FakeRecentsDeviceProfileRepository
import com.android.quickstep.recents.data.FakeTasksRepository
+import com.android.quickstep.recents.data.RecentsDeviceProfile
import com.android.quickstep.recents.usecase.GetThumbnailPositionUseCase
import com.android.quickstep.recents.usecase.ThumbnailPositionState.MatrixScaling
import com.android.quickstep.recents.usecase.ThumbnailPositionState.MissingThumbnail
@@ -34,6 +41,7 @@
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.LiveTile
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Snapshot
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.SnapshotSplash
+import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.ThumbnailHeader
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Uninitialized
import com.android.quickstep.task.viewmodel.TaskContainerData
import com.android.quickstep.task.viewmodel.TaskThumbnailViewModelImpl
@@ -44,6 +52,7 @@
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.mock
@@ -52,6 +61,8 @@
/** Test for [TaskThumbnailView] */
@RunWith(AndroidJUnit4::class)
class TaskThumbnailViewModelImplTest {
+ @get:Rule val setFlagsRule = SetFlagsRule()
+
private val dispatcher = StandardTestDispatcher()
private val testScope = TestScope(dispatcher)
@@ -59,6 +70,7 @@
private val taskContainerData = TaskContainerData()
private val dispatcherProvider = TestDispatcherProvider(dispatcher)
private val tasksRepository = FakeTasksRepository()
+ private val deviceProfileRepository = FakeRecentsDeviceProfileRepository()
private val mGetThumbnailPositionUseCase = mock<GetThumbnailPositionUseCase>()
private val splashAlphaUseCase: SplashAlphaUseCase = mock()
@@ -68,12 +80,18 @@
taskContainerData,
dispatcherProvider,
tasksRepository,
+ deviceProfileRepository,
mGetThumbnailPositionUseCase,
splashAlphaUseCase,
)
}
- private val tasks = (0..5).map(::createTaskWithId)
+ private val fullscreenTaskIdRange: IntRange = 0..5
+ private val freeformTaskIdRange: IntRange = 6..10
+
+ private val fullscreenTasks = fullscreenTaskIdRange.map(::createTaskWithId)
+ private val freeformTasks = freeformTaskIdRange.map(::createFreeformTaskWithId)
+ private val tasks = fullscreenTasks + freeformTasks
@Test
fun initialStateIsUninitialized() =
@@ -88,7 +106,7 @@
recentsViewData.runningTaskIds.value = setOf(taskId)
systemUnderTest.bind(taskId)
- assertThat(systemUnderTest.uiState.first()).isEqualTo(LiveTile)
+ assertThat(systemUnderTest.uiState.first()).isEqualTo(LiveTile.WithoutHeader)
}
@Test
@@ -108,7 +126,7 @@
assertThat(systemUnderTest.uiState.first())
.isEqualTo(
SnapshotSplash(
- Snapshot(
+ Snapshot.WithoutHeader(
backgroundColor = Color.rgb(1, 1, 1),
bitmap = expectedThumbnailData.thumbnail!!,
thumbnailRotation = Surface.ROTATION_0,
@@ -127,7 +145,7 @@
tasksRepository.setVisibleTasks(setOf(runningTaskId, stoppedTaskId))
recentsViewData.runningTaskIds.value = setOf(runningTaskId)
systemUnderTest.bind(runningTaskId)
- assertThat(systemUnderTest.uiState.first()).isEqualTo(LiveTile)
+ assertThat(systemUnderTest.uiState.first()).isEqualTo(LiveTile.WithoutHeader)
systemUnderTest.bind(stoppedTaskId)
assertThat(systemUnderTest.uiState.first())
@@ -175,7 +193,7 @@
assertThat(systemUnderTest.uiState.first())
.isEqualTo(
SnapshotSplash(
- Snapshot(
+ Snapshot.WithoutHeader(
backgroundColor = Color.rgb(2, 2, 2),
bitmap = expectedThumbnailData.thumbnail!!,
thumbnailRotation = Surface.ROTATION_270,
@@ -202,7 +220,7 @@
assertThat(systemUnderTest.uiState.first())
.isEqualTo(
SnapshotSplash(
- Snapshot(
+ Snapshot.WithoutHeader(
backgroundColor = Color.rgb(2, 2, 2),
bitmap = expectedThumbnailData.thumbnail!!,
thumbnailRotation = Surface.ROTATION_0,
@@ -213,6 +231,57 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_EXPLODED_VIEW)
+ fun bindRunningTask_inDesktop_thenStateIs_LiveTile_withHeader() =
+ testScope.runTest {
+ deviceProfileRepository.setRecentsDeviceProfile(
+ RecentsDeviceProfile(isLargeScreen = true, canEnterDesktopMode = true)
+ )
+
+ val taskId = freeformTaskIdRange.first
+ val expectedIconData = mock<Drawable>()
+ tasksRepository.seedIconData(taskId, "Task $taskId", "Task $taskId", expectedIconData)
+ tasksRepository.seedTasks(freeformTasks)
+ tasksRepository.setVisibleTasks(setOf(taskId))
+ recentsViewData.runningTaskIds.value = setOf(taskId)
+ systemUnderTest.bind(taskId)
+
+ assertThat(systemUnderTest.uiState.first())
+ .isEqualTo(LiveTile.WithHeader(ThumbnailHeader(expectedIconData, "Task $taskId")))
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_EXPLODED_VIEW)
+ fun bindStoppedTaskWithThumbnail_inDesktop_thenStateIs_SnapshotSplash_withHeader() =
+ testScope.runTest {
+ deviceProfileRepository.setRecentsDeviceProfile(
+ RecentsDeviceProfile(isLargeScreen = true, canEnterDesktopMode = true)
+ )
+
+ val taskId = freeformTaskIdRange.first
+ val expectedThumbnailData = createThumbnailData(rotation = Surface.ROTATION_0)
+ tasksRepository.seedThumbnailData(mapOf(taskId to expectedThumbnailData))
+ val expectedIconData = mock<Drawable>()
+ tasksRepository.seedIconData(taskId, "Task $taskId", "Task $taskId", expectedIconData)
+ tasksRepository.seedTasks(freeformTasks)
+ tasksRepository.setVisibleTasks(setOf(taskId))
+
+ systemUnderTest.bind(taskId)
+ assertThat(systemUnderTest.uiState.first())
+ .isEqualTo(
+ SnapshotSplash(
+ Snapshot.WithHeader(
+ backgroundColor = Color.rgb(taskId, taskId, taskId),
+ bitmap = expectedThumbnailData.thumbnail!!,
+ thumbnailRotation = Surface.ROTATION_0,
+ header = ThumbnailHeader(expectedIconData, "Task $taskId"),
+ ),
+ expectedIconData,
+ )
+ )
+ }
+
+ @Test
fun getSnapshotMatrix_MissingThumbnail() =
testScope.runTest {
val taskId = 2
@@ -269,9 +338,38 @@
}
private fun createTaskWithId(taskId: Int) =
- Task(Task.TaskKey(taskId, 0, Intent(), ComponentName("", ""), 0, 2000)).apply {
- colorBackground = Color.argb(taskId, taskId, taskId, taskId)
- }
+ Task(
+ Task.TaskKey(
+ taskId,
+ WINDOWING_MODE_FULLSCREEN,
+ Intent(),
+ ComponentName("", ""),
+ 0,
+ 2000,
+ )
+ )
+ .apply {
+ colorBackground = Color.argb(taskId, taskId, taskId, taskId)
+ titleDescription = "Task $taskId"
+ icon = mock<Drawable>()
+ }
+
+ private fun createFreeformTaskWithId(taskId: Int) =
+ Task(
+ Task.TaskKey(
+ taskId,
+ WINDOWING_MODE_FREEFORM,
+ Intent(),
+ ComponentName("", ""),
+ 0,
+ 2000,
+ )
+ )
+ .apply {
+ colorBackground = Color.argb(taskId, taskId, taskId, taskId)
+ titleDescription = "Task $taskId"
+ icon = mock<Drawable>()
+ }
private fun createThumbnailData(rotation: Int = Surface.ROTATION_0): ThumbnailData {
val bitmap = mock<Bitmap>()
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/task/viewmodel/TaskOverlayViewModelTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/task/viewmodel/TaskOverlayViewModelTest.kt
index 2e91f5c..95504af 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/task/viewmodel/TaskOverlayViewModelTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/task/viewmodel/TaskOverlayViewModelTest.kt
@@ -22,6 +22,7 @@
import android.graphics.Color
import android.graphics.Matrix
import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.launcher3.util.TestDispatcherProvider
import com.android.quickstep.recents.data.FakeTasksRepository
import com.android.quickstep.recents.usecase.GetThumbnailPositionUseCase
import com.android.quickstep.recents.usecase.ThumbnailPositionState.MatrixScaling
@@ -33,7 +34,10 @@
import com.android.systemui.shared.recents.model.Task
import com.android.systemui.shared.recents.model.ThumbnailData
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -41,6 +45,7 @@
import org.mockito.kotlin.whenever
/** Test for [TaskOverlayViewModel] */
+@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
class TaskOverlayViewModelTest {
private val task =
@@ -58,104 +63,123 @@
private val recentsViewData = RecentsViewData()
private val tasksRepository = FakeTasksRepository()
private val mGetThumbnailPositionUseCase = mock<GetThumbnailPositionUseCase>()
+ private val dispatcher = UnconfinedTestDispatcher()
+ private val testScope = TestScope(dispatcher)
private val systemUnderTest =
- TaskOverlayViewModel(task, recentsViewData, mGetThumbnailPositionUseCase, tasksRepository)
+ TaskOverlayViewModel(
+ task,
+ recentsViewData,
+ mGetThumbnailPositionUseCase,
+ tasksRepository,
+ TestDispatcherProvider(dispatcher),
+ )
@Test
- fun initialStateIsDisabled() = runTest {
- assertThat(systemUnderTest.overlayState.first()).isEqualTo(Disabled)
- }
+ fun initialStateIsDisabled() =
+ testScope.runTest { assertThat(systemUnderTest.overlayState.first()).isEqualTo(Disabled) }
@Test
- fun recentsViewOverlayDisabled_Disabled() = runTest {
- recentsViewData.overlayEnabled.value = false
- recentsViewData.settledFullyVisibleTaskIds.value = setOf(TASK_ID)
+ fun recentsViewOverlayDisabled_Disabled() =
+ testScope.runTest {
+ recentsViewData.overlayEnabled.value = false
+ recentsViewData.settledFullyVisibleTaskIds.value = setOf(TASK_ID)
- assertThat(systemUnderTest.overlayState.first()).isEqualTo(Disabled)
- }
+ assertThat(systemUnderTest.overlayState.first()).isEqualTo(Disabled)
+ }
@Test
- fun taskNotFullyVisible_Disabled() = runTest {
- recentsViewData.overlayEnabled.value = true
- recentsViewData.settledFullyVisibleTaskIds.value = setOf()
+ fun taskNotFullyVisible_Disabled() =
+ testScope.runTest {
+ recentsViewData.overlayEnabled.value = true
+ recentsViewData.settledFullyVisibleTaskIds.value = setOf()
- assertThat(systemUnderTest.overlayState.first()).isEqualTo(Disabled)
- }
+ assertThat(systemUnderTest.overlayState.first()).isEqualTo(Disabled)
+ }
@Test
- fun noThumbnail_Enabled() = runTest {
- recentsViewData.overlayEnabled.value = true
- recentsViewData.settledFullyVisibleTaskIds.value = setOf(TASK_ID)
- task.isLocked = false
+ fun noThumbnail_Enabled() =
+ testScope.runTest {
+ recentsViewData.overlayEnabled.value = true
+ recentsViewData.settledFullyVisibleTaskIds.value = setOf(TASK_ID)
+ task.isLocked = false
- assertThat(systemUnderTest.overlayState.first())
- .isEqualTo(Enabled(isRealSnapshot = false, thumbnail = null))
- }
+ assertThat(systemUnderTest.overlayState.first())
+ .isEqualTo(Enabled(isRealSnapshot = false, thumbnail = null))
+ }
@Test
- fun withThumbnail_RealSnapshot_NotLocked_Enabled() = runTest {
- recentsViewData.overlayEnabled.value = true
- recentsViewData.settledFullyVisibleTaskIds.value = setOf(TASK_ID)
- tasksRepository.seedTasks(listOf(task))
- tasksRepository.seedThumbnailData(mapOf(TASK_ID to thumbnailData))
- tasksRepository.setVisibleTasks(setOf(TASK_ID))
- thumbnailData.isRealSnapshot = true
- task.isLocked = false
+ fun withThumbnail_RealSnapshot_NotLocked_Enabled() =
+ testScope.runTest {
+ recentsViewData.overlayEnabled.value = true
+ recentsViewData.settledFullyVisibleTaskIds.value = setOf(TASK_ID)
+ tasksRepository.seedTasks(listOf(task))
+ tasksRepository.seedThumbnailData(mapOf(TASK_ID to thumbnailData))
+ tasksRepository.setVisibleTasks(setOf(TASK_ID))
+ thumbnailData.isRealSnapshot = true
+ task.isLocked = false
- assertThat(systemUnderTest.overlayState.first())
- .isEqualTo(Enabled(isRealSnapshot = true, thumbnail = thumbnailData.thumbnail))
- }
+ assertThat(systemUnderTest.overlayState.first())
+ .isEqualTo(Enabled(isRealSnapshot = true, thumbnail = thumbnailData.thumbnail))
+ }
@Test
- fun withThumbnail_RealSnapshot_Locked_Enabled() = runTest {
- recentsViewData.overlayEnabled.value = true
- recentsViewData.settledFullyVisibleTaskIds.value = setOf(TASK_ID)
- tasksRepository.seedTasks(listOf(task))
- tasksRepository.seedThumbnailData(mapOf(TASK_ID to thumbnailData))
- tasksRepository.setVisibleTasks(setOf(TASK_ID))
- thumbnailData.isRealSnapshot = true
- task.isLocked = true
+ fun withThumbnail_RealSnapshot_Locked_Enabled() =
+ testScope.runTest {
+ recentsViewData.overlayEnabled.value = true
+ recentsViewData.settledFullyVisibleTaskIds.value = setOf(TASK_ID)
+ tasksRepository.seedTasks(listOf(task))
+ tasksRepository.seedThumbnailData(mapOf(TASK_ID to thumbnailData))
+ tasksRepository.setVisibleTasks(setOf(TASK_ID))
+ thumbnailData.isRealSnapshot = true
+ task.isLocked = true
- assertThat(systemUnderTest.overlayState.first())
- .isEqualTo(Enabled(isRealSnapshot = false, thumbnail = thumbnailData.thumbnail))
- }
+ assertThat(systemUnderTest.overlayState.first())
+ .isEqualTo(Enabled(isRealSnapshot = false, thumbnail = thumbnailData.thumbnail))
+ }
@Test
- fun withThumbnail_FakeSnapshot_Enabled() = runTest {
- recentsViewData.overlayEnabled.value = true
- recentsViewData.settledFullyVisibleTaskIds.value = setOf(TASK_ID)
- tasksRepository.seedTasks(listOf(task))
- tasksRepository.seedThumbnailData(mapOf(TASK_ID to thumbnailData))
- tasksRepository.setVisibleTasks(setOf(TASK_ID))
- thumbnailData.isRealSnapshot = false
- task.isLocked = false
+ fun withThumbnail_FakeSnapshot_Enabled() =
+ testScope.runTest {
+ recentsViewData.overlayEnabled.value = true
+ recentsViewData.settledFullyVisibleTaskIds.value = setOf(TASK_ID)
+ tasksRepository.seedTasks(listOf(task))
+ tasksRepository.seedThumbnailData(mapOf(TASK_ID to thumbnailData))
+ tasksRepository.setVisibleTasks(setOf(TASK_ID))
+ thumbnailData.isRealSnapshot = false
+ task.isLocked = false
- assertThat(systemUnderTest.overlayState.first())
- .isEqualTo(Enabled(isRealSnapshot = false, thumbnail = thumbnailData.thumbnail))
- }
+ assertThat(systemUnderTest.overlayState.first())
+ .isEqualTo(Enabled(isRealSnapshot = false, thumbnail = thumbnailData.thumbnail))
+ }
@Test
- fun getThumbnailMatrix_MissingThumbnail() = runTest {
- val isRtl = true
+ fun getThumbnailMatrix_MissingThumbnail() =
+ testScope.runTest {
+ val isRtl = true
- whenever(mGetThumbnailPositionUseCase.run(TASK_ID, CANVAS_WIDTH, CANVAS_HEIGHT, isRtl))
- .thenReturn(MissingThumbnail)
+ whenever(mGetThumbnailPositionUseCase.run(TASK_ID, CANVAS_WIDTH, CANVAS_HEIGHT, isRtl))
+ .thenReturn(MissingThumbnail)
- assertThat(systemUnderTest.getThumbnailPositionState(CANVAS_WIDTH, CANVAS_HEIGHT, isRtl))
- .isEqualTo(ThumbnailPositionState(Matrix.IDENTITY_MATRIX, isRotated = false))
- }
+ assertThat(
+ systemUnderTest.getThumbnailPositionState(CANVAS_WIDTH, CANVAS_HEIGHT, isRtl)
+ )
+ .isEqualTo(ThumbnailPositionState(Matrix.IDENTITY_MATRIX, isRotated = false))
+ }
@Test
- fun getThumbnailMatrix_MatrixScaling() = runTest {
- val isRtl = true
- val isRotated = true
+ fun getThumbnailMatrix_MatrixScaling() =
+ testScope.runTest {
+ val isRtl = true
+ val isRotated = true
- whenever(mGetThumbnailPositionUseCase.run(TASK_ID, CANVAS_WIDTH, CANVAS_HEIGHT, isRtl))
- .thenReturn(MatrixScaling(MATRIX, isRotated))
+ whenever(mGetThumbnailPositionUseCase.run(TASK_ID, CANVAS_WIDTH, CANVAS_HEIGHT, isRtl))
+ .thenReturn(MatrixScaling(MATRIX, isRotated))
- assertThat(systemUnderTest.getThumbnailPositionState(CANVAS_WIDTH, CANVAS_HEIGHT, isRtl))
- .isEqualTo(ThumbnailPositionState(MATRIX, isRotated))
- }
+ assertThat(
+ systemUnderTest.getThumbnailPositionState(CANVAS_WIDTH, CANVAS_HEIGHT, isRtl)
+ )
+ .isEqualTo(ThumbnailPositionState(MATRIX, isRotated))
+ }
companion object {
const val TASK_ID = 0
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/ContextualSearchInvokerTest.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/ContextualSearchInvokerTest.java
index 88774be..61971b1 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/ContextualSearchInvokerTest.java
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/ContextualSearchInvokerTest.java
@@ -52,6 +52,7 @@
import com.android.quickstep.DeviceConfigWrapper;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TopTaskTracker;
+import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.RecentsViewContainer;
@@ -82,6 +83,7 @@
private @Mock BaseContainerInterface mMockContainerInterface;
private @Mock RecentsViewContainer mMockRecentsViewContainer;
private @Mock RecentsView mMockRecentsView;
+ private @Mock RecentsPagedOrientationHandler mMockOrientationHandler;
private ContextualSearchInvoker mContextualSearchInvoker;
@Before
@@ -190,6 +192,15 @@
}
@Test
+ public void runContextualSearchInvocationChecksAndLogFailures_isFakeLandscape() {
+ when(mMockRecentsView.getPagedOrientationHandler()).thenReturn(mMockOrientationHandler);
+ when(mMockOrientationHandler.isLayoutNaturalToLauncher()).thenReturn(false);
+ assertFalse("Expect invocation checks to fail in fake landscape.",
+ mContextualSearchInvoker.runContextualSearchInvocationChecksAndLogFailures());
+ verifyNoMoreInteractions(mMockStatsLogManager);
+ }
+
+ @Test
public void invokeContextualSearchUncheckedWithHaptic_cssIsAvailable_commitHapticEnabled() {
try (AutoCloseable flag = overrideSearchHapticCommitFlag(true)) {
assertTrue("Expected invocation unchecked to succeed",
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/TaskViewSimulatorTest.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
index fa81680..be76f9e 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
@@ -35,6 +35,9 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.dagger.LauncherAppComponent;
+import com.android.launcher3.dagger.LauncherAppSingleton;
+import com.android.launcher3.util.AllModulesMinusWMProxy;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.Info;
import com.android.launcher3.util.LauncherModelHelper;
@@ -46,6 +49,9 @@
import com.android.quickstep.FallbackActivityInterface;
import com.android.quickstep.util.SurfaceTransaction.MockProperties;
+import dagger.BindsInstance;
+import dagger.Component;
+
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
import org.junit.Assert;
@@ -159,6 +165,11 @@
void verifyNoTransforms() {
LauncherModelHelper helper = new LauncherModelHelper();
try {
+ DisplayController mockController = mock(DisplayController.class);
+
+ helper.sandboxContext.initDaggerComponent(
+ DaggerTaskViewSimulatorTest_TaskViewSimulatorTestComponent.builder()
+ .bindDisplayController(mockController));
int rotation = mDisplaySize.x > mDisplaySize.y
? Surface.ROTATION_90 : Surface.ROTATION_0;
CachedDisplayInfo cdi = new CachedDisplayInfo(mDisplaySize, rotation);
@@ -192,10 +203,7 @@
DisplayController.Info info = new Info(
configurationContext, wmProxy, perDisplayBoundsCache);
-
- DisplayController mockController = mock(DisplayController.class);
when(mockController.getInfo()).thenReturn(info);
- helper.sandboxContext.putObject(DisplayController.INSTANCE, mockController);
mDeviceProfile = InvariantDeviceProfile.INSTANCE.get(helper.sandboxContext)
.getBestMatch(mAppBounds.width(), mAppBounds.height(), rotation);
@@ -271,4 +279,18 @@
description.appendValue(mExpected);
}
}
+
+ @LauncherAppSingleton
+ @Component(modules = {AllModulesMinusWMProxy.class})
+ interface TaskViewSimulatorTestComponent extends LauncherAppComponent {
+
+ @Component.Builder
+ interface Builder extends LauncherAppComponent.Builder {
+
+ @BindsInstance
+ Builder bindDisplayController(DisplayController controller);
+
+ TaskViewSimulatorTestComponent build();
+ }
+ }
}
diff --git a/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt b/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt
index 94e7c2e..c152ee1 100644
--- a/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt
@@ -18,6 +18,7 @@
import android.content.ComponentName
import android.content.Intent
+import androidx.test.platform.app.InstrumentationRegistry
import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
import com.android.dx.mockito.inline.extended.StaticMockitoSession
import com.android.launcher3.AbstractFloatingView
@@ -113,6 +114,8 @@
whenever(launcher.statsLogManager).thenReturn(statsLogManager)
whenever(statsLogManager.logger()).thenReturn(statsLogger)
whenever(statsLogger.withItemInfo(any())).thenReturn(statsLogger)
+ whenever(taskView.context)
+ .thenReturn(InstrumentationRegistry.getInstrumentation().targetContext)
whenever(recentsView.moveTaskToDesktop(any(), any(), any())).thenAnswer {
val successCallback = it.getArgument<Runnable>(2)
successCallback.run()
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index 5bb2fad..a4c9ef2 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -333,13 +333,11 @@
private class OverviewUpdateHandler implements OverviewChangeListener {
- final RecentsAnimationDeviceState mRads;
final OverviewComponentObserver mObserver;
final CountDownLatch mChangeCounter;
OverviewUpdateHandler() {
Context ctx = getInstrumentation().getTargetContext();
- mRads = new RecentsAnimationDeviceState(ctx);
mObserver = OverviewComponentObserver.INSTANCE.get(ctx);
mChangeCounter = new CountDownLatch(1);
if (mObserver.getHomeIntent().getComponent()
@@ -358,7 +356,6 @@
void destroy() {
mObserver.removeOverviewChangeListener(this);
- mRads.destroy();
}
}
}
diff --git a/quickstep/tests/src/com/android/quickstep/InputConsumerUtilsTest.java b/quickstep/tests/src/com/android/quickstep/InputConsumerUtilsTest.java
index 160c578..3c5e1e8 100644
--- a/quickstep/tests/src/com/android/quickstep/InputConsumerUtilsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/InputConsumerUtilsTest.java
@@ -127,6 +127,8 @@
@Before
public void setupMainThreadInitializedObjects() {
mContext.putObject(LockedUserState.INSTANCE, mLockedUserState);
+ mContext.putObject(RotationTouchHelper.INSTANCE, mock(RotationTouchHelper.class));
+ mContext.putObject(RecentsAnimationDeviceState.INSTANCE, mDeviceState);
}
@Before
@@ -193,7 +195,6 @@
when(mDeviceState.canStartSystemGesture()).thenReturn(true);
when(mDeviceState.isFullyGesturalNavMode()).thenReturn(true);
when(mDeviceState.getNavBarPosition()).thenReturn(mock(NavBarPosition.class));
- when(mDeviceState.getRotationTouchHelper()).thenReturn(mock(RotationTouchHelper.class));
}
@After
diff --git a/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java b/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java
index b3c486c..ccc0311 100644
--- a/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java
+++ b/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java
@@ -102,8 +102,7 @@
true);
assertEquals(1, taskList.size());
- assertNull(taskList.get(0).task1.taskDescription.getLabel());
- assertNull(taskList.get(0).task2.taskDescription.getLabel());
+ taskList.get(0).getTasks().forEach(t -> assertNull(t.taskDescription.getLabel()));
}
@Test
@@ -133,8 +132,10 @@
false);
assertEquals(1, taskList.size());
- assertEquals(taskDescription, taskList.get(0).task1.taskDescription.getLabel());
- assertNull(taskList.get(0).task2.taskDescription.getLabel());
+ var tasks = taskList.get(0).getTasks();
+ assertEquals(2, tasks.size());
+ assertEquals(taskDescription, tasks.get(0).taskDescription.getLabel());
+ assertNull(tasks.get(1).taskDescription.getLabel());
}
@Test
diff --git a/quickstep/tests/src/com/android/quickstep/RecentsDeviceProfileRepositoryImplTest.kt b/quickstep/tests/src/com/android/quickstep/RecentsDeviceProfileRepositoryImplTest.kt
new file mode 100644
index 0000000..418d66c
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/RecentsDeviceProfileRepositoryImplTest.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep
+
+import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
+import com.android.launcher3.FakeInvariantDeviceProfileTest
+import com.android.quickstep.recents.data.RecentsDeviceProfile
+import com.android.quickstep.recents.data.RecentsDeviceProfileRepositoryImpl
+import com.android.quickstep.views.RecentsViewContainer
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+import org.mockito.quality.Strictness
+
+class RecentsDeviceProfileRepositoryImplTest : FakeInvariantDeviceProfileTest() {
+ private val recentsViewContainer: RecentsViewContainer = mock()
+
+ private lateinit var mockitoSession: StaticMockitoSession
+
+ @Before
+ override fun setUp() {
+ super.setUp()
+ mockitoSession =
+ mockitoSession()
+ .strictness(Strictness.LENIENT)
+ .mockStatic(DesktopModeStatus::class.java)
+ .startMocking()
+ whenever(recentsViewContainer.asContext()).thenReturn(context)
+ }
+
+ @After
+ fun tearDown() {
+ mockitoSession.finishMocking()
+ }
+
+ @Test
+ fun deviceProfileMappedCorrectlyForPhone() {
+ val deviceProfileRepo = RecentsDeviceProfileRepositoryImpl(recentsViewContainer)
+ initializeVarsForPhone()
+ val phoneDeviceProfile = newDP()
+ whenever(recentsViewContainer.deviceProfile).thenReturn(phoneDeviceProfile)
+
+ whenever(DesktopModeStatus.canEnterDesktopMode(any())).thenReturn(false)
+ assertThat(deviceProfileRepo.getRecentsDeviceProfile())
+ .isEqualTo(RecentsDeviceProfile(isLargeScreen = false, canEnterDesktopMode = false))
+ }
+
+ @Test
+ fun deviceProfileMappedCorrectlyForTablet() {
+ val deviceProfileRepo = RecentsDeviceProfileRepositoryImpl(recentsViewContainer)
+ initializeVarsForTablet()
+ val tabletDeviceProfile = newDP()
+ whenever(recentsViewContainer.deviceProfile).thenReturn(tabletDeviceProfile)
+
+ whenever(DesktopModeStatus.canEnterDesktopMode(any())).thenReturn(true)
+ assertThat(deviceProfileRepo.getRecentsDeviceProfile())
+ .isEqualTo(RecentsDeviceProfile(isLargeScreen = true, canEnterDesktopMode = true))
+ }
+}
diff --git a/quickstep/tests/src/com/android/quickstep/RecentsModelTest.java b/quickstep/tests/src/com/android/quickstep/RecentsModelTest.java
index 648fa93..4e34e11 100644
--- a/quickstep/tests/src/com/android/quickstep/RecentsModelTest.java
+++ b/quickstep/tests/src/com/android/quickstep/RecentsModelTest.java
@@ -39,12 +39,12 @@
import com.android.launcher3.Flags;
import com.android.launcher3.R;
+import com.android.launcher3.graphics.ThemeManager;
import com.android.launcher3.icons.IconProvider;
import com.android.quickstep.util.GroupTask;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.TaskStackChangeListeners;
-import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -67,7 +67,7 @@
private RecentTasksList mTasksList;
@Mock
- private TaskThumbnailCache.HighResLoadingState mHighResLoadingState;
+ private HighResLoadingState mHighResLoadingState;
private RecentsModel mRecentsModel;
@@ -94,7 +94,8 @@
when(mThumbnailCache.isPreloadingEnabled()).thenReturn(true);
mRecentsModel = new RecentsModel(mContext, mTasksList, mock(TaskIconCache.class),
- mThumbnailCache, mock(IconProvider.class), mock(TaskStackChangeListeners.class));
+ mThumbnailCache, mock(IconProvider.class), mock(TaskStackChangeListeners.class),
+ mock(ThemeManager.class));
mResource = mock(Resources.class);
when(mResource.getInteger((R.integer.recentsThumbnailCacheSize))).thenReturn(3);
@@ -111,10 +112,12 @@
.updateThumbnailInCache(taskArgs.capture(), /* lowResolution= */ eq(false));
GroupTask expectedGroupTask = mTaskResult.get(0);
- assertThat(taskArgs.getAllValues().get(0)).isEqualTo(
- expectedGroupTask.task1);
- assertThat(taskArgs.getAllValues().get(1)).isEqualTo(
- expectedGroupTask.task2);
+ var taskArgsValues = taskArgs.getAllValues();
+ var expectedTasks = expectedGroupTask.getTasks();
+ assertThat(taskArgsValues.size()).isEqualTo(expectedTasks.size());
+ for (int i = 0; i < expectedTasks.size(); ++i) {
+ assertThat(taskArgsValues.get(i)).isEqualTo(expectedTasks.get(i));
+ }
}
@Test
diff --git a/quickstep/tests/src/com/android/quickstep/TaplPrivateSpaceTest.java b/quickstep/tests/src/com/android/quickstep/TaplPrivateSpaceTest.java
index e065dba..88be752 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplPrivateSpaceTest.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplPrivateSpaceTest.java
@@ -165,7 +165,6 @@
}
@Test
- @ScreenRecordRule.ScreenRecord // b/355466672
public void testPrivateSpaceLockingBehaviour() throws IOException {
assumeFalse(mLauncher.isTablet()); // b/367258373
// Scroll to the bottom of All Apps
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsOverviewDesktop.kt b/quickstep/tests/src/com/android/quickstep/TaplTestsOverviewDesktop.kt
index f58c84e..b744039 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsOverviewDesktop.kt
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsOverviewDesktop.kt
@@ -57,16 +57,25 @@
.switchToOverview()
.apply { flingForward() }
.also { moveTaskToDesktop(TEST_ACTIVITY_1) }
-
TEST_ACTIVITIES.forEach { assertTestAppLaunched(it) }
- // Launch static DesktopTaskView
- val desktop =
+ // Launch static DesktopTaskView without live tile in Overview
+ val desktopTask =
mLauncher.goHome().switchToOverview().getTestActivityTask(TEST_ACTIVITIES).open()
TEST_ACTIVITIES.forEach { assertTestAppLaunched(it) }
// Launch live-tile DesktopTaskView
- desktop.switchToOverview().getTestActivityTask(TEST_ACTIVITIES).open()
+ desktopTask.switchToOverview().getTestActivityTask(TEST_ACTIVITIES).open()
+ TEST_ACTIVITIES.forEach { assertTestAppLaunched(it) }
+
+ // Launch static DesktopTaskView with live tile in Overview
+ mLauncher.goHome()
+ startTestActivity(TEST_ACTIVITY_EXTRA)
+ mLauncher.launchedAppState
+ .switchToOverview()
+ .apply { flingBackward() }
+ .getTestActivityTask(TEST_ACTIVITIES)
+ .open()
TEST_ACTIVITIES.forEach { assertTestAppLaunched(it) }
}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index ec6a9c3..15038a4 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -50,7 +50,6 @@
import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
import com.android.launcher3.util.TestUtil;
import com.android.launcher3.util.Wait;
-import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
import com.android.launcher3.util.rule.TestStabilityRule;
import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
import com.android.quickstep.TaskbarModeSwitchRule.TaskbarModeSwitch;
@@ -337,7 +336,6 @@
@Test
@NavigationModeSwitch
@PortraitLandscape
- @ScreenRecord // b/313464374
@TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/325659406
public void testQuickSwitchFromApp() throws Exception {
startTestActivity(2);
@@ -585,19 +583,14 @@
public void testExcludeFromRecents() throws Exception {
startExcludeFromRecentsTestActivity();
OverviewTask currentTask = getAndAssertLaunchedApp().switchToOverview().getCurrentTask();
- // TODO(b/342627272): the expected content description shouldn't be null but for now there
- // is a bug that causes it to sometimes be for excludeForRecents tasks.
assertTrue("Can't find ExcludeFromRecentsTestActivity after entering Overview from it",
- currentTask.containsContentDescription("ExcludeFromRecents")
- || currentTask.containsContentDescription(null));
+ currentTask.containsContentDescription("ExcludeFromRecents"));
// Going home should clear out the excludeFromRecents task.
BaseOverview overview = mLauncher.goHome().switchToOverview();
if (overview.hasTasks()) {
currentTask = overview.getCurrentTask();
assertFalse("Found ExcludeFromRecentsTestActivity after entering Overview from Home",
- currentTask.containsContentDescription(
- "ExcludeFromRecents")
- || currentTask.containsContentDescription(null));
+ currentTask.containsContentDescription("ExcludeFromRecents"));
} else {
// Presumably the test started with 0 tasks and remains that way after going home.
}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
index daa4ec3..afe6368 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
@@ -16,8 +16,6 @@
package com.android.quickstep;
-import static com.android.launcher3.config.FeatureFlags.enableSplitContextually;
-
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
@@ -89,18 +87,10 @@
.getSplitScreenMenuItem()
.click();
- if (enableSplitContextually()) {
- // We're staying in all apps, use same instance
- mLauncher.getAllApps()
- .getAppIcon(CALCULATOR_APP_NAME)
- .launchIntoSplitScreen();
- } else {
- // We're in overview, use taskbar instance
- mLauncher.getLaunchedAppState()
- .getTaskbar()
- .getAppIcon(CALCULATOR_APP_NAME)
- .launchIntoSplitScreen();
- }
+ // We're staying in all apps, use same instance
+ mLauncher.getAllApps()
+ .getAppIcon(CALCULATOR_APP_NAME)
+ .launchIntoSplitScreen();
}
@Test
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
index c24e974..08ce5e7 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
@@ -124,6 +124,7 @@
}
@Test
+ @ScreenRecordRule.ScreenRecord // b/373417111
public void testLaunchShortcut_fromTaskbarAllApps() {
getTaskbar().openAllApps()
.getAppIcon(TEST_APP_NAME)
@@ -134,7 +135,6 @@
@Test
@PortraitLandscape
- @ScreenRecordRule.ScreenRecord // b/349439239
public void testLaunchAppInSplitscreen_fromTaskbarAllApps() {
getTaskbar().openAllApps()
.getAppIcon(TEST_APP_NAME)
diff --git a/quickstep/tests/src/com/android/quickstep/TaskAnimationManagerTest.java b/quickstep/tests/src/com/android/quickstep/TaskAnimationManagerTest.java
index 098b417..a87c328 100644
--- a/quickstep/tests/src/com/android/quickstep/TaskAnimationManagerTest.java
+++ b/quickstep/tests/src/com/android/quickstep/TaskAnimationManagerTest.java
@@ -46,18 +46,12 @@
private SystemUiProxy mSystemUiProxy;
private TaskAnimationManager mTaskAnimationManager;
- protected RecentsAnimationDeviceState mRecentsAnimationDeviceState;
-
- @Before
- public void setUpRecentsAnimationDeviceState() {
- runOnMainSync(() ->
- mRecentsAnimationDeviceState = new RecentsAnimationDeviceState(mContext, true));
- }
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mTaskAnimationManager = new TaskAnimationManager(mContext, mRecentsAnimationDeviceState) {
+ mTaskAnimationManager = new TaskAnimationManager(mContext,
+ RecentsAnimationDeviceState.INSTANCE.get(mContext)) {
@Override
SystemUiProxy getSystemUiProxy() {
return mSystemUiProxy;
diff --git a/res/layout/launcher.xml b/res/layout/launcher.xml
index 83c8d6c..adf4597 100644
--- a/res/layout/launcher.xml
+++ b/res/layout/launcher.xml
@@ -29,6 +29,7 @@
android:importantForAccessibility="no">
<com.android.launcher3.views.AccessibilityActionsView
+ android:id="@+id/accessibility_action_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@string/home_screen"
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 7499264..ef34ea7 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Kon geen programme kry wat by \"<xliff:g id="QUERY">%1$s</xliff:g>\" pas nie"</string>
<string name="label_application" msgid="8531721983832654978">"App"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Alle apps"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Applys"</string>
<string name="notifications_header" msgid="1404149926117359025">"Kennisgewings"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Raak en hou om \'n kortpad te skuif."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Dubbeltik en hou om \'n kortpad te skuif of gebruik gepasmaakte handelinge."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Vouer hernoem na <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Vouer: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> items"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Vouer: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> of meer items"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Naamlose vouer"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Apppaar: <xliff:g id="APP1">%1$s</xliff:g> en <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Muurpapier en styl"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Wysig tuisskerm"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Legstukkelys is toegemaak"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Voeg by tuisskerm"</string>
<string name="action_move_here" msgid="2170188780612570250">"Skuif item hierheen"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Item is by tuisskerm gevoeg"</string>
<string name="item_removed" msgid="851119963877842327">"Item is verwyder"</string>
<string name="undo" msgid="4151576204245173321">"Ontdoen"</string>
<string name="action_move" msgid="4339390619886385032">"Skuif item"</string>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 3e64795..0d1d350 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"ከ«<xliff:g id="QUERY">%1$s</xliff:g>» ጋር የሚዛመዱ ምንም መተግበሪያዎች አልተገኙም"</string>
<string name="label_application" msgid="8531721983832654978">"መተግበሪያ"</string>
<string name="all_apps_label" msgid="5015784846527570951">"ሁሉም መተግበሪያዎች"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"የመተግበሪያዎች ዝርዝር"</string>
<string name="notifications_header" msgid="1404149926117359025">"ማሳወቂያዎች"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"አቋራጭን ለማንቀሳቀስ ይንኩ እና ይያዙ"</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"አቋራጭን ለማንቀሳቀስ ወይም ብጁ እርምጃዎችን ለመጠቀም ሁለቴ መታ ያድርጉ እና ይያዙ።"</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"አቃፊ <xliff:g id="NAME">%1$s</xliff:g> ተብሎ ዳግም ተሰይሟል"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"አቃፊ፦ <xliff:g id="NAME">%1$s</xliff:g>፣ <xliff:g id="SIZE">%2$d</xliff:g> ንጥሎች"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"አቃፊ፦ <xliff:g id="NAME">%1$s</xliff:g>፣ <xliff:g id="SIZE">%2$d</xliff:g> ወይም ተጨማሪ ንጥሎች"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"ያልተሰየመ አቃፊ"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"የመተግበሪያ ጥምረት፦ <xliff:g id="APP1">%1$s</xliff:g> እና <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"ልጣፍ እና ቅጥ"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"መነሻ ማያ ገጽን አርትዕ"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"የመግብሮች ዝርዝር ተዘግቷል"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"ወደ መነሻ ማያ ገፅ አክል"</string>
<string name="action_move_here" msgid="2170188780612570250">"ንጥልን ወደዚህ ውሰድ"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"ወደ መነሻ ማያ ገፅ ንጥል ታክሏል"</string>
<string name="item_removed" msgid="851119963877842327">"ንጥል ነገር ተንቀሳቅሷል"</string>
<string name="undo" msgid="4151576204245173321">"ቀልብስ"</string>
<string name="action_move" msgid="4339390619886385032">"ንጥልን አንቀሳቅስ"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index ba14694..a925866 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"لم يتم العثور على أي تطبيقات تتطابق مع \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="label_application" msgid="8531721983832654978">"تطبيق"</string>
<string name="all_apps_label" msgid="5015784846527570951">"جميع التطبيقات"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"قائمة التطبيقات"</string>
<string name="notifications_header" msgid="1404149926117359025">"الإشعارات"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"انقر مع الاستمرار لنقل اختصار"</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"انقر مرتين مع تثبيت إصبعك لنقل اختصار أو استخدام الإجراءات المخصّصة."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"تمت إعادة تسمية المجلد إلى <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"المجلد: <xliff:g id="NAME">%1$s</xliff:g>، <xliff:g id="SIZE">%2$d</xliff:g> عنصر"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"المجلد: <xliff:g id="NAME">%1$s</xliff:g>، <xliff:g id="SIZE">%2$d</xliff:g> عنصر أو أكثر"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"مجلد بدون اسم"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"استخدام تطبيقين في الوقت نفسه: تطبيق \"<xliff:g id="APP1">%1$s</xliff:g>\" و\"<xliff:g id="APP2">%2$s</xliff:g>\""</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"الخلفية والأسلوب"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"تعديل الشاشة الرئيسية"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"تم إغلاق قائمة الأدوات."</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"إضافة تطبيق للشاشة الرئيسية"</string>
<string name="action_move_here" msgid="2170188780612570250">"نقل العنصر إلى هنا"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"تمت إضافة العنصر إلى الشاشة الرئيسية"</string>
<string name="item_removed" msgid="851119963877842327">"تمّت إزالة العنصر."</string>
<string name="undo" msgid="4151576204245173321">"تراجع"</string>
<string name="action_move" msgid="4339390619886385032">"نقل العنصر"</string>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index 9bdc5cf..7125153 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\"ৰ সৈতে মিলা কোনো এপ্ বিচাৰি পোৱা নগ\'ল"</string>
<string name="label_application" msgid="8531721983832654978">"এপ্"</string>
<string name="all_apps_label" msgid="5015784846527570951">"আটাইবোৰ এপ্"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"এপৰ সূচী"</string>
<string name="notifications_header" msgid="1404149926117359025">"জাননীসমূহ"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"শ্বৰ্টকাট স্থানান্তৰ কৰিবলৈ দুবাৰ টিপি ধৰি ৰাখক।"</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"কোনো শ্বৰ্টকাট স্থানান্তৰ কৰিবলৈ দুবাৰ টিপি ধৰি ৰাখক অথবা কাষ্টম কাৰ্য ব্যৱহাৰ কৰক।"</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"ফ\'ল্ডাৰৰ নাম সলনি কৰি <xliff:g id="NAME">%1$s</xliff:g> কৰা হৈছে"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"ফ’ল্ডাৰ: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> টা বস্তু"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"ফ’ল্ডাৰ: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> টা অথবা তাতকৈ অধিক বস্তু"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"নামবিহীন ফ’ল্ডাৰ"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"এপ্ পেয়াৰ কৰা: <xliff:g id="APP1">%1$s</xliff:g> আৰু <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"ৱালপেপাৰ আৰু শৈলী"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"গৃহ স্ক্ৰীন সম্পাদনা কৰক"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"ৱিজেটৰ তালিকা বন্ধ কৰা হ’ল"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"গৃহ স্ক্ৰীনত যোগ কৰক"</string>
<string name="action_move_here" msgid="2170188780612570250">"বস্তুটো ইয়ালৈ স্থানান্তৰ কৰক"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"বস্তুটো গৃহ স্ক্ৰীনত যোগ কৰা হ’ল"</string>
<string name="item_removed" msgid="851119963877842327">"বস্তুটো আঁতৰোৱা হ’ল"</string>
<string name="undo" msgid="4151576204245173321">"আনডু কৰক"</string>
<string name="action_move" msgid="4339390619886385032">"বস্তু স্থানান্তৰ কৰক"</string>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index a86e2a7..7905400 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"<xliff:g id="QUERY">%1$s</xliff:g> sorğusuna uyğun tətbiq tapılmadı"</string>
<string name="label_application" msgid="8531721983832654978">"Tətbiq"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Bütün tətbiqlər"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Tətbiq siyahısı"</string>
<string name="notifications_header" msgid="1404149926117359025">"Bildirişlər"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Qısayolu daşımaq üçün toxunub saxlayın."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Qısayolu daşımaq üçün iki dəfə toxunub saxlayın və ya fərdi əməliyyatlardan istifadə edin."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Qovluq adı <xliff:g id="NAME">%1$s</xliff:g> ilə dəyişdirildi"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Qovluq: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> element"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Qovluq: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> və ya daha çox element"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Adsız qovluq"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Tətbiq cütü: <xliff:g id="APP1">%1$s</xliff:g> və <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Divar kağızı və üslub"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Əsas ekranı redaktə edin"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Vidcet siyahısı bağlandı"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Əsas ekrana əlavə edin"</string>
<string name="action_move_here" msgid="2170188780612570250">"Elementi bura köçürün"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Element əsas ekrana əlavə edildi"</string>
<string name="item_removed" msgid="851119963877842327">"Element silindi"</string>
<string name="undo" msgid="4151576204245173321">"Ləğv edin"</string>
<string name="action_move" msgid="4339390619886385032">"Elementi köçürün"</string>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index 17d8b04..d1ecaeb 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Nije pronađena nijedna aplikacija za „<xliff:g id="QUERY">%1$s</xliff:g>“"</string>
<string name="label_application" msgid="8531721983832654978">"Aplikacija"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Sve aplikacije"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Lista aplikacija"</string>
<string name="notifications_header" msgid="1404149926117359025">"Obaveštenja"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Dodirnite i zadržite radi pomeranja prečice."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Dvaput dodirnite i zadržite da biste pomerali prečicu ili koristite prilagođene radnje."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Folder je preimenovan u <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> stavke"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ili više stavki"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Neimenovani folder"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Par aplikacija: <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">"Pozadina i stil"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Izmeni početni ekran"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Lista vidžeta je zatvorena"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Dodajte na početni ekran"</string>
<string name="action_move_here" msgid="2170188780612570250">"Premesti stavku ovde"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Stavka je dodata na početni ekran"</string>
<string name="item_removed" msgid="851119963877842327">"Stavka je uklonjena"</string>
<string name="undo" msgid="4151576204245173321">"Opozovi"</string>
<string name="action_move" msgid="4339390619886385032">"Premesti stavku"</string>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index 4c209e9..3c579cb 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Праграм, якія адпавядаюць запыту \"<xliff:g id="QUERY">%1$s</xliff:g>\", не знойдзена"</string>
<string name="label_application" msgid="8531721983832654978">"Праграма"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Усе праграмы"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Спіс праграм"</string>
<string name="notifications_header" msgid="1404149926117359025">"Апавяшчэнні"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Націсніце і ўтрымлівайце ярлык для перамяшчэння."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Дакраніцеся двойчы і ўтрымлівайце, каб перамясціць ярлык або выкарыстоўваць спецыяльныя дзеянні."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Папка перайменавана ў <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Папка: <xliff:g id="NAME">%1$s</xliff:g>, элементы: <xliff:g id="SIZE">%2$d</xliff:g>"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Папка: <xliff:g id="NAME">%1$s</xliff:g>, элементы: <xliff:g id="SIZE">%2$d</xliff:g> ці больш"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Папка без назвы"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Спалучэнне праграм: <xliff:g id="APP1">%1$s</xliff:g> і <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Шпалеры і стыль"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Змяніць Галоўны экран"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Спіс віджэтаў закрыты"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Дадаць на галоўны экран"</string>
<string name="action_move_here" msgid="2170188780612570250">"Перамясціць элемент сюды"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Элемент дададзены на галоўны экран"</string>
<string name="item_removed" msgid="851119963877842327">"Элемент выдалены"</string>
<string name="undo" msgid="4151576204245173321">"Адрабіць"</string>
<string name="action_move" msgid="4339390619886385032">"Перамясціць элемент"</string>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 17a9ea3..6d44a97 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Няма намерени приложения, съответстващи на „<xliff:g id="QUERY">%1$s</xliff:g>“"</string>
<string name="label_application" msgid="8531721983832654978">"Приложение"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Всички приложения"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Списък с приложения"</string>
<string name="notifications_header" msgid="1404149926117359025">"Известия"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Докоснете и задръжте за преместване на пряк път."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Докоснете двукратно и задръжте за преместване на пряк път или използвайте персонализирани действия."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Папката е преименувана на „<xliff:g id="NAME">%1$s</xliff:g>“"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Папка: „<xliff:g id="NAME">%1$s</xliff:g>“ – <xliff:g id="SIZE">%2$d</xliff:g> елемента"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Папка: „<xliff:g id="NAME">%1$s</xliff:g>“ – <xliff:g id="SIZE">%2$d</xliff:g> или повече елементи"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Папка без име"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Двойка приложения: <xliff:g id="APP1">%1$s</xliff:g> и <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Тапет и стил"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Редактиране на началния екран"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Списъкът с приспособления е затворен"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Добавяне към началния екран"</string>
<string name="action_move_here" msgid="2170188780612570250">"Преместване на елемента тук"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Елементът е добавен към началния екран"</string>
<string name="item_removed" msgid="851119963877842327">"Елементът е премахнат"</string>
<string name="undo" msgid="4151576204245173321">"Отмяна"</string>
<string name="action_move" msgid="4339390619886385032">"Преместване на елемента"</string>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index 2809d93..b070133 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" এর সাথে মেলে এমন কোনো অ্যাপ পাওয়া যায়নি"</string>
<string name="label_application" msgid="8531721983832654978">"অ্যাপ"</string>
<string name="all_apps_label" msgid="5015784846527570951">"সব অ্যাপ"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"অ্যাপ তালিকা"</string>
<string name="notifications_header" msgid="1404149926117359025">"বিজ্ঞপ্তি"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"একটি শর্টকাট সরাতে টাচ করে ধরে রাখুন।"</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"একটি শর্টকাট সরাতে বা কাস্টম অ্যাকশন ব্যবহার করতে ডবল ট্যাপ করে ধরে রাখুন।"</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"ফোল্ডারের নাম পাল্টে <xliff:g id="NAME">%1$s</xliff:g> করা হয়েছে"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"ফোল্ডার: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g>টি আইটেম"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"ফোল্ডার: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g>টি বা তার বেশি আইটেম"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"নামবিহীন ফোল্ডার"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"অ্যাপ পেয়ার: <xliff:g id="APP1">%1$s</xliff:g> ও <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"ওয়ালপেপার এবং স্টাইল"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"হোম স্ক্রিন এডিট করুন"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"উইজেটের তালিকা বন্ধ করা হয়েছে"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"হোম স্ক্রিনে যোগ করুন"</string>
<string name="action_move_here" msgid="2170188780612570250">"এখানে আইটেম সরান"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"হোম স্ক্রীনে আইটেম যোগ করা হয়েছে"</string>
<string name="item_removed" msgid="851119963877842327">"আইটেম সরানো হয়েছে"</string>
<string name="undo" msgid="4151576204245173321">"ফিরিয়ে আনুন"</string>
<string name="action_move" msgid="4339390619886385032">"আইটেম সরান"</string>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 9aa61f2..8b7033f 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Nije pronađena nijedna aplikacija za upit \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="label_application" msgid="8531721983832654978">"Aplikacija"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Sve aplikacije"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Lista aplikacija"</string>
<string name="notifications_header" msgid="1404149926117359025">"Obavještenja"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Dodirnite i zadržite da pomjerite prečicu."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Dvaput dodirnite i zadržite da pomjerite prečicu ili da koristite prilagođene radnje."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Ime foldera je promijenjeno u <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, br. stavki: <xliff:g id="SIZE">%2$d</xliff:g>"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ili više stavki"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Neimenovani folder"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Par aplikacija: <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">"Pozadinska slika i stil"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Uredi Početni ekran"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Spisak vidžeta je zatvoren"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Dodavanje na početni ekran"</string>
<string name="action_move_here" msgid="2170188780612570250">"Premjesti stavku ovdje"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Stavka je dodana na Početni ekran."</string>
<string name="item_removed" msgid="851119963877842327">"Stavka je uklonjena"</string>
<string name="undo" msgid="4151576204245173321">"Poništi"</string>
<string name="action_move" msgid="4339390619886385032">"Premjesti stavku"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 01dc551..c5daecc 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"No s\'ha trobat cap aplicació que coincideixi amb \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="label_application" msgid="8531721983832654978">"Aplicació"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Totes les aplicacions"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Llista d\'aplicacions"</string>
<string name="notifications_header" msgid="1404149926117359025">"Notificacions"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Fes doble toc i mantén premut per moure una drecera."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Fes doble toc i mantén premut per moure una drecera o per utilitzar accions personalitzades."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"S\'ha canviat el nom de la carpeta a <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elements"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> o més elements"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Carpeta sense nom"</string>
<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>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"S\'ha tancat la llista de widgets"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Afegeix a la pantalla d\'inici"</string>
<string name="action_move_here" msgid="2170188780612570250">"Mou l\'element aquí"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"S\'ha afegit l\'element a la pantalla d\'inici"</string>
<string name="item_removed" msgid="851119963877842327">"S\'ha suprimit l\'element"</string>
<string name="undo" msgid="4151576204245173321">"Desfés"</string>
<string name="action_move" msgid="4339390619886385032">"Desplaça l\'element"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index f22a358..2ac7233 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Dotazu „<xliff:g id="QUERY">%1$s</xliff:g>“ neodpovídají žádné aplikace"</string>
<string name="label_application" msgid="8531721983832654978">"Aplikace"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Všechny aplikace"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Seznam aplikací"</string>
<string name="notifications_header" msgid="1404149926117359025">"Oznámení"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Klepnutím a podržením přesunete zkratku."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Dvojitým klepnutím a podržením přesunete zkratku, případně použijte vlastní akce."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Složka přejmenována na <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Složka: <xliff:g id="NAME">%1$s</xliff:g>, počet položek: <xliff:g id="SIZE">%2$d</xliff:g>"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Složka: <xliff:g id="NAME">%1$s</xliff:g>, počet položek: <xliff:g id="SIZE">%2$d</xliff:g> nebo více"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Nepojmenovaná složka"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Dvojice aplikací: <xliff:g id="APP1">%1$s</xliff:g> a <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Tapeta a styl"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Upravit plochu"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Seznam widgetů zavřen"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Přidat na plochu"</string>
<string name="action_move_here" msgid="2170188780612570250">"Přesunout položku sem"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Položka byla přidána na plochu"</string>
<string name="item_removed" msgid="851119963877842327">"Položka byla odstraněna"</string>
<string name="undo" msgid="4151576204245173321">"Zpět"</string>
<string name="action_move" msgid="4339390619886385032">"Přesunout položku"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 0973dc5..7979c20 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Der blev ikke fundet nogen apps, som matcher \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="label_application" msgid="8531721983832654978">"App"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Alle apps"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Liste over apps"</string>
<string name="notifications_header" msgid="1404149926117359025">"Notifikationer"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Hold en genvej nede for at flytte den."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Tryk to gange, og hold en genvej nede for at flytte den eller bruge tilpassede handlinger."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Mappen er omdøbt til <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Mappe: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elementer"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Mappe: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> eller flere elementer"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Unavngiven mappe"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Appsammenknytning: <xliff:g id="APP1">%1$s</xliff:g> og <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Baggrund og stil"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Rediger startskærm"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Listen med widgets blev lukket"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Føj til startskærm"</string>
<string name="action_move_here" msgid="2170188780612570250">"Flyt elementet hertil"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Elementet er føjet til startskærmen"</string>
<string name="item_removed" msgid="851119963877842327">"Elementet er fjernet"</string>
<string name="undo" msgid="4151576204245173321">"Fortryd"</string>
<string name="action_move" msgid="4339390619886385032">"Flyt element"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 510ddb2..098c9c1 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Keine Apps für \"<xliff:g id="QUERY">%1$s</xliff:g>\" gefunden"</string>
<string name="label_application" msgid="8531721983832654978">"App"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Alle Apps"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Liste der Apps"</string>
<string name="notifications_header" msgid="1404149926117359025">"Benachrichtigungen"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Zum Verschieben einer Verknüpfung gedrückt halten"</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Doppeltippen und halten, um eine Verknüpfung zu bewegen oder benutzerdefinierte Aktionen zu nutzen."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Ordner umbenannt in <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Ordner: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> Elemente"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Ordner: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> oder mehr Elemente"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Unbenannter Ordner"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"App-Paar: <xliff:g id="APP1">%1$s</xliff:g> und <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Hintergrund & Stil"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Startbildschirm bearbeiten"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Widgetliste geschlossen"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Zum Startbildschirm hinzufügen"</string>
<string name="action_move_here" msgid="2170188780612570250">"Element hierhin verschieben"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Element zum Startbildschirm hinzugefügt"</string>
<string name="item_removed" msgid="851119963877842327">"Element entfernt"</string>
<string name="undo" msgid="4151576204245173321">"Rückgängig"</string>
<string name="action_move" msgid="4339390619886385032">"Element verschieben"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 9aea502..a1e822a 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Δεν βρέθηκαν εφαρμογές αντιστοίχισης για \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="label_application" msgid="8531721983832654978">"Εφαρμογή"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Όλες οι εφαρμογές"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Λίστα εφαρμογών"</string>
<string name="notifications_header" msgid="1404149926117359025">"Ειδοποιήσεις"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Πατήστε παρατεταμένα για μετακίνηση συντόμευσης."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Πατήστε δύο φορές παρατεταμένα για μετακίνηση συντόμευσης ή χρήση προσαρμοσμένων ενεργειών."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Ο φάκελος μετονομάστηκε σε <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Φάκελος: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> στοιχεία"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Φάκελος: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ή περισσότερα στοιχεία"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Φάκελος χωρίς όνομα"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Ζεύγος εφαρμογών: <xliff:g id="APP1">%1$s</xliff:g> και <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Ταπετσαρία και στιλ"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Επεξεργασία αρχικής οθόνης"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Η λίστα γραφικών στοιχείων έκλεισε"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Προσθήκη στην αρχική οθόνη"</string>
<string name="action_move_here" msgid="2170188780612570250">"Μετακίνηση στοιχείου εδώ"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Το στοιχείο προστέθηκε στην αρχική οθόνη"</string>
<string name="item_removed" msgid="851119963877842327">"Το στοιχείο καταργήθηκε"</string>
<string name="undo" msgid="4151576204245173321">"Αναίρεση"</string>
<string name="action_move" msgid="4339390619886385032">"Μετακίνηση στοιχείου"</string>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index f9170eb..5a92bf4 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"No apps found matching \'<xliff:g id="QUERY">%1$s</xliff:g>\'"</string>
<string name="label_application" msgid="8531721983832654978">"App"</string>
<string name="all_apps_label" msgid="5015784846527570951">"All apps"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Apps list"</string>
<string name="notifications_header" msgid="1404149926117359025">"Notifications"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Touch & hold to move a shortcut."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Double-tap & hold to move a shortcut or use custom actions."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Folder renamed to <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> items"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> or more items"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Unnamed folder"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"App pair: <xliff:g id="APP1">%1$s</xliff:g> and <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Wallpaper and style"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Edit home screen"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Widgets list closed"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Add to home screen"</string>
<string name="action_move_here" msgid="2170188780612570250">"Move item here"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Item added to home screen"</string>
<string name="item_removed" msgid="851119963877842327">"Item removed"</string>
<string name="undo" msgid="4151576204245173321">"Undo"</string>
<string name="action_move" msgid="4339390619886385032">"Move item"</string>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index c86d9be..a3d0e0b 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -125,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Folder renamed to <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> items"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> or more items"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Unnamed folder"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"App pair: <xliff:g id="APP1">%1$s</xliff:g> and <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Wallpaper and style"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Edit Home Screen"</string>
@@ -162,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Widgets list closed"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Add to home screen"</string>
<string name="action_move_here" msgid="2170188780612570250">"Move item here"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Item added to home screen"</string>
<string name="item_removed" msgid="851119963877842327">"Item removed"</string>
<string name="undo" msgid="4151576204245173321">"Undo"</string>
<string name="action_move" msgid="4339390619886385032">"Move item"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index f9170eb..5a92bf4 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"No apps found matching \'<xliff:g id="QUERY">%1$s</xliff:g>\'"</string>
<string name="label_application" msgid="8531721983832654978">"App"</string>
<string name="all_apps_label" msgid="5015784846527570951">"All apps"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Apps list"</string>
<string name="notifications_header" msgid="1404149926117359025">"Notifications"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Touch & hold to move a shortcut."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Double-tap & hold to move a shortcut or use custom actions."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Folder renamed to <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> items"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> or more items"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Unnamed folder"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"App pair: <xliff:g id="APP1">%1$s</xliff:g> and <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Wallpaper and style"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Edit home screen"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Widgets list closed"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Add to home screen"</string>
<string name="action_move_here" msgid="2170188780612570250">"Move item here"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Item added to home screen"</string>
<string name="item_removed" msgid="851119963877842327">"Item removed"</string>
<string name="undo" msgid="4151576204245173321">"Undo"</string>
<string name="action_move" msgid="4339390619886385032">"Move item"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index f9170eb..5a92bf4 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"No apps found matching \'<xliff:g id="QUERY">%1$s</xliff:g>\'"</string>
<string name="label_application" msgid="8531721983832654978">"App"</string>
<string name="all_apps_label" msgid="5015784846527570951">"All apps"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Apps list"</string>
<string name="notifications_header" msgid="1404149926117359025">"Notifications"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Touch & hold to move a shortcut."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Double-tap & hold to move a shortcut or use custom actions."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Folder renamed to <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> items"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> or more items"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Unnamed folder"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"App pair: <xliff:g id="APP1">%1$s</xliff:g> and <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Wallpaper and style"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Edit home screen"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Widgets list closed"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Add to home screen"</string>
<string name="action_move_here" msgid="2170188780612570250">"Move item here"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Item added to home screen"</string>
<string name="item_removed" msgid="851119963877842327">"Item removed"</string>
<string name="undo" msgid="4151576204245173321">"Undo"</string>
<string name="action_move" msgid="4339390619886385032">"Move item"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 2024ca7..1d337e0 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"No hay apps que coincidan con \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="label_application" msgid="8531721983832654978">"App"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Todas las apps"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Lista de apps"</string>
<string name="notifications_header" msgid="1404149926117359025">"Notificaciones"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Mantén presionado para mover un acceso directo."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Presiona dos veces y mantén presionado para mover un acceso directo o usar acciones personalizadas."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"El nombre de la carpeta se cambió a <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elementos"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> o más elementos"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Carpeta sin nombre"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Vinculación de apps: <xliff:g id="APP1">%1$s</xliff:g> y <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Fondo de pantalla y estilo"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Editar pantalla principal"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Se cerró la lista de widgets"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Agregar a pantalla principal"</string>
<string name="action_move_here" msgid="2170188780612570250">"Mover elemento aquí"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Se agregó el elemento a la pantalla principal."</string>
<string name="item_removed" msgid="851119963877842327">"Se eliminó el elemento."</string>
<string name="undo" msgid="4151576204245173321">"Deshacer"</string>
<string name="action_move" msgid="4339390619886385032">"Mover elemento"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 30d034a..0dbcbe5 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"No se han encontrado aplicaciones que contengan \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="label_application" msgid="8531721983832654978">"Aplicación"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Todas las aplicaciones"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Lista de aplicaciones"</string>
<string name="notifications_header" msgid="1404149926117359025">"Notificaciones"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Mantén pulsado un acceso directo para moverlo."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Toca dos veces y mantén pulsado un acceso directo para moverlo o usar acciones personalizadas."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Se ha cambiado el nombre de la carpeta a <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SIZE">%2$d</xliff:g> elementos)"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SIZE">%2$d</xliff:g> o más elementos)"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Carpeta sin nombre"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Aplicaciones emparejadas: <xliff:g id="APP1">%1$s</xliff:g> y <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Fondo de pantalla y estilo"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Editar pantalla de inicio"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Lista de widgets cerrada"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Añadir a pantalla de inicio"</string>
<string name="action_move_here" msgid="2170188780612570250">"Mover elemento aquí"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Elemento añadido a la pantalla de inicio"</string>
<string name="item_removed" msgid="851119963877842327">"Elemento quitado"</string>
<string name="undo" msgid="4151576204245173321">"Deshacer"</string>
<string name="action_move" msgid="4339390619886385032">"Mover elemento"</string>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index daf6fbe..b51feb5 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Päringule „<xliff:g id="QUERY">%1$s</xliff:g>” ei vastanud ükski rakendus"</string>
<string name="label_application" msgid="8531721983832654978">"Rakendus"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Kõik rakendused"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Rakenduste loend"</string>
<string name="notifications_header" msgid="1404149926117359025">"Märguanded"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Otsetee teisaldamiseks puudutage ja hoidke all."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Otsetee teisaldamiseks või kohandatud toimingute kasutamiseks topeltpuudutage ja hoidke all."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Kausta uus nimi: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Kaust: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> üksust"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Kaust: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> või rohkem üksust"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Nimetu kaust"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Rakendusepaar: <xliff:g id="APP1">%1$s</xliff:g> ja <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Taustapilt ja stiil"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Muuda avaekraani"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Vidinate loend on suletud"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Lisa avakuvale"</string>
<string name="action_move_here" msgid="2170188780612570250">"Teisalda üksus siia"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Üksus lisati avaekraanile"</string>
<string name="item_removed" msgid="851119963877842327">"Üksus eemaldati"</string>
<string name="undo" msgid="4151576204245173321">"Võta tagasi"</string>
<string name="action_move" msgid="4339390619886385032">"Teisalda üksus"</string>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 8d14731..1fcd004 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Ez da aurkitu \"<xliff:g id="QUERY">%1$s</xliff:g>\" bilaketaren emaitzarik"</string>
<string name="label_application" msgid="8531721983832654978">"Aplikazioa"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Aplikazio guztiak"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Aplikazioen zerrenda"</string>
<string name="notifications_header" msgid="1404149926117359025">"Jakinarazpenak"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Eduki sakatuta lasterbide bat mugitzeko."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Sakatu birritan eta eduki sakatuta lasterbide bat mugitzeko edo ekintza pertsonalizatuak erabiltzeko."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Karpetari <xliff:g id="NAME">%1$s</xliff:g> izena eman zaio"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"<xliff:g id="NAME">%1$s</xliff:g> karpeta (<xliff:g id="SIZE">%2$d</xliff:g> elementu)"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"<xliff:g id="NAME">%1$s</xliff:g> karpeta (<xliff:g id="SIZE">%2$d</xliff:g> elementu edo gehiago)"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Izenik gabeko karpeta"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Aplikazio parea: <xliff:g id="APP1">%1$s</xliff:g> eta <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Horma-papera eta estiloa"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Editatu orri nagusia"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Itxi da widget-zerrenda"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Gehitu orri nagusian"</string>
<string name="action_move_here" msgid="2170188780612570250">"Ekarri elementua hona"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Gehitu da elementua orri nagusian"</string>
<string name="item_removed" msgid="851119963877842327">"Kendu da elementua"</string>
<string name="undo" msgid="4151576204245173321">"Desegin"</string>
<string name="action_move" msgid="4339390619886385032">"Mugitu elementua"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 5227a7c..b62bbfa 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"هیچ برنامهای در مطابقت با «<xliff:g id="QUERY">%1$s</xliff:g>» پیدا نشد"</string>
<string name="label_application" msgid="8531721983832654978">"برنامه"</string>
<string name="all_apps_label" msgid="5015784846527570951">"همه برنامهها"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"فهرست برنامهها"</string>
<string name="notifications_header" msgid="1404149926117359025">"اعلانها"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"برای جابهجا کردن میانبر، لمس کنید و نگه دارید."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"برای جابهجا کردن میانبر یا استفاده از کنشهای سفارشی، دو تکضرب بزنید و نگه دارید."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"نام پوشه به <xliff:g id="NAME">%1$s</xliff:g> تغییر کرد"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"پوشه: <xliff:g id="NAME">%1$s</xliff:g>، <xliff:g id="SIZE">%2$d</xliff:g> مورد"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"پوشه: <xliff:g id="NAME">%1$s</xliff:g>، <xliff:g id="SIZE">%2$d</xliff:g> مورد یا بیشتر"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"پوشه بینام"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"جفت برنامه: <xliff:g id="APP1">%1$s</xliff:g> و <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"کاغذدیواری و سبک"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"ویرایش «صفحه اصلی»"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"فهرست ابزارهها بسته شد"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"افزودن به صفحه اصلی"</string>
<string name="action_move_here" msgid="2170188780612570250">"انتقال مورد به اینجا"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"مورد به صفحه اصلی اضافه شد"</string>
<string name="item_removed" msgid="851119963877842327">"مورد حذف شد"</string>
<string name="undo" msgid="4151576204245173321">"واگرد"</string>
<string name="action_move" msgid="4339390619886385032">"انتقال مورد"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 4c27e6a..2c43a25 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"<xliff:g id="QUERY">%1$s</xliff:g> ei palauttanut sovelluksia."</string>
<string name="label_application" msgid="8531721983832654978">"Sovellus"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Kaikki sovellukset"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Sovellusluettelo"</string>
<string name="notifications_header" msgid="1404149926117359025">"Ilmoitukset"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Kosketa pitkään, niin voit siirtää pikakuvaketta."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Kaksoisnapauta ja paina pitkään, niin voit siirtää pikakuvaketta tai käyttää muokattuja toimintoja."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Kansion nimeksi vaihdettiin <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Kansio: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> kohdetta"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Kansio: <xliff:g id="NAME">%1$s</xliff:g>, ainakin <xliff:g id="SIZE">%2$d</xliff:g> kohdetta"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Nimeämätön kansio"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Sovelluspari: <xliff:g id="APP1">%1$s</xliff:g> ja <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Taustakuva ja tyyli"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Muokkaa aloitusnäyttöä"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Widget-luettelo suljettu"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Lisää aloitusnäytölle"</string>
<string name="action_move_here" msgid="2170188780612570250">"Siirrä kohde tänne"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Kohde lisättiin aloitusnäytölle."</string>
<string name="item_removed" msgid="851119963877842327">"Kohde poistettiin"</string>
<string name="undo" msgid="4151576204245173321">"Kumoa"</string>
<string name="action_move" msgid="4339390619886385032">"Siirrä kohde"</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index c348ac9..c191aae 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Aucune appli trouvée correspondant à « <xliff:g id="QUERY">%1$s</xliff:g> »"</string>
<string name="label_application" msgid="8531721983832654978">"Appli"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Toutes les applis"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Liste des applis"</string>
<string name="notifications_header" msgid="1404149926117359025">"Notifications"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Maintenez le doigt sur un raccourci pour le déplacer."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Touchez deux fois un raccourci et maintenez le doigt dessus pour le déplacer ou utiliser des actions personnalisées."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Nouveau nom du dossier : <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Dossier : <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> élément(s)"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Dossier : <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> éléments ou plus"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Dossier sans nom"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Paire d\'applis : <xliff:g id="APP1">%1$s</xliff:g> et <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Fond d\'écran et style"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Modifier l\'écran d\'accueil"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Liste des widgets fermée"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Ajouter à l\'écran d\'accueil"</string>
<string name="action_move_here" msgid="2170188780612570250">"Déplacer l\'élément ici"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Élément ajouté à l\'écran d\'accueil"</string>
<string name="item_removed" msgid="851119963877842327">"Élément retiré"</string>
<string name="undo" msgid="4151576204245173321">"Annuler"</string>
<string name="action_move" msgid="4339390619886385032">"Déplacer l\'élément"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index c44efa5..e4c40e5 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Aucune application ne correspond à la requête \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="label_application" msgid="8531721983832654978">"Application"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Toutes les applis"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Liste des applis"</string>
<string name="notifications_header" msgid="1404149926117359025">"Notifications"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Appuyez de manière prolongée pour déplacer un raccourci."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Appuyez deux fois et maintenez la pression pour déplacer un raccourci ou utiliser les actions personnalisées."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Nouveau nom du dossier : <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Dossier : <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> éléments"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Dossier : <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> éléments ou plus"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Dossier sans nom"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Paire d\'applications : <xliff:g id="APP1">%1$s</xliff:g> et <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Fond d\'écran et style"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Modifier l\'écran d\'accueil"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"La liste des widgets est fermée"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Ajouter à l\'écran d\'accueil"</string>
<string name="action_move_here" msgid="2170188780612570250">"Déplacer l\'élément ici"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"L\'élément a bien été ajouté à l\'écran d\'accueil."</string>
<string name="item_removed" msgid="851119963877842327">"Élément supprimé"</string>
<string name="undo" msgid="4151576204245173321">"Annuler"</string>
<string name="action_move" msgid="4339390619886385032">"Déplacer l\'élément"</string>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index e0b0fe6..4ded582 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Non se atoparon aplicacións que coincidan con \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="label_application" msgid="8531721983832654978">"Aplicación"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Todas as aplicacións"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Lista de aplicacións"</string>
<string name="notifications_header" msgid="1404149926117359025">"Notificacións"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Mantén premido un atallo para movelo."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Toca dúas veces un atallo e manteno premido para movelo ou utiliza accións personalizadas."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"O cartafol cambiou o nome a <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Cartafol: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elementos"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Cartafol: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elementos ou máis"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Cartafol sen nome"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Emparellamento de aplicacións: <xliff:g id="APP1">%1$s</xliff:g> e <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Estilo e fondo de pantalla"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Editar pantalla de inicio"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Pechouse a lista de widgets"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Engadir á pantalla de inicio"</string>
<string name="action_move_here" msgid="2170188780612570250">"Mover elemento aquí"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Engadiuse o elemento á pantalla de inicio"</string>
<string name="item_removed" msgid="851119963877842327">"Quitouse o elemento"</string>
<string name="undo" msgid="4151576204245173321">"Desfacer"</string>
<string name="action_move" msgid="4339390619886385032">"Mover elemento"</string>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index 5c8c2b1..4925294 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\"થી મેળ ખાતી કોઈ ઍપ્લિકેશનો મળી નથી"</string>
<string name="label_application" msgid="8531721983832654978">"ઍપ"</string>
<string name="all_apps_label" msgid="5015784846527570951">"બધી ઍપ"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"ઍપની સૂચિ"</string>
<string name="notifications_header" msgid="1404149926117359025">"નોટિફિકેશન"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"શૉર્ટકટ ખસેડવા ટચ કરીને થોડી વાર દબાવી રાખો."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"શૉર્ટકટ ખસેડવા બે વાર ટૅપ કરીને દબાવી રાખો અથવા કસ્ટમ ક્રિયાઓનો ઉપયોગ કરો."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"ફોલ્ડરનું નામ બદલીને <xliff:g id="NAME">%1$s</xliff:g> કર્યું"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"ફોલ્ડર: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> આઇટમ"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"ફોલ્ડર: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> કે વધુ આઇટમ"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"અનામાંકિત ફોલ્ડર"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"ઍપની જોડી: <xliff:g id="APP1">%1$s</xliff:g> અને <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"વૉલપેપર અને સ્ટાઇલ"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"હોમ સ્ક્રીનમાં ફેરફાર કરો"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"વિજેટની સૂચિ બંધ કરવામાં આવી છે"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"હોમ સ્ક્રીનમાં ઉમેરો"</string>
<string name="action_move_here" msgid="2170188780612570250">"આઇટમ અહીં ખસેડો"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"હોમ સ્ક્રીનમાં આઇટમ ઉમેરી"</string>
<string name="item_removed" msgid="851119963877842327">"આઇટમ કાઢી નાખી"</string>
<string name="undo" msgid="4151576204245173321">"રદ કરો"</string>
<string name="action_move" msgid="4339390619886385032">"આઇટમ ખસેડો"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 856e6fa..db2b601 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" से मिलता-जुलता कोई ऐप्लिकेशन नहीं मिला"</string>
<string name="label_application" msgid="8531721983832654978">"ऐप्लिकेशन"</string>
<string name="all_apps_label" msgid="5015784846527570951">"सभी ऐप्लिकेशन"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"ऐप्लिकेशन की सूची"</string>
<string name="notifications_header" msgid="1404149926117359025">"सूचनाएं"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"किसी शॉर्टकट को एक से दूसरी जगह ले जाने के लिए, उसे दबाकर रखें."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"किसी शॉर्टकट को एक से दूसरी जगह ले जाने के लिए, उस पर दो बार टैप करके दबाकर रखें या पसंद के मुताबिक कार्रवाइयां इस्तेमाल करें."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"फ़ोल्डर का नाम बदलकर <xliff:g id="NAME">%1$s</xliff:g> किया गया"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"फ़ोल्डर: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> आइटम"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"फ़ोल्डर: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> या इससे ज़्यादा आइटम"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"बिना नाम का फ़ोल्डर"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"साथ में इस्तेमाल किए जा सकने वाले ऐप्लिकेशन: <xliff:g id="APP1">%1$s</xliff:g> और <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"वॉलपेपर और स्टाइल"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"होम स्क्रीन में बदलाव करें"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"विजेट की सूची बंद हो गई है"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"होम स्क्रीन पर जोड़ें"</string>
<string name="action_move_here" msgid="2170188780612570250">"आइटम यहां ले जाएं"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"होम स्क्रीन में आइटम जोड़ा गया"</string>
<string name="item_removed" msgid="851119963877842327">"आइटम हटाया गया"</string>
<string name="undo" msgid="4151576204245173321">"पहले जैसा करें"</string>
<string name="action_move" msgid="4339390619886385032">"आइटम ले जाएं"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 798f156..d859f58 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Nema aplikacija podudarnih s upitom \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="label_application" msgid="8531721983832654978">"Aplikacija"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Sve aplikacije"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Popis aplikacija"</string>
<string name="notifications_header" msgid="1404149926117359025">"Obavijesti"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Dodirnite i zadržite da biste premjestili prečac."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Dvaput dodirnite i zadržite pritisak da biste premjestili prečac ili upotrijebite prilagođene radnje."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Mapa je preimenovana u <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Mapa: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> stavke"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Mapa: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ili više stavki"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Neimenovana mapa"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Par aplikacija: <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">"Pozadina i stil"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Uredi početni zaslon"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Popis widgeta zatvoren"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Dodajte na početni zaslon"</string>
<string name="action_move_here" msgid="2170188780612570250">"Premjesti stavku ovdje"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Stavka je dodana na početni zaslon"</string>
<string name="item_removed" msgid="851119963877842327">"Stavka je uklonjena"</string>
<string name="undo" msgid="4151576204245173321">"Poništi"</string>
<string name="action_move" msgid="4339390619886385032">"Premještanje stavke"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index e84f223..9f4ac5a 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Nem található alkalmazás a(z) „<xliff:g id="QUERY">%1$s</xliff:g>” lekérdezésre"</string>
<string name="label_application" msgid="8531721983832654978">"Alkalmazás"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Összes alkalmazás"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Alkalmazások listája"</string>
<string name="notifications_header" msgid="1404149926117359025">"Értesítések"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Tartsa lenyomva a parancsikont az áthelyezéshez."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Parancsikon áthelyezéséhez koppintson duplán, és tartsa nyomva az ujját, vagy használjon egyéni műveleteket."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"A mappa új neve: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Mappa: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elem"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Mappa: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> vagy több elem"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Névtelen mappa"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Alkalmazáspár: <xliff:g id="APP1">%1$s</xliff:g> és <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Háttérkép és stílus"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Kezdőképernyő szerkesztése"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Widgetlista bezárva"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Hozzáadás a kezdőképernyőhöz"</string>
<string name="action_move_here" msgid="2170188780612570250">"Elem áthelyezése ide"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Elem hozzáadva a kezdőképernyőhöz"</string>
<string name="item_removed" msgid="851119963877842327">"Elem eltávolítva"</string>
<string name="undo" msgid="4151576204245173321">"Mégse"</string>
<string name="action_move" msgid="4339390619886385032">"Elem mozgatása"</string>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index 0df104a..036e9c9 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"«<xliff:g id="QUERY">%1$s</xliff:g>» հարցմանը համապատասխանող հավելվածներ չեն գտնվել"</string>
<string name="label_application" msgid="8531721983832654978">"Հավելված"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Բոլոր հավելվածները"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Հավելվածների ցանկ"</string>
<string name="notifications_header" msgid="1404149926117359025">"Ծանուցումներ"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Հպեք և պահեք՝ դյուրանցում տեղափոխելու համար։"</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Կրկնակի հպեք և պահեք՝ դյուրանցում տեղափոխելու համար, կամ օգտվեք հատուկ գործողություններից։"</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Պանակը վերանվանվեց <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Պանակ՝ <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> տարր"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Պանակ՝ <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> կամ ավելի տարրեր"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Անանուն պանակ"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Հավելվածների զույգ՝ <xliff:g id="APP1">%1$s</xliff:g> և <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Պաստառ և ոճ"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Փոփոխել հիմնական էկրանը"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Վիջեթների ցանկը փակվեց"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Ավելացնել հիմնական էկրանին"</string>
<string name="action_move_here" msgid="2170188780612570250">"Տեղափոխել տարրն այստեղ"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Տարրն ավելացվեց հիմնական էկրանին"</string>
<string name="item_removed" msgid="851119963877842327">"Տարրը հեռացվեց"</string>
<string name="undo" msgid="4151576204245173321">"Հետարկել"</string>
<string name="action_move" msgid="4339390619886385032">"Տեղափոխել տարրը"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index c473a61..2ba2997 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Tidak ditemukan aplikasi yang cocok dengan \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="label_application" msgid="8531721983832654978">"Aplikasi"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Semua aplikasi"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Daftar aplikasi"</string>
<string name="notifications_header" msgid="1404149926117359025">"Notifikasi"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Sentuh lama untuk memindahkan pintasan."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Ketuk dua kali & tahan untuk memindahkan pintasan atau gunakan tindakan khusus."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Folder diganti namanya menjadi <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> item"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> item atau lebih"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Folder tanpa nama"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Pasangan aplikasi: <xliff:g id="APP1">%1$s</xliff:g> dan <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Wallpaper & gaya"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Edit Layar Utama"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Daftar widget ditutup"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Tambahkan ke layar utama"</string>
<string name="action_move_here" msgid="2170188780612570250">"Pindahkan item ke sini"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Item ditambahkan ke layar utama"</string>
<string name="item_removed" msgid="851119963877842327">"Item dihapus"</string>
<string name="undo" msgid="4151576204245173321">"Urungkan"</string>
<string name="action_move" msgid="4339390619886385032">"Pindahkan item"</string>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index 9e0ffac..9316f90 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Ekki fundust forrit sem samsvara „<xliff:g id="QUERY">%1$s</xliff:g>“"</string>
<string name="label_application" msgid="8531721983832654978">"Forrit"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Öll forrit"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Forritalisti"</string>
<string name="notifications_header" msgid="1404149926117359025">"Tilkynningar"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Haltu fingri á flýtileið til að færa hana."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Ýttu tvisvar og haltu fingri á flýtileið til að færa hana eða notaðu sérsniðnar aðgerðir."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Heiti möppu breytt í <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Mappa: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> atriði"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Mappa: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> eða fleiri atriði"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Mappa án heitis"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Forritapar: <xliff:g id="APP1">%1$s</xliff:g> og <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Veggfóður og stíll"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Breyta heimaskjá"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Græjulista lokað"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Bæta á heimaskjá"</string>
<string name="action_move_here" msgid="2170188780612570250">"Færa atriði hingað"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Atriði bætt á heimaskjáinn"</string>
<string name="item_removed" msgid="851119963877842327">"Atriði fjarlægt"</string>
<string name="undo" msgid="4151576204245173321">"Afturkalla"</string>
<string name="action_move" msgid="4339390619886385032">"Færa atriði"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 9bba764..75e7cd2 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -125,9 +125,10 @@
<string name="folder_renamed" msgid="1794088362165669656">"Nome della cartella sostituito con <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Cartella: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elementi"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Cartella: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> o più elementi"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Cartella senza nome"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Coppia di app: <xliff:g id="APP1">%1$s</xliff:g> and <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Sfondo e stile"</string>
- <string name="edit_home_screen" msgid="8947858375782098427">"Modifica la schermata Home"</string>
+ <string name="edit_home_screen" msgid="8947858375782098427">"Modifica schermata Home"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Impostazioni schermata Home"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Disattivata dall\'amministratore"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Consenti rotazione della schermata Home"</string>
@@ -162,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Elenco di widget chiuso"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Aggiungi alla schermata Home"</string>
<string name="action_move_here" msgid="2170188780612570250">"Sposta elemento qui"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Elemento aggiunto alla schermata Home"</string>
<string name="item_removed" msgid="851119963877842327">"Elemento rimosso"</string>
<string name="undo" msgid="4151576204245173321">"Annulla"</string>
<string name="action_move" msgid="4339390619886385032">"Sposta elemento"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index d2942d9..ec6da15 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -68,22 +68,21 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ווידג\'טים לעבודה"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"שיחות"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"כתיבת הערות"</string>
- <string name="widget_cell_tap_to_show_add_button_label" msgid="4354194214317043581">"הצגת לחצן ההוספה"</string>
- <string name="widget_cell_tap_to_hide_add_button_label" msgid="6117805205101555997">"הסתרת לחצן ההוספה"</string>
+ <string name="widget_cell_tap_to_show_add_button_label" msgid="4354194214317043581">"הצגת כפתור ההוספה"</string>
+ <string name="widget_cell_tap_to_hide_add_button_label" msgid="6117805205101555997">"הסתרת כפתור ההוספה"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"הוספה"</string>
<string name="widget_add_button_content_description" msgid="1810530016360039643">"הוספת הווידג\'ט <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widgets_list_expand_button_label" msgid="7912016136574932622">"הצגת הכול"</string>
<string name="widgets_list_expand_button_content_description" msgid="4600513860973450888">"הצגת כל הווידג\'טים"</string>
<string name="widgets_list_expanded" msgid="7374857868788557730">"כל הווידג\'טים מוצגים"</string>
- <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"אפשר לשנות את הגדרות הווידג\'ט בהקשה"</string>
+ <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"אפשר לשנות את הגדרות הווידג\'ט בלחיצה"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"שינוי הגדרות הווידג\'ט"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"חיפוש אפליקציות"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"טעינת אפליקציות מתבצעת…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"לא נמצאו אפליקציות התואמות ל-\"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="label_application" msgid="8531721983832654978">"אפליקציה"</string>
<string name="all_apps_label" msgid="5015784846527570951">"כל האפליקציות"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"רשימת האפליקציות"</string>
<string name="notifications_header" msgid="1404149926117359025">"התראות"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"כדי להעביר קיצור דרך למקום אחר יש לגעת ולא להרפות."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"כדי להעביר קיצור דרך למקום אחר או להשתמש בפעולות מותאמות אישית\' יש ללחוץ פעמיים ולא להרפות."</string>
@@ -111,7 +110,7 @@
<string name="permdesc_write_settings" msgid="726859348127868466">"מאפשרת לאפליקציה לשנות את ההגדרות וקיצורי הדרך בדף הבית."</string>
<string name="gadget_error_text" msgid="740356548025791839">"לא ניתן לטעון את הווידג\'ט"</string>
<string name="gadget_setup_text" msgid="8348374825537681407">"הגדרות הווידג\'ט"</string>
- <string name="gadget_complete_setup_text" msgid="309040266978007925">"צריך להקיש כדי לסיים את תהליך ההגדרה"</string>
+ <string name="gadget_complete_setup_text" msgid="309040266978007925">"צריך ללחוץ כדי לסיים את תהליך ההגדרה"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"זוהי אפליקציית מערכת ולא ניתן להסיר את התקנתה."</string>
<string name="folder_hint_text" msgid="5174843001373488816">"עריכת השם"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> מושבתת"</string>
@@ -120,12 +119,13 @@
<string name="workspace_scroll_format" msgid="8458889198184077399">"מסך הבית %1$d מתוך %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"מסך הבית חדש"</string>
<string name="folder_opened" msgid="94695026776264709">"תיקייה פתוחה, <xliff:g id="WIDTH">%1$d</xliff:g> על <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
- <string name="folder_tap_to_close" msgid="4625795376335528256">"יש להקיש כדי לסגור את התיקייה"</string>
- <string name="folder_tap_to_rename" msgid="4017685068016979677">"יש להקיש כדי לשמור שינוי שם"</string>
+ <string name="folder_tap_to_close" msgid="4625795376335528256">"יש ללחוץ כדי לסגור את התיקייה"</string>
+ <string name="folder_tap_to_rename" msgid="4017685068016979677">"יש ללחוץ כדי לשמור שינוי שם"</string>
<string name="folder_closed" msgid="4100806530910930934">"התיקייה נסגרה"</string>
<string name="folder_renamed" msgid="1794088362165669656">"שם התיקייה שונה ל-<xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"תיקייה: <xliff:g id="NAME">%1$s</xliff:g>, מספר הפריטים: <xliff:g id="SIZE">%2$d</xliff:g>"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"תיקייה: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> פריטים או יותר"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"תיקייה ללא שם"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"צמד אפליקציות: <xliff:g id="APP1">%1$s</xliff:g> ו-<xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"טפט וסגנון"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"עריכה של מסך הבית"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"רשימת הווידג\'טים נסגרה"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"הוספה למסך הבית"</string>
<string name="action_move_here" msgid="2170188780612570250">"העברת הפריט לכאן"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"הפריט הועבר אל מסך הבית"</string>
<string name="item_removed" msgid="851119963877842327">"הפריט הוסר"</string>
<string name="undo" msgid="4151576204245173321">"ביטול"</string>
<string name="action_move" msgid="4339390619886385032">"העברת הפריט"</string>
@@ -203,7 +202,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>
- <string name="private_space_secondary_label" msgid="9203933341714508907">"יש להקיש כדי להגדיר או לפתוח"</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_unlock_button_content_description" msgid="9181551784092204234">"פרטי, פתוח."</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 9c40784..3d42652 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"「<xliff:g id="QUERY">%1$s</xliff:g>」に一致するアプリは見つかりませんでした"</string>
<string name="label_application" msgid="8531721983832654978">"アプリ"</string>
<string name="all_apps_label" msgid="5015784846527570951">"すべてのアプリ"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"アプリ一覧"</string>
<string name="notifications_header" msgid="1404149926117359025">"通知"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"長押ししてショートカットを移動してください。"</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"ショートカットをダブルタップして長押ししながら移動するか、カスタム操作を使用してください。"</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"フォルダの名前を「<xliff:g id="NAME">%1$s</xliff:g>」に変更しました"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"フォルダ: <xliff:g id="NAME">%1$s</xliff:g>、<xliff:g id="SIZE">%2$d</xliff:g> 件のアイテム"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"フォルダ: <xliff:g id="NAME">%1$s</xliff:g>、<xliff:g id="SIZE">%2$d</xliff:g> 件以上のアイテム"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"名前のないフォルダ"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"アプリのペア設定: <xliff:g id="APP1">%1$s</xliff:g> と <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"壁紙とスタイル"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"ホーム画面を編集"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"ウィジェット リストを閉じました"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"ホーム画面に追加"</string>
<string name="action_move_here" msgid="2170188780612570250">"アイテムをここに移動"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"アイテムをホーム画面に追加しました"</string>
<string name="item_removed" msgid="851119963877842327">"アイテムを削除しました"</string>
<string name="undo" msgid="4151576204245173321">"元に戻す"</string>
<string name="action_move" msgid="4339390619886385032">"アイテムを移動"</string>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index 05252e1..6a6d30d 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"„<xliff:g id="QUERY">%1$s</xliff:g>“-ის თანხვედრი აპები არ მოიძებნა"</string>
<string name="label_application" msgid="8531721983832654978">"აპი"</string>
<string name="all_apps_label" msgid="5015784846527570951">"ყველა აპი"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"აპების სია"</string>
<string name="notifications_header" msgid="1404149926117359025">"შეტყობინებები"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"შეხებით აირჩიეთ და გეჭიროთ მალსახმობის გადასაადგილებლად."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"ორმაგი შეხებით აირჩიეთ და გეჭიროთ მალსახმობის გადასაადგილებლად ან მორგებული მოქმედებების გამოსაყენებლად."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"საქაღალდეს შეეცვალა სახელი „<xliff:g id="NAME">%1$s</xliff:g>“-ად"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"საქაღალდე: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ერთეული"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"საქაღალდე: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ან მეტი ერთეული"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"უსახელო საქაღალდე"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"აპთა წყვილი: <xliff:g id="APP1">%1$s</xliff:g> და <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"ფონი და სტილი"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"მთავარი ეკრანის რედაქტირება"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"ვიჯეტების სია დაიხურა"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"მთავარ ეკრანზე დამატება"</string>
<string name="action_move_here" msgid="2170188780612570250">"ერთეულის გადაადგილება აქ"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"ერთეული დაემატა მთავარ ეკრანს"</string>
<string name="item_removed" msgid="851119963877842327">"ერთეული წაიშალა"</string>
<string name="undo" msgid="4151576204245173321">"მოქმედების გაუქმება"</string>
<string name="action_move" msgid="4339390619886385032">"ერთეულის გადაადგილება"</string>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index 56fc008..4f457ee 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" сұрауына сәйкес келетін қолданбалар жоқ"</string>
<string name="label_application" msgid="8531721983832654978">"Қолданба"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Барлық қолданба"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Қолданбалар тізімі"</string>
<string name="notifications_header" msgid="1404149926117359025">"Хабарландырулар"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Таңбашаны жылжыту үшін басып тұрыңыз."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Таңбашаны жылжыту үшін екі рет түртіңіз де, ұстап тұрыңыз немесе арнаулы әрекеттерді пайдаланыңыз."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Қалта атауы <xliff:g id="NAME">%1$s</xliff:g> болып өзгертілді"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Қалта: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> элемент бар"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Қалта: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> не одан көп элемент бар"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Атауы жоқ қалта"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Қолданбаларды жұптау: <xliff:g id="APP1">%1$s</xliff:g> және <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Тұсқағаз және стиль"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Негізгі экранды өзгерту"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Видджеттер тізімі жабылды"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Негізгі экранға қосу"</string>
<string name="action_move_here" msgid="2170188780612570250">"Элементті мұнда жылжыту"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Элемент негізгі экранға қосылды"</string>
<string name="item_removed" msgid="851119963877842327">"Элемент жойылды"</string>
<string name="undo" msgid="4151576204245173321">"Қайтару"</string>
<string name="action_move" msgid="4339390619886385032">"Элементті жылжыту"</string>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index 2845d1c..6f3fb5c 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"រកមិនឃើញកម្មវិធីដែលត្រូវគ្នាជាមួយ \"<xliff:g id="QUERY">%1$s</xliff:g>\" ទេ"</string>
<string name="label_application" msgid="8531721983832654978">"កម្មវិធី"</string>
<string name="all_apps_label" msgid="5015784846527570951">"កម្មវិធីទាំងអស់"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"បញ្ជីកម្មវិធី"</string>
<string name="notifications_header" msgid="1404149926117359025">"ការជូនដំណឹង"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"ចុចឱ្យជាប់ដើម្បីផ្លាស់ទីផ្លូវកាត់។"</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"ចុចពីរដង រួចសង្កត់ឱ្យជាប់ ដើម្បីផ្លាស់ទីផ្លូវកាត់ ឬប្រើសកម្មភាពតាមបំណង។"</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"បានប្ដូរឈ្មោះថតជា <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"ថត៖ <xliff:g id="NAME">%1$s</xliff:g>, ធាតុ <xliff:g id="SIZE">%2$d</xliff:g>"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"ថត៖ <xliff:g id="NAME">%1$s</xliff:g>, ធាតុ <xliff:g id="SIZE">%2$d</xliff:g> ឬច្រើនជាងនេះ"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"ថតដែលគ្មានឈ្មោះ"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"គូកម្មវិធី៖ <xliff:g id="APP1">%1$s</xliff:g> និង <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"ផ្ទាំងរូបភាព និងរចនាបថ"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"កែអេក្រង់ដើម"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"បានបិទបញ្ជីធាតុក្រាហ្វិក"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"បញ្ចូលទៅក្នុងអេក្រង់ដើម"</string>
<string name="action_move_here" msgid="2170188780612570250">"ផ្លាស់ធាតុមកទីនេះ"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"ធាតុដែលត្រូវបានបន្ថែមទៅអេក្រង់ដើម"</string>
<string name="item_removed" msgid="851119963877842327">"បានដកធាតុចេញ"</string>
<string name="undo" msgid="4151576204245173321">"ត្រឡប់វិញ"</string>
<string name="action_move" msgid="4339390619886385032">"ផ្លាស់ទីធាតុ"</string>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 14f5968..5cb996c 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" ಹೊಂದಿಕೆಯ ಯಾವುದೇ ಅಪ್ಲಿಕೇಶನ್ಗಳು ಕಂಡುಬಂದಿಲ್ಲ"</string>
<string name="label_application" msgid="8531721983832654978">"ಆ್ಯಪ್"</string>
<string name="all_apps_label" msgid="5015784846527570951">"ಎಲ್ಲಾ ಆ್ಯಪ್ಗಳು"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"ಆ್ಯಪ್ಗಳ ಪಟ್ಟಿ"</string>
<string name="notifications_header" msgid="1404149926117359025">"ನೋಟಿಫಿಕೇಶನ್ಗಳು"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"ಶಾರ್ಟ್ಕಟ್ ಸರಿಸಲು ಸ್ಪರ್ಶಿಸಿ ಮತ್ತು ಹಿಡಿದುಕೊಳ್ಳಿ."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"ಶಾರ್ಟ್ಕಟ್ ಸರಿಸಲು ಅಥವಾ ಕಸ್ಟಮ್ ಕ್ರಿಯೆಗಳನ್ನು ಬಳಸಲು ಡಬಲ್-ಟ್ಯಾಪ್ ಮಾಡಿ ಮತ್ತು ಹಿಡಿದುಕೊಳ್ಳಿ."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"ಫೋಲ್ಡರ್ ಅನ್ನು <xliff:g id="NAME">%1$s</xliff:g> ಗೆ ಮರುಹೆಸರಿಸಲಾಗಿದೆ"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"ಫೋಲ್ಡರ್: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ಐಟಂಗಳು"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"ಫೋಲ್ಡರ್: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ಅಥವಾ ಹೆಚ್ಚಿನ ಐಟಂಗಳು"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"ಹೆಸರಿಲ್ಲದ ಫೋಲ್ಡರ್"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"ಆ್ಯಪ್ ಜೋಡಿ: <xliff:g id="APP1">%1$s</xliff:g> ಮತ್ತು <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"ವಾಲ್ಪೇಪರ್ ಮತ್ತು ಶೈಲಿ"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"ಹೋಮ್ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಎಡಿಟ್ ಮಾಡಿ"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"ವಿಜೆಟ್ ಪಟ್ಟಿಯನ್ನು ಮುಚ್ಚಲಾಗಿದೆ"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"ಹೋಮ್ ಸ್ಕ್ರೀನ್ಗೆ ಸೇರಿಸಿ"</string>
<string name="action_move_here" msgid="2170188780612570250">"ಐಟಂ ಇಲ್ಲಿಗೆ ಸರಿಸಿ"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"ಹೋಮ್ ಸ್ಕ್ರೀನ್ಗೆ ಐಟಂ ಸೇರಿಸಲಾಗಿದೆ"</string>
<string name="item_removed" msgid="851119963877842327">"ಐಟಂ ತೆಗೆದುಹಾಕಲಾಗಿದೆ"</string>
<string name="undo" msgid="4151576204245173321">"ರದ್ದುಮಾಡಿ"</string>
<string name="action_move" msgid="4339390619886385032">"ಐಟಂ ಸರಿಸಿ"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 00827ca..e5cae63 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"\'<xliff:g id="QUERY">%1$s</xliff:g>\'과(와) 일치하는 앱이 없습니다."</string>
<string name="label_application" msgid="8531721983832654978">"앱"</string>
<string name="all_apps_label" msgid="5015784846527570951">"모든 앱"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"앱 목록"</string>
<string name="notifications_header" msgid="1404149926117359025">"알림"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"길게 터치하여 바로가기를 이동하세요."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"두 번 탭한 다음 길게 터치하여 바로가기를 이동하거나 맞춤 작업을 사용하세요."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"폴더 이름 변경: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"폴더: <xliff:g id="NAME">%1$s</xliff:g>, 항목 <xliff:g id="SIZE">%2$d</xliff:g>개"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"폴더: <xliff:g id="NAME">%1$s</xliff:g>, 항목 <xliff:g id="SIZE">%2$d</xliff:g>개 이상"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"이름 없는 폴더"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"앱 페어링: <xliff:g id="APP1">%1$s</xliff:g> 및 <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"배경화면 및 스타일"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"홈 화면 수정"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"위젯 목록 닫힘"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"홈 화면에 추가"</string>
<string name="action_move_here" msgid="2170188780612570250">"여기에 항목을 이동"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"홈 화면에 항목 추가됨"</string>
<string name="item_removed" msgid="851119963877842327">"항목 삭제됨"</string>
<string name="undo" msgid="4151576204245173321">"실행취소"</string>
<string name="action_move" msgid="4339390619886385032">"항목 이동"</string>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index bd95a2d..b6b5b66 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" сурамына дал келген колдонмолор табылган жок"</string>
<string name="label_application" msgid="8531721983832654978">"Колдонмо"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Бардык колдонмолор"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Колдонмолор тизмеси"</string>
<string name="notifications_header" msgid="1404149926117359025">"Билдирмелер"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Ыкчам баскычты жылдыруу үчүн коё бербей басып туруңуз."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Ыкчам баскычты жылдыруу үчүн эки жолу таптап, кармап туруңуз же ыңгайлаштырылган аракеттерди колдонуңуз."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Фолдердин аты <xliff:g id="NAME">%1$s</xliff:g> деп өзгөртүлдү"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"<xliff:g id="NAME">%1$s</xliff:g> папкасындагы объекттер: <xliff:g id="SIZE">%2$d</xliff:g>"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"<xliff:g id="NAME">%1$s</xliff:g> папкасындагы объекттер: <xliff:g id="SIZE">%2$d</xliff:g> же андан көбүрөөк"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Аталышы жок папка"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Эки колдонмону бир маалда пайдалануу: <xliff:g id="APP1">%1$s</xliff:g> жана <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Тушкагаз жана стиль"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Башкы экранды түзөтүү"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Виджеттердин тизмеси жабык"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Башкы экранга кошуу"</string>
<string name="action_move_here" msgid="2170188780612570250">"Бул нерсени бул жерге жылдыруу"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Башкы экранга кошулду"</string>
<string name="item_removed" msgid="851119963877842327">"Жоюлду"</string>
<string name="undo" msgid="4151576204245173321">"Кайтаруу"</string>
<string name="action_move" msgid="4339390619886385032">"Муну жылдыруу"</string>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index 9b0971d..457c185 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"ບໍ່ພົບແອັບທີ່ກົງກັບ \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="label_application" msgid="8531721983832654978">"ແອັບ"</string>
<string name="all_apps_label" msgid="5015784846527570951">"ແອັບທັງໝົດ"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"ລາຍຊື່ແອັບ"</string>
<string name="notifications_header" msgid="1404149926117359025">"ການແຈ້ງເຕືອນ"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"ແຕະຄ້າງໄວ້ເພື່ອຍ້າຍທາງລັດ."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"ແຕະສອງເທື່ອຄ້າງໄວ້ເພື່ອຍ້າຍທາງລັດ ຫຼື ໃຊ້ຄຳສັ່ງກຳນົດເອງ."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"ປ່ຽນຊື່ໂຟນເດີເປັນ <xliff:g id="NAME">%1$s</xliff:g> ແລ້ວ"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"ໂຟນເດີ: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ລາຍການ"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"ໂຟນເດີ: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ຫຼື ລາຍການເພີ່ມເຕີມ"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"ໂຟນເດີທີ່ບໍ່ມີຊື່"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"ຈັບຄູ່ແອັບ: <xliff:g id="APP1">%1$s</xliff:g> ແລະ <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"ຮູບພື້ນຫຼັງ ແລະ ຮູບແບບ"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"ແກ້ໄຂໂຮມສະກຣີນ"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"ປິດລາຍຊື່ວິດເຈັດແລ້ວ"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"ເພີ່ມໃສ່ໂຮມສະກຣີນ"</string>
<string name="action_move_here" msgid="2170188780612570250">"Move item here"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"ເພີ່ມລາຍການໃສ່ໜ້າຈໍຫຼັກແລ້ວ"</string>
<string name="item_removed" msgid="851119963877842327">"ເອົາລາຍການອອກໄປແລ້ວ"</string>
<string name="undo" msgid="4151576204245173321">"ຍົກເລີກ"</string>
<string name="action_move" msgid="4339390619886385032">"ຍ້າຍລາຍການ"</string>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 4738e26..b23b97b 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Nerasta jokių užklausą „<xliff:g id="QUERY">%1$s</xliff:g>“ atitinkančių programų"</string>
<string name="label_application" msgid="8531721983832654978">"Programa"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Visos programos"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Programų sąrašas"</string>
<string name="notifications_header" msgid="1404149926117359025">"Pranešimai"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Dukart pal. ir palaik., kad perk. spart. klavišą."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Dukart palieskite ir palaikykite, kad perkeltumėte spartųjį klavišą ar naudotumėte tinkintus veiksmus."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Aplankas pervardytas kaip „<xliff:g id="NAME">%1$s</xliff:g>“"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Aplankas: „<xliff:g id="NAME">%1$s</xliff:g>“, elementų: <xliff:g id="SIZE">%2$d</xliff:g>"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Aplankas: „<xliff:g id="NAME">%1$s</xliff:g>“, elementų: <xliff:g id="SIZE">%2$d</xliff:g> ar daugiau"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Aplankas be pavadinimo"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Programų pora: „<xliff:g id="APP1">%1$s</xliff:g>“ ir „<xliff:g id="APP2">%2$s</xliff:g>“"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Ekrano fonas ir stilius"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Redaguoti pagrindinį ekraną"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Valdiklių sąrašas uždarytas"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Pridėti prie pagrind. ekrano"</string>
<string name="action_move_here" msgid="2170188780612570250">"Perkelti elementą čia"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Elementas pridėtas prie pagrindinio ekrano"</string>
<string name="item_removed" msgid="851119963877842327">"Elementas perkeltas"</string>
<string name="undo" msgid="4151576204245173321">"Anuliuoti"</string>
<string name="action_move" msgid="4339390619886385032">"Perkelti elementą"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 7e6276d..857b492 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Vaicājumam “<xliff:g id="QUERY">%1$s</xliff:g>” neatbilda neviena lietotne"</string>
<string name="label_application" msgid="8531721983832654978">"Lietotne"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Visas lietotnes"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Lietotņu saraksts"</string>
<string name="notifications_header" msgid="1404149926117359025">"Paziņojumi"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Lai pārvietotu saīsni, pieskarieties un turiet."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Lai pārvietotu saīsni, uz tās veiciet dubultskārienu un turiet. Varat arī veikt pielāgotas darbības."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Mape pārdēvēta par: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Mape <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> vienumi"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Mape <xliff:g id="NAME">%1$s</xliff:g>, vienumu skaits mapē: vismaz <xliff:g id="SIZE">%2$d</xliff:g>"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Mape bez nosaukuma"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Lietotņu pāris: <xliff:g id="APP1">%1$s</xliff:g> un <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Fona tapete un stils"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Rediģēt sākuma ekrānu"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Logrīku saraksts aizvērts"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Pievienot sākuma ekrānam"</string>
<string name="action_move_here" msgid="2170188780612570250">"Pārvietot vienumu šeit"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Vienums pievienots sākuma ekrānam"</string>
<string name="item_removed" msgid="851119963877842327">"Vienums noņemts"</string>
<string name="undo" msgid="4151576204245173321">"Atsaukt"</string>
<string name="action_move" msgid="4339390619886385032">"Pārvietot vienumu"</string>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 9e8f464..450d5d6 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Не се најдени апликации што одговараат на „<xliff:g id="QUERY">%1$s</xliff:g>“"</string>
<string name="label_application" msgid="8531721983832654978">"Апликација"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Сите апликации"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Список со апликации"</string>
<string name="notifications_header" msgid="1404149926117359025">"Известувања"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Допрете и задржете за да преместите кратенка."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Допрете двапати и задржете за да преместите кратенка или користете приспособени дејства."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Папката е преименувана во <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Папка: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ставки"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Папка: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> или повеќе ставки"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Неименувана папка"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Пар апликации: <xliff:g id="APP1">%1$s</xliff:g> и <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Тапет и стил"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Изменете го почетниот екран"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Списокот со виџети е затворен"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Додај на почетниот екран"</string>
<string name="action_move_here" msgid="2170188780612570250">"Премести ја ставката овде"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Ставката е додадена на почетниот екран"</string>
<string name="item_removed" msgid="851119963877842327">"Ставката е отстранета"</string>
<string name="undo" msgid="4151576204245173321">"Врати"</string>
<string name="action_move" msgid="4339390619886385032">"Премести ја ставката"</string>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index a770603..4d65869 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" എന്നതുമായി പൊരുത്തപ്പെടുന്ന ആപ്പുകളൊന്നും കണ്ടെത്തിയില്ല"</string>
<string name="label_application" msgid="8531721983832654978">"ആപ്പ്"</string>
<string name="all_apps_label" msgid="5015784846527570951">"എല്ലാ ആപ്പുകളും"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"ആപ്പുകളുടെ ലിസ്റ്റ്"</string>
<string name="notifications_header" msgid="1404149926117359025">"അറിയിപ്പുകൾ"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"കുറുക്കുവഴി നീക്കാൻ സ്പർശിച്ച് പിടിക്കുക."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"കുറുക്കുവഴി നീക്കാൻ ഡബിൾ ടാപ്പ് ചെയ്യൂ, ഹോൾഡ് ചെയ്യൂ അല്ലെങ്കിൽ ഇഷ്ടാനുസൃത പ്രവർത്തനങ്ങൾ ഉപയോഗിക്കൂ."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"ഫോൾഡറിന്റെ പേര് <xliff:g id="NAME">%1$s</xliff:g> എന്നായി മാറ്റി"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"ഫോൾഡർ: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ഇനങ്ങൾ"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"ഫോൾഡർ: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> അല്ലെങ്കിൽ അതിലധികം ഇനങ്ങൾ"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"പേരിടാത്ത ഫോൾഡർ"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"ആപ്പ് ജോടി: <xliff:g id="APP1">%1$s</xliff:g>, <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"വാൾപേപ്പറും സ്റ്റൈലും"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"ഹോം സ്ക്രീൻ എഡിറ്റ് ചെയ്യുക"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"വിജറ്റുകളുടെ ലിസ്റ്റ് അവസാനിപ്പിച്ചു"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"ഹോം സ്ക്രീനിലേക്ക് ചേർക്കുക"</string>
<string name="action_move_here" msgid="2170188780612570250">"ഇനം ഇവിടേക്ക് നീക്കുക"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"ഹോം സ്ക്രീനിൽ ഇനം ചേർത്തു"</string>
<string name="item_removed" msgid="851119963877842327">"ഇനം നീക്കംചെയ്തു"</string>
<string name="undo" msgid="4151576204245173321">"പഴയപടിയാക്കുക"</string>
<string name="action_move" msgid="4339390619886385032">"ഇനം നീക്കുക"</string>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index 5c01ca5..9e40f08 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\"-д тохирох апп олдсонгүй"</string>
<string name="label_application" msgid="8531721983832654978">"Апп"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Бүх апп"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Аппын жагсаалт"</string>
<string name="notifications_header" msgid="1404149926117359025">"Мэдэгдэл"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Товчлолыг зөөхийн тулд хүрээд, удаан дарна уу."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Товчлолыг зөөх эсвэл захиалгат үйлдлийг ашиглахын тулд хоёр товшоод, удаан дарна уу."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Фолдерын нэр <xliff:g id="NAME">%1$s</xliff:g> болов"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Фолдер: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> зүйл"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Фолдер: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> эсвэл үүнээс олон зүйл"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Нэргүй фолдер"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Апп хослуулалт: <xliff:g id="APP1">%1$s</xliff:g> болон <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Дэлгэцийн зураг, загвар"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Үндсэн нүүрийг засах"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Жижиг хэрэгслийн жагсаалтыг хаасан"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Үндсэн нүүрэнд нэмэх"</string>
<string name="action_move_here" msgid="2170188780612570250">"Энд байршуулах"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Нүүр дэлгэцэнд нэмсэн зүйл"</string>
<string name="item_removed" msgid="851119963877842327">"Зүйлийг устгалаа"</string>
<string name="undo" msgid="4151576204245173321">"Болих"</string>
<string name="action_move" msgid="4339390619886385032">"Зөөх"</string>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index ca7d3f9..8907bcf 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" शी जुळणारे कोणतेही अॅप्स आढळले नाहीत"</string>
<string name="label_application" msgid="8531721983832654978">"ॲप"</string>
<string name="all_apps_label" msgid="5015784846527570951">"सर्व अॅप्स"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"अॅप्स सूची"</string>
<string name="notifications_header" msgid="1404149926117359025">"सूचना"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"शॉर्टकट हलवण्यासाठी स्पर्श करा आणि धरून ठेवा."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"शॉर्टकट हलवण्यासाठी किंवा कस्टम कृती वापरण्यासाठी दोनदा टॅप करा आणि धरून ठेवा."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"फोल्डरचे नाव बदलून <xliff:g id="NAME">%1$s</xliff:g> असे ठेवले"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"फोल्डर: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> आयटम"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"फोल्डर: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> किंवा त्याहून अधिक आयटम"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"नाव नसलेले फोल्डर"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"ॲपची जोडी: <xliff:g id="APP1">%1$s</xliff:g> आणि <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"वॉलपेपर आणि शैली"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"होम स्क्रीन संपादित करा"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"विजेट सूची बंद केली"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"होम स्क्रीनवर जोडा"</string>
<string name="action_move_here" msgid="2170188780612570250">"आयटम येथे हलवा"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"आयटम मुख्य स्क्रीनवर जोडला"</string>
<string name="item_removed" msgid="851119963877842327">"आयटम काढून टाकला"</string>
<string name="undo" msgid="4151576204245173321">"पहिल्यासारखे करा"</string>
<string name="action_move" msgid="4339390619886385032">"आयटम हलवा"</string>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index 77563ae..90245af 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Tiada apl yang ditemui sepadan dengan \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="label_application" msgid="8531721983832654978">"Apl"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Semua apl"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Senarai apl"</string>
<string name="notifications_header" msgid="1404149926117359025">"Pemberitahuan"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Sentuh & tahan untuk menggerakkan pintasan."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Ketik dua kali & tahan untuk menggerakkan pintasan atau menggunakan tindakan tersuai."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Folder dinamakan semula kepada <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> item"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> atau lebih banyak item"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Folder tidak bernama"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Gandingan apl: <xliff:g id="APP1">%1$s</xliff:g> dan <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Hiasan latar & gaya"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Edit Skrin Utama"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Senarai widget ditutup"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Tambahkan pada skrin utama"</string>
<string name="action_move_here" msgid="2170188780612570250">"Alihkan item ke sini"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Item ditambahkan pada skrin utama"</string>
<string name="item_removed" msgid="851119963877842327">"Item dialih keluar"</string>
<string name="undo" msgid="4151576204245173321">"Buat asal"</string>
<string name="action_move" msgid="4339390619886385032">"Alihkan Item"</string>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index 720bc7a..d6410da 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" နှင့်ကိုက်ညီသည့် အပ်ပ်များကို မတွေ့ပါ"</string>
<string name="label_application" msgid="8531721983832654978">"အက်ပ်"</string>
<string name="all_apps_label" msgid="5015784846527570951">"အက်ပ်အားလုံး"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"အက်ပ်စာရင်း"</string>
<string name="notifications_header" msgid="1404149926117359025">"အကြောင်းကြားချက်များ"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"ဖြတ်လမ်းလင့်ခ်ကို ရွှေ့ရန် နှစ်ချက်တို့ပြီး ဖိထားပါ။"</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"ဖြတ်လမ်းလင့်ခ်ကို ရွှေ့ရန် (သို့) စိတ်ကြိုက်လုပ်ဆောင်ချက်များကို သုံးရန် နှစ်ချက်တို့ပြီး ဖိထားပါ။"</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"ပြောင်းလဲလိုက်သော အကန့်အမည် <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"ဖိုင်တွဲ - <xliff:g id="NAME">%1$s</xliff:g>၊ <xliff:g id="SIZE">%2$d</xliff:g> ဖိုင်များ"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"ဖိုင်တွဲ - <xliff:g id="NAME">%1$s</xliff:g>၊ <xliff:g id="SIZE">%2$d</xliff:g> သို့မဟုတ် နောက်ထပ်ဖိုင်များ"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"အမည်ပေးမထားသောဖိုင်တွဲ"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"အက်ပ်တွဲချိတ်ခြင်း- <xliff:g id="APP1">%1$s</xliff:g> နှင့် <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"နောက်ခံနှင့် ပုံစံ"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"ပင်မစာမျက်နှာ တည်းဖြတ်ရန်"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"ဝိဂျက်စာရင်းကို ပိတ်ထားသည်"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"ပင်မစာမျက်နှာတွင် ထည့်ရန်"</string>
<string name="action_move_here" msgid="2170188780612570250">"၎င်းအား ဤသို့ ရွှေ့ပါ"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"ပင်မ ဖန်မျက်နှာပြင်သို့ ထည့်ပြီး၏"</string>
<string name="item_removed" msgid="851119963877842327">"ဖယ်ရှားပြီးပြီ"</string>
<string name="undo" msgid="4151576204245173321">"နောက်ပြန်ရန်"</string>
<string name="action_move" msgid="4339390619886385032">"၎င်းအား ရွှေ့ပါ"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index d7804f4..bdce1f4 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Fant ingen apper som samsvarer med «<xliff:g id="QUERY">%1$s</xliff:g>»"</string>
<string name="label_application" msgid="8531721983832654978">"App"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Alle apper"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Appliste"</string>
<string name="notifications_header" msgid="1404149926117359025">"Varsler"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Trykk og hold for å flytte en snarvei."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Dobbelttrykk og hold for å flytte en snarvei eller bruke tilpassede handlinger."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Mappen heter nå <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Mappe: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elementer"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Mappe: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> eller flere elementer"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Mappe uten navn"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Apptilkobling: <xliff:g id="APP1">%1$s</xliff:g> og <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Bakgrunn og stil"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Endre startsiden"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Modullisten er lukket"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Legg til på startskjermen"</string>
<string name="action_move_here" msgid="2170188780612570250">"Flytt elementet hit"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Elementet er lagt til på startskjermen"</string>
<string name="item_removed" msgid="851119963877842327">"Elementet er fjernet"</string>
<string name="undo" msgid="4151576204245173321">"Angre"</string>
<string name="action_move" msgid="4339390619886385032">"Flytt elementet"</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index acdd005..78b566a 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" सँग मिल्दो कुनै एप भेटिएन"</string>
<string name="label_application" msgid="8531721983832654978">"एप"</string>
<string name="all_apps_label" msgid="5015784846527570951">"सबै एप"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"एपहरूको सूची"</string>
<string name="notifications_header" msgid="1404149926117359025">"सूचनाहरू"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"कुनै सर्टकट सार्न डबल ट्याप गरेर छोइराख्नुहोस्।"</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"कुनै सर्टकट सार्न वा आफ्नो रोजाइका कारबाही प्रयोग गर्न डबल ट्याप गरेर छोइराख्नुहोस्।"</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"फोल्डर <xliff:g id="NAME">%1$s</xliff:g> मा पुनःनामाकरण गरियो।"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"फोल्डर: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> वस्तुहरू"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"फोल्डर: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> वा सोभन्दा बढी वस्तुहरू"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"नामरहित फोल्डर"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"एप पेयर: <xliff:g id="APP1">%1$s</xliff:g> र <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"वालपेपर तथा शैली"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"होम स्क्रिन बदल्नुहोस्"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"विजेटहरूको सूची बन्द गरियो"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"होम स्क्रिनमा राख्नुहोस्"</string>
<string name="action_move_here" msgid="2170188780612570250">"वस्तु यहाँ सार्नुहोस्"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"वस्तु गृह स्क्रिनमा थपियो"</string>
<string name="item_removed" msgid="851119963877842327">"वस्तु हटाइयो"</string>
<string name="undo" msgid="4151576204245173321">"अन्डू गर्नुहोस्"</string>
<string name="action_move" msgid="4339390619886385032">"वस्तु सार्नुहोस्"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index dee17f0..d771c6f 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Er zijn geen apps gevonden die overeenkomen met \'<xliff:g id="QUERY">%1$s</xliff:g>\'"</string>
<string name="label_application" msgid="8531721983832654978">"App"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Alle apps"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Lijst met apps"</string>
<string name="notifications_header" msgid="1404149926117359025">"Meldingen"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Tik en houd vast om een snelkoppeling te verplaatsen."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Dubbeltik en houd vast om een snelkoppeling te verplaatsen of aangepaste acties te gebruiken."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"De naam van de map is gewijzigd in <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Map: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> items"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Map: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> of meer items"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Naamloze map"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"App-paar: <xliff:g id="APP1">%1$s</xliff:g> en <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Achtergrond en stijl"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Startscherm bewerken"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Lijst met widgets gesloten"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Toevoegen aan startscherm"</string>
<string name="action_move_here" msgid="2170188780612570250">"Item hier naartoe verplaatsen"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Item toegevoegd aan startscherm"</string>
<string name="item_removed" msgid="851119963877842327">"Item verwijderd"</string>
<string name="undo" msgid="4151576204245173321">"Ongedaan maken"</string>
<string name="action_move" msgid="4339390619886385032">"Item verplaatsen"</string>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index 2652454..3031259 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" ସହିତ ମେଳ ହେଉଥିବା କୌଣସି ଆପ୍ ମିଳିଲା ନାହିଁ"</string>
<string name="label_application" msgid="8531721983832654978">"ଆପ୍"</string>
<string name="all_apps_label" msgid="5015784846527570951">"ସବୁ ଆପ"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"ଆପ୍ସ ତାଲିକା"</string>
<string name="notifications_header" msgid="1404149926117359025">"ବିଜ୍ଞପ୍ତି"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"ଏକ ସର୍ଟକଟକୁ ମୁଭ୍ କରିବା ପାଇଁ ସ୍ପର୍ଶ କରି ଧରି ରଖନ୍ତୁ।"</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"ଏକ ସର୍ଟକଟକୁ ମୁଭ୍ କରିବା ପାଇଁ ଦୁଇଥର-ଟାପ୍ କରି ଧରି ରଖନ୍ତୁ କିମ୍ବା କଷ୍ଟମ୍ କାର୍ଯ୍ୟଗୁଡ଼ିକୁ ବ୍ୟବହାର କରନ୍ତୁ।"</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"ଫୋଲ୍ଡରର ନାମ <xliff:g id="NAME">%1$s</xliff:g>କୁ ବଦଳାଗଲା"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"ଫୋଲ୍ଡର୍: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ଆଇଟମଗୁଡ଼ିକ"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"ଫୋଲ୍ଡର୍: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> କିମ୍ବା ଅଧିକ ଆଇଟମ୍"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"ବେନାମୀ ଫୋଲ୍ଡର"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"ଆପ ପେୟାର: <xliff:g id="APP1">%1$s</xliff:g> ଏବଂ <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"ୱାଲପେପର ଏବଂ ଷ୍ଟାଇଲ"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"ହୋମ ସ୍କ୍ରିନକୁ ଏଡିଟ କରନ୍ତୁ"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"ୱିଜେଟ୍ ତାଲିକା ବନ୍ଦ ହୋଇଛି"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"ହୋମ ସ୍କ୍ରିନରେ ଯୋଗ କରନ୍ତୁ"</string>
<string name="action_move_here" msgid="2170188780612570250">"ଆଇଟମ୍କୁ ଏଠାକୁ ଘୁଞ୍ଚାନ୍ତୁ"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"ହୋମ ସ୍କ୍ରିନରେ ଆଇଟମ ଯୋଗ କରାଗଲା"</string>
<string name="item_removed" msgid="851119963877842327">"ଆଇଟମକୁ କାଢ଼ି ଦିଆଯାଇଛି"</string>
<string name="undo" msgid="4151576204245173321">"ପୂର୍ବବତ କରନ୍ତୁ"</string>
<string name="action_move" msgid="4339390619886385032">"ଆଇଟମ୍ ଘୁଞ୍ଚାନ୍ତୁ"</string>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index f2a5e27..ac5f3be 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" ਨਾਲ ਮੇਲ ਖਾਂਦੀਆਂ ਕੋਈ ਐਪਾਂ ਨਹੀਂ ਮਿਲੀਆਂ"</string>
<string name="label_application" msgid="8531721983832654978">"ਐਪ"</string>
<string name="all_apps_label" msgid="5015784846527570951">"ਸਾਰੀਆਂ ਐਪਾਂ"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"ਐਪਾਂ ਦੀ ਸੂਚੀ"</string>
<string name="notifications_header" msgid="1404149926117359025">"ਸੂਚਨਾਵਾਂ"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"ਕਿਸੇ ਸ਼ਾਰਟਕੱਟ ਨੂੰ ਲਿਜਾਉਣ ਲਈ ਸਪੱਰਸ਼ ਕਰਕੇ ਦਬਾਈ ਰੱਖੋ।"</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"ਕਿਸੇ ਸ਼ਾਰਟਕੱਟ ਨੂੰ ਲਿਜਾਉਣ ਲਈ ਡਬਲ ਟੈਪ ਕਰਕੇ ਦਬਾਈ ਰੱਖੋ ਜਾਂ ਵਿਉਂਤੀਆਂ ਕਾਰਵਾਈਆਂ ਵਰਤੋ।"</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"ਫੋਲਡਰ ਨੂੰ <xliff:g id="NAME">%1$s</xliff:g> ਮੁੜ ਨਾਮ ਦਿੱਤਾ ਗਿਆ"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"ਫੋਲਡਰ: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ਆਈਟਮਾਂ"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"ਫੋਲਡਰ: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ਜਾਂ ਹੋਰ ਆਈਟਮਾਂ"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"ਬੇਨਾਮ ਫੋਲਡਰ"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"ਐਪ ਜੋੜਾਬੱਧ: <xliff:g id="APP1">%1$s</xliff:g> ਅਤੇ <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"ਵਾਲਪੇਪਰ ਅਤੇ ਸਟਾਈਲ"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"ਹੋਮ ਸਕ੍ਰੀਨ ਦਾ ਸੰਪਾਦਨ ਕਰੋ"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"ਵਿਜੇਟਾਂ ਦੀ ਸੂਚੀ ਬੰਦ ਕੀਤੀ ਗਈ"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="action_move_here" msgid="2170188780612570250">"ਆਈਟਮ ਨੂੰ ਇੱਥੇ ਮੂਵ ਕਰੋ"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"ਆਈਟਮ ਨੂੰ ਹੋਮ ਸਕ੍ਰੀਨ ਵਿੱਚ ਜੋੜਿਆ ਗਿਆ"</string>
<string name="item_removed" msgid="851119963877842327">"ਆਈਟਮ ਹਟਾਈ ਗਈ"</string>
<string name="undo" msgid="4151576204245173321">"ਅਣਕੀਤਾ ਕਰੋ"</string>
<string name="action_move" msgid="4339390619886385032">"ਆਈਟਮ ਨੂੰ ਮੂਵ ਕਰੋ"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 7e1e844..754bd76 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Nie znaleziono aplikacji pasujących do zapytania „<xliff:g id="QUERY">%1$s</xliff:g>”"</string>
<string name="label_application" msgid="8531721983832654978">"Aplikacja"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Wszystkie aplikacje"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Lista aplikacji"</string>
<string name="notifications_header" msgid="1404149926117359025">"Powiadomienia"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Naciśnij i przytrzymaj, aby przenieść skrót."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Naciśnij dwukrotnie i przytrzymaj, aby przenieść skrót lub użyć działań niestandardowych."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Nazwa folderu zmieniona na <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elementy"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, liczba elementów: <xliff:g id="SIZE">%2$d</xliff:g> lub więcej"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Folder bez nazwy"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Para aplikacji: <xliff:g id="APP1">%1$s</xliff:g> oraz <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Tapeta i styl"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Edytuj ekran główny"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Lista widgetów zamknięta"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Dodaj do ekranu głównego"</string>
<string name="action_move_here" msgid="2170188780612570250">"Przenieś element tutaj"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Element został dodany do ekranu głównego"</string>
<string name="item_removed" msgid="851119963877842327">"Element został usunięty"</string>
<string name="undo" msgid="4151576204245173321">"Cofnij"</string>
<string name="action_move" msgid="4339390619886385032">"Przenieś element"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 368518f..701bb25 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -125,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Nome de pasta alterado para <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Pasta: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> itens"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Pasta: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ou mais itens"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Pasta sem nome"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Par de apps: <xliff:g id="APP1">%1$s</xliff:g> e <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Imagem fundo/estilo"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Editar ecrã principal"</string>
@@ -162,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Lista de widgets fechada."</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Adicionar ao ecrã principal"</string>
<string name="action_move_here" msgid="2170188780612570250">"Mover o item para aqui"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Item adicionado ao ecrã principal"</string>
<string name="item_removed" msgid="851119963877842327">"Item removido"</string>
<string name="undo" msgid="4151576204245173321">"Anular"</string>
<string name="action_move" msgid="4339390619886385032">"Mover item"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 0a6c3f6..dc792f1 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Nenhum app encontrado que corresponda a \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="label_application" msgid="8531721983832654978">"App"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Todos os apps"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Lista de apps"</string>
<string name="notifications_header" msgid="1404149926117359025">"Notificações"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Toque e mantenha a tela pressionada para mover um atalho."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Toque duas vezes e mantenha a tela pressionada para mover um atalho ou usar ações personalizadas."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Pasta renomeada para <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Pasta: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> itens"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Pasta: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ou mais itens"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Pasta sem nome"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Par de apps: <xliff:g id="APP1">%1$s</xliff:g> e <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Plano de fundo e estilo"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Editar tela inicial"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Lista de widgets fechada"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Adicionar à tela inicial"</string>
<string name="action_move_here" msgid="2170188780612570250">"Mover item para cá"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Item adicionado à tela inicial"</string>
<string name="item_removed" msgid="851119963877842327">"Item removido"</string>
<string name="undo" msgid="4151576204245173321">"Desfazer"</string>
<string name="action_move" msgid="4339390619886385032">"Mover item"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index cf78f2c..03e26e1 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Nu s-a găsit nicio aplicație pentru „<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="label_application" msgid="8531721983832654978">"Aplicație"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Toate aplicațiile"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Listă de aplicații"</string>
<string name="notifications_header" msgid="1404149926117359025">"Notificări"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Atinge și ține apăsat ca să muți comanda rapidă."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Atinge de două ori și ține apăsat pentru a muta o comandă rapidă sau folosește acțiuni personalizate."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Dosar redenumit <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Dosar: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elemente"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Dosar: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> sau mai multe elemente"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Dosar fără nume"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Pereche de aplicații: <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">"Imagine de fundal și stil"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Editează ecranul de pornire"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Lista de widgeturi este închisă"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Adaugă pe ecranul de pornire"</string>
<string name="action_move_here" msgid="2170188780612570250">"Mută elementul aici"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Element adăugat pe ecranul de pornire"</string>
<string name="item_removed" msgid="851119963877842327">"Element eliminat"</string>
<string name="undo" msgid="4151576204245173321">"Anulează"</string>
<string name="action_move" msgid="4339390619886385032">"Mută elementul"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 2fdc42f..b2dd767 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"По запросу \"<xliff:g id="QUERY">%1$s</xliff:g>\" ничего не найдено"</string>
<string name="label_application" msgid="8531721983832654978">"Приложение"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Все приложения"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Список приложений"</string>
<string name="notifications_header" msgid="1404149926117359025">"Уведомления"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Нажмите и удерживайте для переноса ярлыка."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Чтобы использовать специальные действия или перенести ярлык, нажмите на него дважды и удерживайте."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Папка переименована в \"<xliff:g id="NAME">%1$s</xliff:g>\""</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Папка \"<xliff:g id="NAME">%1$s</xliff:g>\" (объектов: <xliff:g id="SIZE">%2$d</xliff:g>)"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Папка \"<xliff:g id="NAME">%1$s</xliff:g>\" (объектов: <xliff:g id="SIZE">%2$d</xliff:g> или больше)"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Папка без названия"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Одновременное использование двух приложений: <xliff:g id="APP1">%1$s</xliff:g> и <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Обои и стиль"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Изменить главный экран"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Список виджетов закрыт"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Добавить на главный экран"</string>
<string name="action_move_here" msgid="2170188780612570250">"Переместить элемент сюда"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Элемент добавлен на главный экран"</string>
<string name="item_removed" msgid="851119963877842327">"Объект убран."</string>
<string name="undo" msgid="4151576204245173321">"Отменить"</string>
<string name="action_move" msgid="4339390619886385032">"Переместить элемент"</string>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index 0f6f45a..fe27579 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" සමග ගැළපෙන යෙදුම් හමු නොවිණි"</string>
<string name="label_application" msgid="8531721983832654978">"යෙදුම"</string>
<string name="all_apps_label" msgid="5015784846527570951">"සියලු යෙදුම්"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"යෙදුම් ලැයිස්තුව"</string>
<string name="notifications_header" msgid="1404149926117359025">"දැනුම්දීම්"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"කෙටි මගක් ගෙන යාමට ස්පර්ශ කර අල්ලාගෙන සිටින්න."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"කෙටි මගක් ගෙන යාමට හෝ අභිරුචි ක්රියා භාවිත කිරීමට දෙවරක් තට්ටු කර අල්ලා ගෙන සිටින්න."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"<xliff:g id="NAME">%1$s</xliff:g> වෙත ෆෝල්ඩරය නැවත නම් කෙරිණි"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"ෆෝල්ඩරය: <xliff:g id="NAME">%1$s</xliff:g>, අයිතම <xliff:g id="SIZE">%2$d</xliff:g>"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"ෆෝල්ඩර: <xliff:g id="NAME">%1$s</xliff:g>, අයිතම <xliff:g id="SIZE">%2$d</xliff:g>ක් හෝ වැඩි ගණනක්"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"නම් නොකළ ෆෝල්ඩරය"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"යෙදුම් යුගල: <xliff:g id="APP1">%1$s</xliff:g> සහ <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"වෝල්පේපරය සහ මෝස්තරය"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"මුල් තිරය සංස්කරණය කරන්න"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"විජට් ලැයිස්තුව වසා ඇත"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"මුල් තිරය වෙත එක් කරන්න"</string>
<string name="action_move_here" msgid="2170188780612570250">"මෙතනට අයිතමය ගෙන එන්න"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"අයිතමය මුල් තිරය වෙත එකතු කරන ලදි"</string>
<string name="item_removed" msgid="851119963877842327">"අයිතමය ඉවත් කරන ලදි"</string>
<string name="undo" msgid="4151576204245173321">"අස් කරන්න"</string>
<string name="action_move" msgid="4339390619886385032">"අයිතමය ගෙනයන්න"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index ad15a17..7410051 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Nenašli sa žiadne aplikácie zodpovedajúce dopytu <xliff:g id="QUERY">%1$s</xliff:g>"</string>
<string name="label_application" msgid="8531721983832654978">"Aplikácia"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Všetky aplikácie"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Zoznam aplikácií"</string>
<string name="notifications_header" msgid="1404149926117359025">"Upozornenia"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Pridržaním presuňte skratku."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Dvojitým klepnutím a pridržaním presuňte odkaz alebo použite vlastné akcie."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Priečinok bol premenovaný na <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Priečinok: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> položky"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Priečinok: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> alebo viac položiek"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Nepomenovaný priečinok"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Pár aplikácií: <xliff:g id="APP1">%1$s</xliff:g> a <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Tapeta a štýl"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Upraviť plochu"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Zoznam miniaplikácií je zavretý"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Pridať na plochu"</string>
<string name="action_move_here" msgid="2170188780612570250">"Presunúť položku sem"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Položka bola pridaná na plochu"</string>
<string name="item_removed" msgid="851119963877842327">"Položka bola odstránená"</string>
<string name="undo" msgid="4151576204245173321">"Späť"</string>
<string name="action_move" msgid="4339390619886385032">"Presunúť položku"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 25592d0..7016df5 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Ni aplikacij, ki bi ustrezale poizvedbi »<xliff:g id="QUERY">%1$s</xliff:g>«"</string>
<string name="label_application" msgid="8531721983832654978">"Aplikacija"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Vse aplikacije"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Seznam aplikacij"</string>
<string name="notifications_header" msgid="1404149926117359025">"Obvestila"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Pridržite bližnjico, da jo premaknete."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Dvakrat se dotaknite bližnjice in jo pridržite, da jo premaknete, ali pa uporabite dejanja po meri."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Mapa je preimenovana v <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Mapa: <xliff:g id="NAME">%1$s</xliff:g>, št. elementov: <xliff:g id="SIZE">%2$d</xliff:g>"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Mapa: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ali več elementov"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Neimenovana mapa"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Par aplikacij: <xliff:g id="APP1">%1$s</xliff:g> in <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Zaslonsko ozadje in slog"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Urejanje začetnega zaslona"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Seznam pripomočkov se je zaprl"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Dodajanje na začetni zaslon"</string>
<string name="action_move_here" msgid="2170188780612570250">"Premik elementa sem"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Element je bil dodan na začetni zaslon"</string>
<string name="item_removed" msgid="851119963877842327">"Element je bil odstranjen."</string>
<string name="undo" msgid="4151576204245173321">"Razveljavi"</string>
<string name="action_move" msgid="4339390619886385032">"Premik elementa"</string>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index 7db0080..eb6660c 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Nuk u gjet asnjë aplikacion që përputhet me \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="label_application" msgid="8531721983832654978">"Aplikacioni"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Të gjitha aplikacionet"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Lista e aplikacioneve"</string>
<string name="notifications_header" msgid="1404149926117359025">"Njoftimet"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Prek dhe mbaj shtypur një shkurtore për ta zhvendosur."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Trokit dy herë dhe mbaje shtypur një shkurtore për ta zhvendosur atë ose për të përdorur veprimet e personalizuara."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Dosja u riemërtua në <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Dosja: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> artikuj"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Dosja: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ose më shumë artikuj"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Dosje pa emër"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Çifti i aplikacioneve: <xliff:g id="APP1">%1$s</xliff:g> dhe <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Imazhi i sfondit dhe stili"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Modifiko ekranin bazë"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Lista e miniaplikacioneve u mbyll"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Shto në ekranin bazë"</string>
<string name="action_move_here" msgid="2170188780612570250">"Zhvendose artikullin këtu"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Artikulli u shtua tek ekrani bazë"</string>
<string name="item_removed" msgid="851119963877842327">"Artikulli u hoq"</string>
<string name="undo" msgid="4151576204245173321">"Zhbëj"</string>
<string name="action_move" msgid="4339390619886385032">"Zhvendose artikullin"</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 72caf91..e97e39b 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Није пронађена ниједна апликација за „<xliff:g id="QUERY">%1$s</xliff:g>“"</string>
<string name="label_application" msgid="8531721983832654978">"Апликација"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Све апликације"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Листа апликација"</string>
<string name="notifications_header" msgid="1404149926117359025">"Обавештења"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Додирните и задржите ради померања пречице."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Двапут додирните и задржите да бисте померали пречицу или користите прилагођене радње."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Фолдер је преименован у <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Фолдер: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ставке"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Фолдер: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> или више ставки"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Неименовани фолдер"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Пар апликација: <xliff:g id="APP1">%1$s</xliff:g> и <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Позадина и стил"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Измени почетни екран"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Листа виџета је затворена"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Додајте на почетни екран"</string>
<string name="action_move_here" msgid="2170188780612570250">"Премести ставку овде"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Ставка је додата на почетни екран"</string>
<string name="item_removed" msgid="851119963877842327">"Ставка је уклоњена"</string>
<string name="undo" msgid="4151576204245173321">"Опозови"</string>
<string name="action_move" msgid="4339390619886385032">"Премести ставку"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index ea954b7..ad5e281 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Inga appar som matchar <xliff:g id="QUERY">%1$s</xliff:g> hittades"</string>
<string name="label_application" msgid="8531721983832654978">"App"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Alla appar"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Applista"</string>
<string name="notifications_header" msgid="1404149926117359025">"Aviseringar"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Tryck länge för att flytta en genväg."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Tryck snabbt två gånger och håll kvar för att flytta en genväg eller använda anpassade åtgärder."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Mappen har bytt namn till <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Mapp: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> objekt"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Mapp: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> eller fler objekt"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Namnlös mapp"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Appar som ska användas tillsammans: <xliff:g id="APP1">%1$s</xliff:g> och <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Bakgrund och utseende"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Redigera startskärm"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Widgetslistan har stängts"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Lägg till på startskärmen"</string>
<string name="action_move_here" msgid="2170188780612570250">"Flytta objekt hit"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Objektet har lagts till på startskärmen"</string>
<string name="item_removed" msgid="851119963877842327">"Objektet har tagits bort"</string>
<string name="undo" msgid="4151576204245173321">"Ångra"</string>
<string name="action_move" msgid="4339390619886385032">"Flytta objekt"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 2a8f423..bd0029c 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Haikupata programu zozote zinazolingana na \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="label_application" msgid="8531721983832654978">"Programu"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Programu zote"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Orodha ya programu"</string>
<string name="notifications_header" msgid="1404149926117359025">"Arifa"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Gusa na ushikilie ili usogeze njia ya mkato."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Gusa mara mbili na ushikilie ili usogeze njia ya mkato au utumie vitendo maalum."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Folda imebadilishwa jina kuwa <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Folda: <xliff:g id="NAME">%1$s</xliff:g>, vipengee <xliff:g id="SIZE">%2$d</xliff:g>"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Folda: <xliff:g id="NAME">%1$s</xliff:g>, vipengee <xliff:g id="SIZE">%2$d</xliff:g> au zaidi"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Folda isiyo na jina"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Jozi ya programu: <xliff:g id="APP1">%1$s</xliff:g> na <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Mandhari na mtindo"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Badilisha Skrini ya Kwanza"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Orodha ya wijeti imefungwa"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Weka kwenye skrini ya kwanza"</string>
<string name="action_move_here" msgid="2170188780612570250">"Hamishia kipengee hapa"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Kipengee kimeongezwa kwenye skrini ya kwanza"</string>
<string name="item_removed" msgid="851119963877842327">"Kipengee kimeondolewa"</string>
<string name="undo" msgid="4151576204245173321">"Tendua"</string>
<string name="action_move" msgid="4339390619886385032">"Hamisha kipengee"</string>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index aea175a..30dbbb3 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" உடன் பொருந்தும் ஆப்ஸ் இல்லை"</string>
<string name="label_application" msgid="8531721983832654978">"ஆப்ஸ்"</string>
<string name="all_apps_label" msgid="5015784846527570951">"அனைத்து ஆப்ஸும்"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"ஆப்ஸ் பட்டியல்"</string>
<string name="notifications_header" msgid="1404149926117359025">"அறிவிப்புகள்"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"ஷார்ட்கட்டை நகர்த்தத் தொட்டுப் பிடிக்கவும்."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"ஷார்ட்கட்டை நகர்த்த இருமுறை தட்டிப் பிடிக்கவும் அல்லது பிரத்தியேகச் செயல்களைப் பயன்படுத்தவும்."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"ஃபோல்டர் <xliff:g id="NAME">%1$s</xliff:g> என மறுபெயரிடப்பட்டது"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"ஃபோல்டர்: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ஃபைல்கள்"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"ஃபோல்டர்: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> அல்லது அதற்கு அதிகமான ஃபைல்கள்"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"பெயரிடப்படாத ஃபோல்டர்"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"ஆப்ஸ் ஜோடி: <xliff:g id="APP1">%1$s</xliff:g> மற்றும் <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"வால்பேப்பர் & ஸ்டைல்"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"முகப்புத் திரையில் மாற்று"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"விட்ஜெட்கள் பட்டியல் மூடப்பட்டது"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"முகப்புத் திரையில் சேர்"</string>
<string name="action_move_here" msgid="2170188780612570250">"இங்கு நகர்த்து"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"முகப்புத் திரையில் சேர்க்கப்பட்டது"</string>
<string name="item_removed" msgid="851119963877842327">"அகற்றப்பட்டது"</string>
<string name="undo" msgid="4151576204245173321">"செயல்தவிர்"</string>
<string name="action_move" msgid="4339390619886385032">"நகர்த்து"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 777be5b..b6563b0 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\"కి మ్యాచ్ అయ్యే అప్లికేషన్లేవీ కనుగొనబడలేదు"</string>
<string name="label_application" msgid="8531721983832654978">"యాప్"</string>
<string name="all_apps_label" msgid="5015784846527570951">"అన్ని యాప్లు"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"యాప్ల లిస్ట్"</string>
<string name="notifications_header" msgid="1404149926117359025">"నోటిఫికేషన్లు"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"షార్ట్కట్ను తరలించడానికి తాకి & నొక్కి ఉంచు."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"షార్ట్కట్ను తరలించడానికి లేదా అనుకూల చర్యలను ఉపయోగించడానికి రెండుసార్లు నొక్కండి & హోల్డ్ చేయండి."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"ఫోల్డర్ పేరు <xliff:g id="NAME">%1$s</xliff:g>గా మార్చబడింది"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"ఫోల్డర్: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ఐటెమ్లు"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"ఫోల్డర్: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> లేదా అంతకంటే ఎక్కువ ఐటెమ్లు"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"పేరు లేని ఫోల్డర్"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"యాప్ పెయిర్: <xliff:g id="APP1">%1$s</xliff:g>, <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"వాల్పేపర్ & స్టయిల్"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"మొదటి స్క్రీన్ను ఎడిట్ చేయండి"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"విడ్జెట్ల లిస్ట్ మూసివేయబడింది"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"మొదటి స్క్రీన్కు జోడించండి"</string>
<string name="action_move_here" msgid="2170188780612570250">"అంశాన్ని ఇక్కడికి తరలించు"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"అంశం హోమ్స్క్రీన్కి జోడించబడింది"</string>
<string name="item_removed" msgid="851119963877842327">"ఐటెమ్ తీసివేయబడింది"</string>
<string name="undo" msgid="4151576204245173321">"చర్య రద్దు"</string>
<string name="action_move" msgid="4339390619886385032">"అంశాన్ని తరలించు"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 85e448f..f5fd93d 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"ไม่พบแอปที่ตรงกับ \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="label_application" msgid="8531721983832654978">"แอป"</string>
<string name="all_apps_label" msgid="5015784846527570951">"แอปทั้งหมด"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"รายชื่อแอป"</string>
<string name="notifications_header" msgid="1404149926117359025">"การแจ้งเตือน"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"แตะค้างไว้เพื่อย้ายทางลัด"</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"แตะสองครั้งค้างไว้เพื่อย้ายทางลัดหรือใช้การดำเนินการที่กำหนดเอง"</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"เปลี่ยนชื่อโฟลเดอร์เป็น <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"โฟลเดอร์: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> รายการ"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"โฟลเดอร์: <xliff:g id="NAME">%1$s</xliff:g>, อย่างน้อย <xliff:g id="SIZE">%2$d</xliff:g> รายการ"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"โฟลเดอร์ที่ไม่มีชื่อ"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"คู่แอป: <xliff:g id="APP1">%1$s</xliff:g> และ <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"วอลเปเปอร์และสไตล์"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"แก้ไขหน้าจอหลัก"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"ปิดรายการวิดเจ็ตแล้ว"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"เพิ่มลงในหน้าจอหลัก"</string>
<string name="action_move_here" msgid="2170188780612570250">"ย้ายรายการมาที่นี่"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"เพิ่มรายการไปยังหน้าจอหลักแล้ว"</string>
<string name="item_removed" msgid="851119963877842327">"นำรายการออกแล้ว"</string>
<string name="undo" msgid="4151576204245173321">"เลิกทำ"</string>
<string name="action_move" msgid="4339390619886385032">"ย้ายรายการ"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index afcef90..4d9a340 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Walang nahanap na app na tumutugma sa \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="label_application" msgid="8531721983832654978">"App"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Lahat ng app"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Listahan ng mga app"</string>
<string name="notifications_header" msgid="1404149926117359025">"Mga Notification"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Pindutin nang matagal para ilipat ang shortcut."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"I-double tap at pindutin nang matagal para ilipat ang shortcut o gumamit ng mga custom na pagkilos."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Pinalitan ang pangalan ng folder ng <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> (na) item"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> o higit pang item"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Walang pangalang folder"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Pares ng app: <xliff:g id="APP1">%1$s</xliff:g> at <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Wallpaper & istilo"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"I-edit ang Home Screen"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Nakasara ang listahan ng mga widget"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Idagdag sa home screen"</string>
<string name="action_move_here" msgid="2170188780612570250">"Ilipat ang item dito"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Naidagdag sa home screen ang item"</string>
<string name="item_removed" msgid="851119963877842327">"Naalis na ang item"</string>
<string name="undo" msgid="4151576204245173321">"I-undo"</string>
<string name="action_move" msgid="4339390619886385032">"Ilipat ang item"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 49c8755..cb47400 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" ile eşleşen uygulama bulunamadı"</string>
<string name="label_application" msgid="8531721983832654978">"Uygulama"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Tüm uygulamalar"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Uygulama listesi"</string>
<string name="notifications_header" msgid="1404149926117359025">"Bildirimler"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Kısayolu taşımak için dokunup basılı tutun."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Kısayolu taşımak veya özel işlemleri kullanmak için iki kez dokunup basılı tutun."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Klasörün adı <xliff:g id="NAME">%1$s</xliff:g> olarak değiştirildi"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Klasör: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> öğe"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Klasör: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> veya daha fazla öğe"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Adsız klasör"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Uygulama çifti: <xliff:g id="APP1">%1$s</xliff:g> ve <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Duvar kağıdı ve stil"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Ana ekranı düzenleyin"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Widget listesi kapalı"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Ana ekrana ekle"</string>
<string name="action_move_here" msgid="2170188780612570250">"Öğeyi buraya taşı"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Öğe ana ekrana eklendi"</string>
<string name="item_removed" msgid="851119963877842327">"Öğe silindi"</string>
<string name="undo" msgid="4151576204245173321">"Geri al"</string>
<string name="action_move" msgid="4339390619886385032">"Öğeyi taşı"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 2d0d364..fd3855d 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Немає додатків для запиту \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="label_application" msgid="8531721983832654978">"Додаток"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Усі додатки"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Список додатків"</string>
<string name="notifications_header" msgid="1404149926117359025">"Сповіщення"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Натисніть і втримуйте, щоб перемістити ярлик."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Двічі натисніть і втримуйте ярлик, щоб перемістити його або виконати інші дії."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Папку перейменовано на <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Папка \"<xliff:g id="NAME">%1$s</xliff:g>\", елементів: <xliff:g id="SIZE">%2$d</xliff:g>"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Папка \"<xliff:g id="NAME">%1$s</xliff:g>\", елементів: <xliff:g id="SIZE">%2$d</xliff:g> або більше"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Папка без назви"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Одночасне використання двох додатків: <xliff:g id="APP1">%1$s</xliff:g> і <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Оформлення й стиль"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Редагувати головний екран"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Список віджектів закрито"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Додати на головний екран"</string>
<string name="action_move_here" msgid="2170188780612570250">"Перемістити елемент сюди"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Елемент додано на головний екран"</string>
<string name="item_removed" msgid="851119963877842327">"Елемент вилучено"</string>
<string name="undo" msgid="4151576204245173321">"Відмінити"</string>
<string name="action_move" msgid="4339390619886385032">"Перемістити елемент"</string>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index 73b8ad4..ed002ba 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" سے مماثل کوئی ایپس نہیں ملیں"</string>
<string name="label_application" msgid="8531721983832654978">"ایپ"</string>
<string name="all_apps_label" msgid="5015784846527570951">"سبھی ایپس"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"ایپس کی فہرست"</string>
<string name="notifications_header" msgid="1404149926117359025">"اطلاعات"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"شارٹ کٹ منتقل کرنے کیلیے ٹچ کریں اور پکڑ کر رکھیں۔"</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"شارٹ کٹ کو منتقل کرنے یا حسب ضرورت کارروائیاں استعمال کرنے کے لیے دوبار تھپتھپائیں اور پکڑ کر رکھیں۔"</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"فولڈر کا نام تبدیل کر کے <xliff:g id="NAME">%1$s</xliff:g> کر دیا گیا"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"فولڈر: <xliff:g id="NAME">%1$s</xliff:g>، <xliff:g id="SIZE">%2$d</xliff:g> آئٹمز"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"فولڈر: <xliff:g id="NAME">%1$s</xliff:g>، <xliff:g id="SIZE">%2$d</xliff:g> یا مزید آئٹمز"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"بلا نام فولڈر"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"ایپس کا جوڑا: <xliff:g id="APP1">%1$s</xliff:g> اور <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"وال پیپر اور طرز"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"ہوم اسکرین میں ترمیم کریں"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"ویجیٹس کی فہرست بند کر دی گئی"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"ہوم اسکرین میں شامل کریں"</string>
<string name="action_move_here" msgid="2170188780612570250">"آئٹم یہاں منتقل کریں"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"آئٹم کو ہوم اسکرین میں شامل کر دیا گیا"</string>
<string name="item_removed" msgid="851119963877842327">"آئٹم ہٹا دیا گیا"</string>
<string name="undo" msgid="4151576204245173321">"کالعدم کریں"</string>
<string name="action_move" msgid="4339390619886385032">"آئٹم منتقل کریں"</string>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index 6cbec3e..f96b6a2 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"“<xliff:g id="QUERY">%1$s</xliff:g>” bilan mos hech qanday ilova topilmadi"</string>
<string name="label_application" msgid="8531721983832654978">"Ilova"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Barcha ilovalar"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Ilovalar roʻyxati"</string>
<string name="notifications_header" msgid="1404149926117359025">"Bildirishnomalar"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Yorliqni bosib turgan holatda suring."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Ikki marta bosing va yorliqni bosib turgan holatda suring yoki maxsus amaldan foydalaning."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Jild nomi <xliff:g id="NAME">%1$s</xliff:g>ga o‘zgartirildi"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Jild: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> fayllar"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Jild: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> va undan ortiq fayllar"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Nomsiz jild"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Ilovani juftlash: <xliff:g id="APP1">%1$s</xliff:g> va <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Fon rasmi va uslubi"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Bosh ekranni tahrirlash"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Vidjetlar ro‘yxati yopildi"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Bosh ekranga chiqarish"</string>
<string name="action_move_here" msgid="2170188780612570250">"Obyektni bu yerga ko‘chirish"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Obyekt bosh ekranga qo‘shildi"</string>
<string name="item_removed" msgid="851119963877842327">"Element olib tashlandi"</string>
<string name="undo" msgid="4151576204245173321">"Qaytarish"</string>
<string name="action_move" msgid="4339390619886385032">"Obyektni ko‘chirib o‘tkazish"</string>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index e3523f6..1d886b2 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Không tìm thấy ứng dụng nào phù hợp với \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="label_application" msgid="8531721983832654978">"Ứng dụng"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Tất cả ứng dụng"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Danh sách ứng dụng"</string>
<string name="notifications_header" msgid="1404149926117359025">"Thông báo"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Chạm và giữ để di chuyển một lối tắt."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Nhấn đúp và giữ để di chuyển một lối tắt hoặc sử dụng các thao tác tùy chỉnh."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Đã đổi tên thư mục thành <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Thư mục: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> mục"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Thư mục: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> mục trở lên"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Thư mục chưa đặt tên"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Cặp ứng dụng: <xliff:g id="APP1">%1$s</xliff:g> và <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Hình nền và phong cách"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Chỉnh sửa Màn hình chính"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Đã đóng danh sách tiện ích"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Thêm vào màn hình chính"</string>
<string name="action_move_here" msgid="2170188780612570250">"Di chuyển mục vào đây"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Đã thêm mục vào màn hình chính"</string>
<string name="item_removed" msgid="851119963877842327">"Đã xóa mục"</string>
<string name="undo" msgid="4151576204245173321">"Hủy"</string>
<string name="action_move" msgid="4339390619886385032">"Di chuyển mục"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 1dea916..b2d2a1b 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"未找到与“<xliff:g id="QUERY">%1$s</xliff:g>”相符的应用"</string>
<string name="label_application" msgid="8531721983832654978">"应用"</string>
<string name="all_apps_label" msgid="5015784846527570951">"所有应用"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"应用列表"</string>
<string name="notifications_header" msgid="1404149926117359025">"通知"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"轻触并按住快捷方式即可移动该快捷方式。"</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"点按两次并按住快捷方式即可移动该快捷方式或使用自定义操作。"</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"已将文件夹重命名为“<xliff:g id="NAME">%1$s</xliff:g>”"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"文件夹:<xliff:g id="NAME">%1$s</xliff:g>,<xliff:g id="SIZE">%2$d</xliff:g> 个项目"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"文件夹:<xliff:g id="NAME">%1$s</xliff:g>,<xliff:g id="SIZE">%2$d</xliff:g> 个或更多项目"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"未命名文件夹"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"应用对:“<xliff:g id="APP1">%1$s</xliff:g>”和“<xliff:g id="APP2">%2$s</xliff:g>”"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"壁纸与个性化"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"修改主屏幕"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"微件列表已关闭"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"添加到主屏幕"</string>
<string name="action_move_here" msgid="2170188780612570250">"将项目移至此处"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"已将项目添加到主屏幕"</string>
<string name="item_removed" msgid="851119963877842327">"项目已移除"</string>
<string name="undo" msgid="4151576204245173321">"撤消"</string>
<string name="action_move" msgid="4339390619886385032">"移动项目"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index ac6671d..5a573a1 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"找不到與「<xliff:g id="QUERY">%1$s</xliff:g>」相符的應用程式"</string>
<string name="label_application" msgid="8531721983832654978">"應用程式"</string>
<string name="all_apps_label" msgid="5015784846527570951">"所有應用程式"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"應用程式清單"</string>
<string name="notifications_header" msgid="1404149926117359025">"通知"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"輕觸並按住即可移動捷徑。"</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"㩒兩下之後㩒住,就可以郁捷徑或者用自訂操作。"</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"資料夾已重新命名為「<xliff:g id="NAME">%1$s</xliff:g>」"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"資料夾:<xliff:g id="NAME">%1$s</xliff:g>,<xliff:g id="SIZE">%2$d</xliff:g> 個項目"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"資料夾:<xliff:g id="NAME">%1$s</xliff:g>,<xliff:g id="SIZE">%2$d</xliff:g> 個或以上的項目"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"未命名的資料夾"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"應用程式配對:<xliff:g id="APP1">%1$s</xliff:g> 和 <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"桌布和樣式"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"編輯主畫面"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"已經關閉嘅小工具清單"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"加去主畫面"</string>
<string name="action_move_here" msgid="2170188780612570250">"移動項目至這裡"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"已將項目加入至主畫面"</string>
<string name="item_removed" msgid="851119963877842327">"項目已移除"</string>
<string name="undo" msgid="4151576204245173321">"復原"</string>
<string name="action_move" msgid="4339390619886385032">"移動項目"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 384ffef..6cf783c 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"找不到與「<xliff:g id="QUERY">%1$s</xliff:g>」相符的應用程式"</string>
<string name="label_application" msgid="8531721983832654978">"應用程式"</string>
<string name="all_apps_label" msgid="5015784846527570951">"所有應用程式"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"應用程式清單"</string>
<string name="notifications_header" msgid="1404149926117359025">"通知"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"按住即可移動捷徑。"</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"輕觸兩下並按住即可移動捷徑或使用自訂操作。"</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"已將資料夾重新命名為「<xliff:g id="NAME">%1$s</xliff:g>」"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"資料夾:<xliff:g id="NAME">%1$s</xliff:g>,<xliff:g id="SIZE">%2$d</xliff:g> 個項目"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"資料夾:<xliff:g id="NAME">%1$s</xliff:g>,<xliff:g id="SIZE">%2$d</xliff:g> 個以上的項目"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"未命名的資料夾"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"應用程式配對:「<xliff:g id="APP1">%1$s</xliff:g>」與「<xliff:g id="APP2">%2$s</xliff:g>」"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"桌布和樣式"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"編輯主畫面"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"已關閉小工具清單"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"新增至主畫面"</string>
<string name="action_move_here" msgid="2170188780612570250">"將項目移至這裡"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"已將項目新增到主畫面"</string>
<string name="item_removed" msgid="851119963877842327">"已移除項目"</string>
<string name="undo" msgid="4151576204245173321">"復原"</string>
<string name="action_move" msgid="4339390619886385032">"移動項目"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 3c68231..58d1188 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -82,8 +82,7 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Azikho izinhlelo zokusebenza ezitholiwe ezifana ne-\"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="label_application" msgid="8531721983832654978">"Uhlelo lokusebenza"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Wonke ama-app"</string>
- <!-- no translation found for all_apps_list_label (5106226764073070906) -->
- <skip />
+ <string name="all_apps_list_label" msgid="5106226764073070906">"Uhlu lwama-app"</string>
<string name="notifications_header" msgid="1404149926117359025">"Izaziso"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Thinta uphinde ubambe ukuze uhambise isinqamuleli."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Thepha kabili uphinde ubambe ukuze uhambise isinqamuleli noma usebenzise izenzo ezingokwezifiso."</string>
@@ -126,6 +125,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Ifolda iqanjwe kabusha ngo-<xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Ifolda: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> izinto"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Ifolda: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> noma izinto eziningi"</string>
+ <string name="unnamed_folder" msgid="2420192029474044442">"Ifolda engenagama"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"Ama-app abhangqwayo: I-<xliff:g id="APP1">%1$s</xliff:g> ne-<xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Isithombe sangemuva nesitayela"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Hlela Isikrini Sasekhaya"</string>
@@ -163,7 +163,6 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"Uhlu lwamawijethi luvaliwe"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"Faka kusikrini sasekhaya"</string>
<string name="action_move_here" msgid="2170188780612570250">"Hambisa into lapha"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Into ingezwe kusikrini sasekhaya"</string>
<string name="item_removed" msgid="851119963877842327">"Into isusiwe"</string>
<string name="undo" msgid="4151576204245173321">"Susa"</string>
<string name="action_move" msgid="4339390619886385032">"Hambisa into"</string>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 698877a..e06895c 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -163,8 +163,8 @@
</declare-styleable>
<declare-styleable name="GridSize">
- <attr name="minDeviceWidthDp" format="float"/>
- <attr name="minDeviceHeightDp" format="float"/>
+ <attr name="minDeviceWidthPx" format="float"/>
+ <attr name="minDeviceHeightPx" format="float"/>
<attr name="numGridRows" format="integer"/>
<attr name="numGridColumns" format="integer"/>
<attr name="dbFile" />
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 21aabfa..212534b 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -488,8 +488,6 @@
<dimen name="overview_grid_side_margin">0dp</dimen>
<dimen name="overview_grid_row_spacing">0dp</dimen>
<dimen name="overview_page_spacing">0dp</dimen>
- <dimen name="overview_top_margin_grid_only">0dp</dimen>
- <dimen name="overview_bottom_margin_grid_only">0dp</dimen>
<dimen name="split_placeholder_size">72dp</dimen>
<dimen name="split_placeholder_inset">16dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 6f293b6..a02516a 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -308,6 +308,8 @@
<string name="folder_name_format_exact">Folder: <xliff:g id="name" example="Games">%1$s</xliff:g>, <xliff:g id="size" example="2">%2$d</xliff:g> items</string>
<!-- Folder name format when folder has 4 or more items shown in preview-->
<string name="folder_name_format_overflow">Folder: <xliff:g id="name" example="Games">%1$s</xliff:g>, <xliff:g id="size" example="2">%2$d</xliff:g> or more items</string>
+ <!-- Accessibility announement for unnamed folders -->
+ <string name="unnamed_folder">Unnamed folder</string>
<!-- App pair accessibility -->
<!-- App pair name -->
@@ -406,9 +408,6 @@
<!-- Accessibility action to move item to the current location. [CHAR_LIMIT=30] -->
<string name="action_move_here">Move item here</string>
- <!-- Accessibility confirmation for item added to workspace. -->
- <string name="item_added_to_workspace">Item added to home screen</string>
-
<!-- Accessibility confirmation for item removed. [CHAR_LIMIT=50]-->
<string name="item_removed">Item removed</string>
diff --git a/res/xml/folder_shapes.xml b/res/xml/folder_shapes.xml
deleted file mode 100644
index e60d333..0000000
--- a/res/xml/folder_shapes.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2019 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.
--->
-<shapes xmlns:launcher="http://schemas.android.com/apk/res-auto" >
-
- <Circle launcher:folderIconRadius="1" />
-
- <!-- Default icon for AOSP -->
- <RoundedSquare launcher:folderIconRadius="0.16" />
-
- <!-- Rounded icon from RRO -->
- <RoundedSquare launcher:folderIconRadius="0.6" />
-
- <!-- Square icon -->
- <RoundedSquare launcher:folderIconRadius="0" />
-
- <TearDrop launcher:folderIconRadius="0.3" />
- <Squircle launcher:folderIconRadius="0.2" />
-
-</shapes>
\ No newline at end of file
diff --git a/shared/src/com/android/launcher3/testing/shared/TestProtocol.java b/shared/src/com/android/launcher3/testing/shared/TestProtocol.java
index 4a7471a..5fcbbf1 100644
--- a/shared/src/com/android/launcher3/testing/shared/TestProtocol.java
+++ b/shared/src/com/android/launcher3/testing/shared/TestProtocol.java
@@ -170,6 +170,7 @@
public static final String ICON_MISSING = "b/282963545";
public static final String REQUEST_FLAG_ENABLE_GRID_ONLY_OVERVIEW = "enable-grid-only-overview";
public static final String REQUEST_FLAG_ENABLE_APP_PAIRS = "enable-app-pairs";
+ public static final String REQUEST_IS_RECENTS_WINDOW_ENABLED = "recents-window-enabled";
public static final String REQUEST_UNSTASH_BUBBLE_BAR_IF_STASHED =
"unstash-bubble-bar-if-stashed";
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index c78666e..9aa06bf 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -161,7 +161,7 @@
};
private final MultiTranslateDelegate mTranslateDelegate = new MultiTranslateDelegate(this);
- private final ActivityContext mActivity;
+ protected final ActivityContext mActivity;
private FastBitmapDrawable mIcon;
private DeviceProfile mDeviceProfile;
private boolean mCenterVertically;
@@ -190,7 +190,6 @@
@ViewDebug.ExportedProperty(category = "launcher")
private DotInfo mDotInfo;
private DotRenderer mDotRenderer;
- private String mCurrentLanguage;
@ViewDebug.ExportedProperty(category = "launcher", deepExport = true)
protected DotRenderer.DrawParams mDotParams;
private Animator mDotScaleAnim;
@@ -302,7 +301,6 @@
mDotParams = new DotRenderer.DrawParams();
- mCurrentLanguage = context.getResources().getConfiguration().locale.getLanguage();
setEllipsize(TruncateAt.END);
setAccessibilityDelegate(mActivity.getAccessibilityDelegate());
setTextAlpha(1f);
@@ -369,27 +367,9 @@
@UiThread
public void applyFromWorkspaceItem(WorkspaceItemInfo info) {
- applyFromWorkspaceItem(info, /* animate = */ false, /* staggerIndex = */ 0);
- }
-
- @UiThread
- public void applyFromWorkspaceItem(WorkspaceItemInfo info, boolean animate, int staggerIndex) {
applyFromWorkspaceItem(info, null);
}
- /**
- * Returns whether the newInfo differs from the current getTag().
- */
- public boolean shouldAnimateIconChange(WorkspaceItemInfo newInfo) {
- WorkspaceItemInfo oldInfo = getTag() instanceof WorkspaceItemInfo
- ? (WorkspaceItemInfo) getTag()
- : null;
- boolean changedIcons = oldInfo != null && oldInfo.getTargetComponent() != null
- && newInfo.getTargetComponent() != null
- && !oldInfo.getTargetComponent().equals(newInfo.getTargetComponent());
- return changedIcons && isShown();
- }
-
@Override
public void setAccessibilityDelegate(AccessibilityDelegate delegate) {
if (delegate instanceof BaseAccessibilityDelegate) {
@@ -484,21 +464,16 @@
}
protected boolean shouldUseTheme() {
- return (mDisplay == DISPLAY_WORKSPACE || mDisplay == DISPLAY_FOLDER
- || mDisplay == DISPLAY_TASKBAR) && Themes.isThemedIconEnabled(getContext());
+ return mDisplay == DISPLAY_WORKSPACE || mDisplay == DISPLAY_FOLDER
+ || mDisplay == DISPLAY_TASKBAR;
}
/**
* Only if actual text can be displayed in two line, the {@code true} value will be effective.
*/
protected boolean shouldUseTwoLine() {
- return isCurrentLanguageEnglish() && (mDisplay == DISPLAY_ALL_APPS
- || mDisplay == DISPLAY_PREDICTION_ROW) && (Flags.enableTwolineToggle()
- && LauncherPrefs.ENABLE_TWOLINE_ALLAPPS_TOGGLE.get(getContext()));
- }
-
- protected boolean isCurrentLanguageEnglish() {
- return mCurrentLanguage.equals(Locale.ENGLISH.getLanguage());
+ return mDeviceProfile.inv.enableTwoLinesInAllApps
+ && (mDisplay == DISPLAY_ALL_APPS || mDisplay == DISPLAY_PREDICTION_ROW);
}
@UiThread
@@ -1333,7 +1308,6 @@
applyFromApplicationInfo((AppInfo) info);
} else if (info instanceof WorkspaceItemInfo) {
applyFromWorkspaceItem((WorkspaceItemInfo) info);
- mActivity.invalidateParent(info);
} else if (info != null) {
applyFromItemInfoWithIcon(info);
}
@@ -1347,12 +1321,11 @@
* Verifies that the current icon is high-res otherwise posts a request to load the icon.
*/
public void verifyHighRes() {
- if (mIconLoadRequest != null) {
- mIconLoadRequest.cancel();
- mIconLoadRequest = null;
- }
if (getTag() instanceof ItemInfoWithIcon info && !mHighResUpdateInProgress
&& info.getMatchingLookupFlag().useLowRes()) {
+ if (mIconLoadRequest != null) {
+ mIconLoadRequest.cancel();
+ }
mIconLoadRequest = LauncherAppState.getInstance(getContext()).getIconCache()
.updateIconInBackground(BubbleTextView.this, info);
}
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 5387815..9ebae9f 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -24,7 +24,6 @@
import static com.android.launcher3.InvariantDeviceProfile.INDEX_TWO_PANEL_LANDSCAPE;
import static com.android.launcher3.InvariantDeviceProfile.INDEX_TWO_PANEL_PORTRAIT;
import static com.android.launcher3.Utilities.dpiFromPx;
-import static com.android.launcher3.Utilities.isEnglishLanguage;
import static com.android.launcher3.Utilities.pxFromSp;
import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.ICON_OVERLAP_FACTOR;
import static com.android.launcher3.icons.GraphicsUtils.getShapePath;
@@ -1362,16 +1361,9 @@
if (isVerticalLayout && !mIsResponsiveGrid) {
hideWorkspaceLabelsIfNotEnoughSpace();
}
- if ((Flags.enableTwolineToggle()
- && LauncherPrefs.ENABLE_TWOLINE_ALLAPPS_TOGGLE.get(context))) {
- if (!isEnglishLanguage(context)) {
- // Set toggle preference value to false if not english here as it's possible the
- // preference is stale after language change.
- LauncherPrefs.get(context).put(LauncherPrefs.ENABLE_TWOLINE_ALLAPPS_TOGGLE, false);
- } else {
- // Add extra textHeight to the existing allAppsCellHeight.
- allAppsCellHeightPx += Utilities.calculateTextHeight(allAppsIconTextSizePx);
- }
+ if (inv.enableTwoLinesInAllApps) {
+ // Add extra textHeight to the existing allAppsCellHeight.
+ allAppsCellHeightPx += Utilities.calculateTextHeight(allAppsIconTextSizePx);
}
updateHotseatSizes(iconSizePx);
diff --git a/src/com/android/launcher3/DropTargetHandler.kt b/src/com/android/launcher3/DropTargetHandler.kt
index 4d3fe52..66c948a 100644
--- a/src/com/android/launcher3/DropTargetHandler.kt
+++ b/src/com/android/launcher3/DropTargetHandler.kt
@@ -66,6 +66,11 @@
fun onDeleteComplete(item: ItemInfo) {
removeItemAndStripEmptyScreens(null /* view */, item)
+ AbstractFloatingView.closeOpenViews(
+ mLauncher,
+ false,
+ AbstractFloatingView.TYPE_WIDGET_RESIZE_FRAME,
+ )
var pageItem: ItemInfo = item
if (item.container <= 0) {
val v = mLauncher.workspace.getHomescreenIconByItemId(item.container)
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 28293d1..753e017 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -17,6 +17,7 @@
package com.android.launcher3;
import static com.android.launcher3.LauncherPrefs.DB_FILE;
+import static com.android.launcher3.LauncherPrefs.ENABLE_TWOLINE_ALLAPPS_TOGGLE;
import static com.android.launcher3.LauncherPrefs.FIXED_LANDSCAPE_MODE;
import static com.android.launcher3.LauncherPrefs.GRID_NAME;
import static com.android.launcher3.Utilities.dpiFromPx;
@@ -30,6 +31,7 @@
import android.annotation.TargetApi;
import android.content.Context;
+import android.content.Intent;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
@@ -57,14 +59,15 @@
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.DeviceGridState;
import com.android.launcher3.provider.RestoreDbTask;
-import com.android.launcher3.settings.SettingsActivity;
import com.android.launcher3.testing.shared.ResourceUtils;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.Info;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.Partner;
import com.android.launcher3.util.ResourceHelper;
+import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.SafeCloseable;
+import com.android.launcher3.util.SimpleBroadcastReceiver;
import com.android.launcher3.util.WindowBounds;
import com.android.launcher3.util.window.CachedDisplayInfo;
import com.android.launcher3.util.window.WindowManagerProxy;
@@ -123,6 +126,8 @@
private static final String RES_GRID_NUM_COLUMNS = "grid_num_columns";
private static final String RES_GRID_ICON_SIZE_DP = "grid_icon_size_dp";
+ private final RunnableList mCloseActions = new RunnableList();
+
/**
* Number of icons per row and column in the workspace.
*/
@@ -218,12 +223,12 @@
@XmlRes
public int allAppsCellSpecsTwoPanelId = INVALID_RESOURCE_HANDLE;
-
+ private String mLocale = "";
+ public boolean enableTwoLinesInAllApps = false;
/**
* Fixed landscape mode is the landscape on the phones.
*/
public boolean isFixedLandscape = false;
- private LauncherPrefChangeListener mLandscapeModePreferenceListener;
public String dbFile;
public int defaultLayoutId;
@@ -247,7 +252,9 @@
private InvariantDeviceProfile(Context context) {
String gridName = getCurrentGridName(context);
initGrid(context, gridName);
- DisplayController.INSTANCE.get(context).setPriorityListener(
+
+ DisplayController dc = DisplayController.INSTANCE.get(context);
+ dc.setPriorityListener(
(displayContext, info, flags) -> {
if ((flags & (CHANGE_DENSITY | CHANGE_SUPPORTED_BOUNDS
| CHANGE_NAVIGATION_MODE | CHANGE_TASKBAR_PINNING
@@ -255,25 +262,28 @@
onConfigChanged(displayContext);
}
});
- if (Flags.oneGridSpecs()) {
- mLandscapeModePreferenceListener = (String preference_name) -> {
- // Here we need both conditions even though they might seem redundant but because
- // the update happens in the executable there can be race conditions and this avoids
- // it.
- if (isFixedLandscape != FIXED_LANDSCAPE_MODE.get(context)
- && SettingsActivity.FIXED_LANDSCAPE_MODE.equals(preference_name)) {
- MAIN_EXECUTOR.execute(() -> {
- Trace.beginSection("InvariantDeviceProfile#setFixedLandscape");
- onConfigChanged(context.getApplicationContext());
- Trace.endSection();
- });
- }
- };
- LauncherPrefs.INSTANCE.get(context).addListener(
- mLandscapeModePreferenceListener,
- FIXED_LANDSCAPE_MODE
- );
- }
+ mCloseActions.add(() -> dc.setPriorityListener(null));
+
+ LauncherPrefChangeListener prefListener = key -> {
+ if (FIXED_LANDSCAPE_MODE.getSharedPrefKey().equals(key)
+ && isFixedLandscape != FIXED_LANDSCAPE_MODE.get(context)) {
+ Trace.beginSection("InvariantDeviceProfile#setFixedLandscape");
+ onConfigChanged(context);
+ Trace.endSection();
+ } else if (ENABLE_TWOLINE_ALLAPPS_TOGGLE.getSharedPrefKey().equals(key)
+ && enableTwoLinesInAllApps != ENABLE_TWOLINE_ALLAPPS_TOGGLE.get(context)) {
+ onConfigChanged(context);
+ }
+ };
+ LauncherPrefs prefs = LauncherPrefs.INSTANCE.get(context);
+ prefs.addListener(prefListener, FIXED_LANDSCAPE_MODE, ENABLE_TWOLINE_ALLAPPS_TOGGLE);
+ mCloseActions.add(() -> prefs.removeListener(prefListener,
+ FIXED_LANDSCAPE_MODE, ENABLE_TWOLINE_ALLAPPS_TOGGLE));
+
+ SimpleBroadcastReceiver localeReceiver = new SimpleBroadcastReceiver(
+ MAIN_EXECUTOR, i -> onConfigChanged(context));
+ localeReceiver.register(context, Intent.ACTION_LOCALE_CHANGED);
+ mCloseActions.add(() -> localeReceiver.unregisterReceiverSafely(context));
}
/**
@@ -341,12 +351,7 @@
@Override
public void close() {
- DisplayController.INSTANCE.executeIfCreated(dc -> dc.setPriorityListener(null));
- if (mLandscapeModePreferenceListener != null) {
- LauncherPrefs.INSTANCE.executeIfCreated(
- lp -> lp.removeListener(mLandscapeModePreferenceListener, FIXED_LANDSCAPE_MODE)
- );
- }
+ mCloseActions.executeAllAndDestroy();
}
public static String getCurrentGridName(Context context) {
@@ -413,6 +418,11 @@
}
private void initGrid(Context context, Info displayInfo, DisplayOption displayOption) {
+ enableTwoLinesInAllApps = Flags.enableTwolineToggle()
+ && Utilities.isEnglishLanguage(context)
+ && ENABLE_TWOLINE_ALLAPPS_TOGGLE.get(context);
+ mLocale = context.getResources().getConfiguration().locale.toString();
+
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
GridOption closestProfile = displayOption.grid;
numRows = closestProfile.numRows;
@@ -564,7 +574,7 @@
private Object[] toModelState() {
return new Object[]{
numColumns, numRows, numSearchContainerColumns, numDatabaseHotseatIcons,
- iconBitmapSize, fillResIconDpi, numDatabaseAllAppsColumns, dbFile};
+ iconBitmapSize, fillResIconDpi, numDatabaseAllAppsColumns, dbFile, mLocale};
}
/** Updates IDP using the provided context. Notifies listeners of change. */
@@ -675,7 +685,7 @@
}
// Finds the min width and height in dp for all displays.
- int[] dimens = findMinWidthAndHeightDpForDevice(displayInfo);
+ int[] dimens = findMinWidthAndHeightPxForDevice(displayInfo);
return findBestGridSize(gridSizes, dimens[0], dimens[1]);
}
@@ -684,11 +694,11 @@
* @return the biggest grid size that fits the display dimensions.
* If no best grid size is found, return null.
*/
- private static GridSize findBestGridSize(List<GridSize> list, int minWidthDp,
- int minHeightDp) {
+ private static GridSize findBestGridSize(List<GridSize> list, int minWidthPx,
+ int minHeightPx) {
GridSize selectedGridSize = null;
for (GridSize item: list) {
- if (minWidthDp >= item.mMinDeviceWidthDp && minHeightDp >= item.mMinDeviceHeightDp) {
+ if (minWidthPx >= item.mMinDeviceWidthPx && minHeightPx >= item.mMinDeviceHeightPx) {
if (selectedGridSize == null
|| (selectedGridSize.mNumColumns <= item.mNumColumns
&& selectedGridSize.mNumRows <= item.mNumRows)) {
@@ -699,16 +709,14 @@
return selectedGridSize;
}
- private static int[] findMinWidthAndHeightDpForDevice(Info displayInfo) {
- int minDisplayWidthDp = Integer.MAX_VALUE;
- int minDisplayHeightDp = Integer.MAX_VALUE;
+ private static int[] findMinWidthAndHeightPxForDevice(Info displayInfo) {
+ int minDisplayWidthPx = Integer.MAX_VALUE;
+ int minDisplayHeightPx = Integer.MAX_VALUE;
for (CachedDisplayInfo display: displayInfo.getAllDisplays()) {
- minDisplayWidthDp = Math.min(minDisplayWidthDp,
- (int) dpiFromPx(display.size.x, DisplayMetrics.DENSITY_DEVICE_STABLE));
- minDisplayHeightDp = Math.min(minDisplayHeightDp,
- (int) dpiFromPx(display.size.y, DisplayMetrics.DENSITY_DEVICE_STABLE));
+ minDisplayWidthPx = Math.min(minDisplayWidthPx, display.size.x);
+ minDisplayHeightPx = Math.min(minDisplayHeightPx, display.size.y);
}
- return new int[]{minDisplayWidthDp, minDisplayHeightDp};
+ return new int[]{minDisplayWidthPx, minDisplayHeightPx};
}
/**
@@ -1236,8 +1244,8 @@
public static final class GridSize {
final int mNumRows;
final int mNumColumns;
- final float mMinDeviceWidthDp;
- final float mMinDeviceHeightDp;
+ final float mMinDeviceWidthPx;
+ final float mMinDeviceHeightPx;
final String mDbFile;
final int mDefaultLayoutId;
final int mDemoModeLayoutId;
@@ -1248,8 +1256,8 @@
mNumRows = (int) a.getFloat(R.styleable.GridSize_numGridRows, 0);
mNumColumns = (int) a.getFloat(R.styleable.GridSize_numGridColumns, 0);
- mMinDeviceWidthDp = a.getFloat(R.styleable.GridSize_minDeviceWidthDp, 0);
- mMinDeviceHeightDp = a.getFloat(R.styleable.GridSize_minDeviceHeightDp, 0);
+ mMinDeviceWidthPx = a.getFloat(R.styleable.GridSize_minDeviceWidthPx, 0);
+ mMinDeviceHeightPx = a.getFloat(R.styleable.GridSize_minDeviceHeightPx, 0);
mDbFile = a.getString(R.styleable.GridSize_dbFile);
mDefaultLayoutId = a.getResourceId(
R.styleable.GridSize_defaultLayoutId, 0);
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index b38efc2..7df4014 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -72,7 +72,6 @@
import static com.android.launcher3.Utilities.postAsyncCallback;
import static com.android.launcher3.config.FeatureFlags.FOLDABLE_SINGLE_PAGE;
import static com.android.launcher3.config.FeatureFlags.MULTI_SELECT_EDIT_MODE;
-import static com.android.launcher3.folder.FolderGridOrganizer.createFolderGridOrganizer;
import static com.android.launcher3.logging.KeyboardStateManager.KeyboardState.HIDE;
import static com.android.launcher3.logging.KeyboardStateManager.KeyboardState.SHOW;
import static com.android.launcher3.logging.StatsLogManager.EventEnum;
@@ -206,7 +205,6 @@
import com.android.launcher3.model.ModelWriter;
import com.android.launcher3.model.StringCache;
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;
@@ -417,7 +415,6 @@
private final List<BackPressHandler> mBackPressedHandlers = new ArrayList<>();
private boolean mIsColdStartupAfterReboot;
- private boolean mForceConfigUpdate;
private boolean mIsNaturalScrollingEnabled;
@@ -633,6 +630,10 @@
return new ColdRebootStartupLatencyLogger();
}
+ @NonNull View getAccessibilityActionView() {
+ return findViewById(R.id.accessibility_action_view);
+ }
+
/**
* Provide {@link OnBackAnimationCallback} in below order:
* <ol>
@@ -763,7 +764,7 @@
protected void onHandleConfigurationChanged() {
Trace.beginSection("Launcher#onHandleconfigurationChanged");
try {
- if (!initDeviceProfile(mDeviceProfile.inv) && !mForceConfigUpdate) {
+ if (!initDeviceProfile(mDeviceProfile.inv)) {
return;
}
dispatchDeviceProfileChanged();
@@ -776,7 +777,6 @@
mModel.rebindCallbacks();
updateDisallowBack();
} finally {
- mForceConfigUpdate = false;
Trace.endSection();
}
}
@@ -831,26 +831,6 @@
return true;
}
- @Override
- public void invalidateParent(ItemInfo info) {
- if (info.container >= 0) {
- View collectionIcon = getWorkspace().getHomescreenIconByItemId(info.container);
- if (collectionIcon instanceof FolderIcon folderIcon
- && collectionIcon.getTag() instanceof FolderInfo) {
- if (createFolderGridOrganizer(getDeviceProfile())
- .setFolderInfo((FolderInfo) folderIcon.getTag())
- .isItemInPreview(info.rank)) {
- folderIcon.invalidate();
- }
- } else if (collectionIcon instanceof AppPairIcon appPairIcon
- && collectionIcon.getTag() instanceof AppPairInfo appPairInfo) {
- if (appPairInfo.getContents().contains(info)) {
- appPairIcon.getIconDrawableArea().redraw();
- }
- }
- }
- }
-
/**
* Returns whether we should delay spring loaded mode -- for shortcuts and widgets that have
* a configuration step, this allows the proper animations to run after other transitions.
@@ -1677,9 +1657,7 @@
}
}
- if (FeatureFlags.enableSplitContextually()) {
- handleSplitAnimationGoingToHome(LAUNCHER_SPLIT_SELECTION_EXIT_HOME);
- }
+ handleSplitAnimationGoingToHome(LAUNCHER_SPLIT_SELECTION_EXIT_HOME);
mOverlayManager.hideOverlay(isStarted());
handleGestureContract(intent);
} else if (Intent.ACTION_ALL_APPS.equals(intent.getAction())) {
@@ -3181,13 +3159,6 @@
return mAnimationCoordinator;
}
- /**
- * Set to force config update when set to true next time onHandleConfigurationChanged is called.
- */
- public void setForceConfigUpdate(boolean forceConfigUpdate) {
- mForceConfigUpdate = forceConfigUpdate;
- }
-
@Override
public View.OnLongClickListener getAllAppsItemLongClickListener() {
return ItemLongClickListener.INSTANCE_ALL_APPS;
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index a53238d..e560a14 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -20,12 +20,6 @@
import static android.content.Context.RECEIVER_EXPORTED;
import static com.android.launcher3.Flags.enableSmartspaceRemovalToggle;
-import static com.android.launcher3.InvariantDeviceProfile.GRID_NAME_PREFS_KEY;
-import static com.android.launcher3.LauncherPrefs.DB_FILE;
-import static com.android.launcher3.LauncherPrefs.GRID_NAME;
-import static com.android.launcher3.LauncherPrefs.ICON_STATE;
-import static com.android.launcher3.LauncherPrefs.THEMED_ICONS;
-import static com.android.launcher3.model.DeviceGridState.KEY_DB_FILE;
import static com.android.launcher3.model.LoaderTask.SMARTSPACE_ON_HOME_SCREEN;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
@@ -34,24 +28,21 @@
import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.pm.LauncherApps;
import android.content.pm.LauncherApps.ArchiveCompatibilityParams;
-import android.os.UserHandle;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.core.os.BuildCompat;
-import com.android.launcher3.graphics.IconShape;
+import com.android.launcher3.graphics.ThemeManager;
+import com.android.launcher3.graphics.ThemeManager.ThemeChangeListener;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.IconProvider;
import com.android.launcher3.icons.LauncherIconProvider;
import com.android.launcher3.icons.LauncherIcons;
-import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.ModelLauncherCallbacks;
import com.android.launcher3.model.WidgetsFilterDataProvider;
import com.android.launcher3.notification.NotificationListener;
@@ -66,13 +57,9 @@
import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.util.SettingsCache;
import com.android.launcher3.util.SimpleBroadcastReceiver;
-import com.android.launcher3.util.Themes;
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.widget.custom.CustomWidgetManager;
-import java.util.Locale;
-import java.util.Objects;
-
public class LauncherAppState implements SafeCloseable {
public static final String TAG = "LauncherAppState";
@@ -113,6 +100,11 @@
}
});
+ ThemeChangeListener themeChangeListener = this::refreshAndReloadLauncher;
+ ThemeManager.INSTANCE.get(context).addChangeListener(themeChangeListener);
+ mOnTerminateCallback.add(() ->
+ ThemeManager.INSTANCE.get(context).removeChangeListener(themeChangeListener));
+
ModelLauncherCallbacks callbacks = mModel.newModelCallbacks();
LauncherApps launcherApps = mContext.getSystemService(LauncherApps.class);
launcherApps.registerCallback(callbacks);
@@ -128,22 +120,11 @@
SimpleBroadcastReceiver modelChangeReceiver =
new SimpleBroadcastReceiver(UI_HELPER_EXECUTOR, mModel::onBroadcastIntent);
- final Locale oldLocale = mContext.getResources().getConfiguration().locale;
modelChangeReceiver.register(
mContext,
- () -> {
- // if local has changed before receiver is registered on bg thread,
- // mModel needs to reload.
- Locale newLocale = mContext.getResources().getConfiguration().locale;
- if (!Objects.equals(oldLocale, newLocale)) {
- mModel.forceReload();
- }
- },
- Intent.ACTION_LOCALE_CHANGED,
ACTION_DEVICE_POLICY_RESOURCE_UPDATED);
if (BuildConfig.IS_STUDIO_BUILD) {
- mContext.registerReceiver(modelChangeReceiver, new IntentFilter(ACTION_FORCE_ROLOAD),
- RECEIVER_EXPORTED);
+ modelChangeReceiver.register(mContext, RECEIVER_EXPORTED, ACTION_FORCE_ROLOAD);
}
mOnTerminateCallback.add(() -> modelChangeReceiver.unregisterReceiverSafely(mContext));
@@ -172,14 +153,9 @@
CustomWidgetManager cwm = CustomWidgetManager.INSTANCE.get(mContext);
mOnTerminateCallback.add(cwm.addWidgetRefreshCallback(mModel::rebindCallbacks)::close);
- IconObserver observer = new IconObserver();
SafeCloseable iconChangeTracker = mIconProvider.registerIconChangeListener(
- observer, MODEL_EXECUTOR.getHandler());
+ mModel::onAppIconChanged, MODEL_EXECUTOR.getHandler());
mOnTerminateCallback.add(iconChangeTracker::close);
- MODEL_EXECUTOR.execute(observer::verifyIconChanged);
- LauncherPrefs.get(context).addListener(observer, THEMED_ICONS);
- mOnTerminateCallback.add(
- () -> LauncherPrefs.get(mContext).removeListener(observer, THEMED_ICONS));
InstallSessionTracker installSessionTracker =
InstallSessionHelper.INSTANCE.get(context).registerInstallTracker(callbacks);
@@ -271,41 +247,4 @@
public static InvariantDeviceProfile getIDP(Context context) {
return InvariantDeviceProfile.INSTANCE.get(context);
}
-
- private class IconObserver
- implements IconProvider.IconChangeListener, LauncherPrefChangeListener {
-
- @Override
- public void onAppIconChanged(String packageName, UserHandle user) {
- mModel.onAppIconChanged(packageName, user);
- }
-
- @Override
- public void onSystemIconStateChanged(String iconState) {
- IconShape.INSTANCE.get(mContext).pickBestShape(mContext);
- refreshAndReloadLauncher();
- LauncherPrefs.get(mContext).put(ICON_STATE, iconState);
- }
-
- void verifyIconChanged() {
- String iconState = mIconProvider.getSystemIconState();
- if (!iconState.equals(LauncherPrefs.get(mContext).get(ICON_STATE))) {
- onSystemIconStateChanged(iconState);
- }
- }
-
- @Override
- public void onPrefChanged(String key) {
- if (Themes.KEY_THEMED_ICONS.equals(key)) {
- mIconProvider.setIconThemeSupported(Themes.isThemedIconEnabled(mContext));
- verifyIconChanged();
- } else if (GRID_NAME_PREFS_KEY.equals(key)) {
- FileLog.d(TAG, "onPrefChanged GRID_NAME changed: "
- + LauncherPrefs.get(mContext).get(GRID_NAME));
- } else if (KEY_DB_FILE.equals(key)) {
- FileLog.d(TAG, "onPrefChanged DB_FILE changed: "
- + LauncherPrefs.get(mContext).get(DB_FILE));
- }
- }
- }
}
diff --git a/src/com/android/launcher3/LauncherModel.kt b/src/com/android/launcher3/LauncherModel.kt
index b56df46..185629b 100644
--- a/src/com/android/launcher3/LauncherModel.kt
+++ b/src/com/android/launcher3/LauncherModel.kt
@@ -176,7 +176,6 @@
fun onBroadcastIntent(intent: Intent) {
if (DEBUG_RECEIVER || sDebugTracing) Log.d(TAG, "onReceive intent=$intent")
when (intent.action) {
- Intent.ACTION_LOCALE_CHANGED,
LauncherAppState.ACTION_FORCE_ROLOAD ->
// If we have changed locale we need to clear out the labels in all apps/workspace.
forceReload()
diff --git a/src/com/android/launcher3/LauncherPrefs.kt b/src/com/android/launcher3/LauncherPrefs.kt
index 5b9c2fa..d8bb84e 100644
--- a/src/com/android/launcher3/LauncherPrefs.kt
+++ b/src/com/android/launcher3/LauncherPrefs.kt
@@ -23,72 +23,220 @@
import com.android.launcher3.InvariantDeviceProfile.GRID_NAME_PREFS_KEY
import com.android.launcher3.LauncherFiles.DEVICE_PREFERENCES_KEY
import com.android.launcher3.LauncherFiles.SHARED_PREFERENCES_KEY
+import com.android.launcher3.dagger.ApplicationContext
+import com.android.launcher3.dagger.LauncherAppComponent
+import com.android.launcher3.dagger.LauncherAppSingleton
import com.android.launcher3.model.DeviceGridState
import com.android.launcher3.pm.InstallSessionHelper
import com.android.launcher3.provider.RestoreDbTask
import com.android.launcher3.provider.RestoreDbTask.FIRST_LOAD_AFTER_RESTORE_KEY
import com.android.launcher3.settings.SettingsActivity
import com.android.launcher3.states.RotationHelper
+import com.android.launcher3.util.DaggerSingletonObject
import com.android.launcher3.util.DisplayController
-import com.android.launcher3.util.MainThreadInitializedObject
-import com.android.launcher3.util.SafeCloseable
-import com.android.launcher3.util.Themes
+import javax.inject.Inject
/**
* Manages Launcher [SharedPreferences] through [Item] instances.
*
* TODO(b/262721340): Replace all direct SharedPreference refs with LauncherPrefs / Item methods.
*/
-abstract class LauncherPrefs : SafeCloseable {
+@LauncherAppSingleton
+open class LauncherPrefs
+@Inject
+constructor(@ApplicationContext private val encryptedContext: Context) {
+
+ private val deviceProtectedSharedPrefs: SharedPreferences by lazy {
+ encryptedContext
+ .createDeviceProtectedStorageContext()
+ .getSharedPreferences(BOOT_AWARE_PREFS_KEY, MODE_PRIVATE)
+ }
+
+ open val Item.sharedPrefs: SharedPreferences
+ get() =
+ if (encryptionType == EncryptionType.DEVICE_PROTECTED) deviceProtectedSharedPrefs
+ else encryptedContext.getSharedPreferences(sharedPrefFile, MODE_PRIVATE)
/** Returns the value with type [T] for [item]. */
- abstract fun <T> get(item: ContextualItem<T>): T
+ fun <T> get(item: ContextualItem<T>): T =
+ getInner(item, item.defaultValueFromContext(encryptedContext))
/** Returns the value with type [T] for [item]. */
- abstract fun <T> get(item: ConstantItem<T>): T
+ fun <T> get(item: ConstantItem<T>): T = getInner(item, item.defaultValue)
- /** Stores the values for each item in preferences. */
- abstract fun put(vararg itemsToValues: Pair<Item, Any>)
+ /**
+ * Retrieves the value for an [Item] from [SharedPreferences]. It handles method typing via the
+ * default value type, and will throw an error if the type of the item provided is not a
+ * `String`, `Boolean`, `Float`, `Int`, `Long`, or `Set<String>`.
+ */
+ @Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST")
+ private fun <T> getInner(item: Item, default: T): T {
+ val sp = item.sharedPrefs
- /** Stores the [value] with type [T] for [item] in preferences. */
- abstract fun <T : Any> put(item: Item, value: T)
+ return when (item.type) {
+ String::class.java -> sp.getString(item.sharedPrefKey, default as? String)
+ Boolean::class.java,
+ java.lang.Boolean::class.java -> sp.getBoolean(item.sharedPrefKey, default as Boolean)
+ Int::class.java,
+ java.lang.Integer::class.java -> sp.getInt(item.sharedPrefKey, default as Int)
+ Float::class.java,
+ java.lang.Float::class.java -> sp.getFloat(item.sharedPrefKey, default as Float)
+ Long::class.java,
+ java.lang.Long::class.java -> sp.getLong(item.sharedPrefKey, default as Long)
+ Set::class.java -> sp.getStringSet(item.sharedPrefKey, default as? Set<String>)
+ else ->
+ throw IllegalArgumentException(
+ "item type: ${item.type}" + " is not compatible with sharedPref methods"
+ )
+ }
+ as T
+ }
- /** Synchronous version of [put]. */
- abstract fun putSync(vararg itemsToValues: Pair<Item, Any>)
+ /**
+ * Stores each of the values provided in `SharedPreferences` according to the configuration
+ * contained within the associated items provided. Internally, it uses apply, so the caller
+ * cannot assume that the values that have been put are immediately available for use.
+ *
+ * The forEach loop is necessary here since there is 1 `SharedPreference.Editor` returned from
+ * prepareToPutValue(itemsToValues) for every distinct `SharedPreferences` file present in the
+ * provided item configurations.
+ */
+ fun put(vararg itemsToValues: Pair<Item, Any>): Unit =
+ prepareToPutValues(itemsToValues).forEach { it.apply() }
- /** Registers [listener] for [items]. */
- abstract fun addListener(listener: LauncherPrefChangeListener, vararg items: Item)
+ /** See referenced `put` method above. */
+ fun <T : Any> put(item: Item, value: T): Unit = put(item.to(value))
- /** Unregisters [listener] for [items]. */
- abstract fun removeListener(listener: LauncherPrefChangeListener, vararg items: Item)
+ /**
+ * Synchronously stores all the values provided according to their associated Item
+ * configuration.
+ */
+ fun putSync(vararg itemsToValues: Pair<Item, Any>): Unit =
+ prepareToPutValues(itemsToValues).forEach { it.commit() }
- /** Returns `true` iff all [items] have a value. */
- abstract fun has(vararg items: Item): Boolean
+ /**
+ * Updates the values stored in `SharedPreferences` for each corresponding Item-value pair. If
+ * the item is boot aware, this method updates both the boot aware and the encrypted files. This
+ * is done because: 1) It allows for easy roll-back if the data is already in encrypted prefs
+ * and we need to turn off the boot aware data feature & 2) It simplifies Backup/Restore, which
+ * already points to encrypted storage.
+ *
+ * Returns a list of editors with all transactions added so that the caller can determine to use
+ * .apply() or .commit()
+ */
+ private fun prepareToPutValues(
+ updates: Array<out Pair<Item, Any>>
+ ): List<SharedPreferences.Editor> {
+ val updatesPerPrefFile = updates.groupBy { it.first.sharedPrefs }.toMap()
- /** Removes the value for each item in [items]. */
- abstract fun remove(vararg items: Item)
+ return updatesPerPrefFile.map { (sharedPref, itemList) ->
+ sharedPref.edit().apply { itemList.forEach { (item, value) -> putValue(item, value) } }
+ }
+ }
- /** Synchronous version of [remove]. */
- abstract fun removeSync(vararg items: Item)
+ /**
+ * Handles adding values to `SharedPreferences` regardless of type. This method is especially
+ * helpful for updating `SharedPreferences` values for `List<<Item>Any>` that have multiple
+ * types of Item values.
+ */
+ @Suppress("UNCHECKED_CAST")
+ private fun SharedPreferences.Editor.putValue(
+ item: Item,
+ value: Any?,
+ ): SharedPreferences.Editor =
+ when (item.type) {
+ String::class.java -> putString(item.sharedPrefKey, value as? String)
+ Boolean::class.java,
+ java.lang.Boolean::class.java -> putBoolean(item.sharedPrefKey, value as Boolean)
+ Int::class.java,
+ java.lang.Integer::class.java -> putInt(item.sharedPrefKey, value as Int)
+ Float::class.java,
+ java.lang.Float::class.java -> putFloat(item.sharedPrefKey, value as Float)
+ Long::class.java,
+ java.lang.Long::class.java -> putLong(item.sharedPrefKey, value as Long)
+ Set::class.java -> putStringSet(item.sharedPrefKey, value as? Set<String>)
+ else ->
+ throw IllegalArgumentException(
+ "item type: ${item.type} is not compatible with sharedPref methods"
+ )
+ }
+
+ /**
+ * After calling this method, the listener will be notified of any future updates to the
+ * `SharedPreferences` files associated with the provided list of items. The listener will need
+ * to filter update notifications so they don't activate for non-relevant updates.
+ */
+ fun addListener(listener: LauncherPrefChangeListener, vararg items: Item) {
+ items
+ .map { it.sharedPrefs }
+ .distinct()
+ .forEach { it.registerOnSharedPreferenceChangeListener(listener) }
+ }
+
+ /**
+ * Stops the listener from getting notified of any more updates to any of the
+ * `SharedPreferences` files associated with any of the provided list of [Item].
+ */
+ fun removeListener(listener: LauncherPrefChangeListener, vararg items: Item) {
+ // If a listener is not registered to a SharedPreference, unregistering it does nothing
+ items
+ .map { it.sharedPrefs }
+ .distinct()
+ .forEach { it.unregisterOnSharedPreferenceChangeListener(listener) }
+ }
+
+ /**
+ * Checks if all the provided [Item] have values stored in their corresponding
+ * `SharedPreferences` files.
+ */
+ fun has(vararg items: Item): Boolean {
+ items
+ .groupBy { it.sharedPrefs }
+ .forEach { (prefs, itemsSublist) ->
+ if (!itemsSublist.none { !prefs.contains(it.sharedPrefKey) }) return false
+ }
+ return true
+ }
+
+ /**
+ * Asynchronously removes the [Item]'s value from its corresponding `SharedPreferences` file.
+ */
+ fun remove(vararg items: Item) = prepareToRemove(items).forEach { it.apply() }
+
+ /** Synchronously removes the [Item]'s value from its corresponding `SharedPreferences` file. */
+ fun removeSync(vararg items: Item) = prepareToRemove(items).forEach { it.commit() }
+
+ /**
+ * Removes the key value pairs stored in `SharedPreferences` for each corresponding Item. If the
+ * item is boot aware, this method removes the data from both the boot aware and encrypted
+ * files.
+ *
+ * @return a list of editors with all transactions added so that the caller can determine to use
+ * .apply() or .commit()
+ */
+ private fun prepareToRemove(items: Array<out Item>): List<SharedPreferences.Editor> {
+ val itemsPerFile = items.groupBy { it.sharedPrefs }.toMap()
+
+ return itemsPerFile.map { (prefs, items) ->
+ prefs.edit().also { editor ->
+ items.forEach { item -> editor.remove(item.sharedPrefKey) }
+ }
+ }
+ }
companion object {
@VisibleForTesting const val BOOT_AWARE_PREFS_KEY = "boot_aware_prefs"
- @JvmField
- var INSTANCE = MainThreadInitializedObject<LauncherPrefs> { LauncherPrefsImpl(it) }
+ @JvmField val INSTANCE = DaggerSingletonObject(LauncherAppComponent::getLauncherPrefs)
@JvmStatic fun get(context: Context): LauncherPrefs = INSTANCE.get(context)
const val TASKBAR_PINNING_KEY = "TASKBAR_PINNING_KEY"
const val TASKBAR_PINNING_DESKTOP_MODE_KEY = "TASKBAR_PINNING_DESKTOP_MODE_KEY"
const val SHOULD_SHOW_SMARTSPACE_KEY = "SHOULD_SHOW_SMARTSPACE_KEY"
- @JvmField
- val ICON_STATE = nonRestorableItem("pref_icon_shape_path", "", EncryptionType.ENCRYPTED)
@JvmField
val ENABLE_TWOLINE_ALLAPPS_TOGGLE = backedUpItem("pref_enable_two_line_toggle", false)
- @JvmField
- val THEMED_ICONS = backedUpItem(Themes.KEY_THEMED_ICONS, false, EncryptionType.ENCRYPTED)
@JvmField val PROMISE_ICON_IDS = backedUpItem(InstallSessionHelper.PROMISE_ICON_IDS, "")
@JvmField val WORK_EDU_STEP = backedUpItem("showed_work_profile_edu", 0)
@JvmField
@@ -212,214 +360,6 @@
}
}
-private class LauncherPrefsImpl(private val encryptedContext: Context) : LauncherPrefs() {
- private val deviceProtectedStorageContext =
- encryptedContext.createDeviceProtectedStorageContext()
-
- private val bootAwarePrefs
- get() =
- deviceProtectedStorageContext.getSharedPreferences(BOOT_AWARE_PREFS_KEY, MODE_PRIVATE)
-
- private val Item.encryptedPrefs
- get() = encryptedContext.getSharedPreferences(sharedPrefFile, MODE_PRIVATE)
-
- private fun chooseSharedPreferences(item: Item): SharedPreferences =
- if (item.encryptionType == EncryptionType.DEVICE_PROTECTED) bootAwarePrefs
- else item.encryptedPrefs
-
- /** Wrapper around `getInner` for a `ContextualItem` */
- override fun <T> get(item: ContextualItem<T>): T =
- getInner(item, item.defaultValueFromContext(encryptedContext))
-
- /** Wrapper around `getInner` for an `Item` */
- override fun <T> get(item: ConstantItem<T>): T = getInner(item, item.defaultValue)
-
- /**
- * Retrieves the value for an [Item] from [SharedPreferences]. It handles method typing via the
- * default value type, and will throw an error if the type of the item provided is not a
- * `String`, `Boolean`, `Float`, `Int`, `Long`, or `Set<String>`.
- */
- @Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST")
- private fun <T> getInner(item: Item, default: T): T {
- val sp = chooseSharedPreferences(item)
-
- return when (item.type) {
- String::class.java -> sp.getString(item.sharedPrefKey, default as? String)
- Boolean::class.java,
- java.lang.Boolean::class.java -> sp.getBoolean(item.sharedPrefKey, default as Boolean)
- Int::class.java,
- java.lang.Integer::class.java -> sp.getInt(item.sharedPrefKey, default as Int)
- Float::class.java,
- java.lang.Float::class.java -> sp.getFloat(item.sharedPrefKey, default as Float)
- Long::class.java,
- java.lang.Long::class.java -> sp.getLong(item.sharedPrefKey, default as Long)
- Set::class.java -> sp.getStringSet(item.sharedPrefKey, default as? Set<String>)
- else ->
- throw IllegalArgumentException(
- "item type: ${item.type}" + " is not compatible with sharedPref methods"
- )
- }
- as T
- }
-
- /**
- * Stores each of the values provided in `SharedPreferences` according to the configuration
- * contained within the associated items provided. Internally, it uses apply, so the caller
- * cannot assume that the values that have been put are immediately available for use.
- *
- * The forEach loop is necessary here since there is 1 `SharedPreference.Editor` returned from
- * prepareToPutValue(itemsToValues) for every distinct `SharedPreferences` file present in the
- * provided item configurations.
- */
- override fun put(vararg itemsToValues: Pair<Item, Any>): Unit =
- prepareToPutValues(itemsToValues).forEach { it.apply() }
-
- /** See referenced `put` method above. */
- override fun <T : Any> put(item: Item, value: T): Unit = put(item.to(value))
-
- /**
- * Synchronously stores all the values provided according to their associated Item
- * configuration.
- */
- override fun putSync(vararg itemsToValues: Pair<Item, Any>): Unit =
- prepareToPutValues(itemsToValues).forEach { it.commit() }
-
- /**
- * Updates the values stored in `SharedPreferences` for each corresponding Item-value pair. If
- * the item is boot aware, this method updates both the boot aware and the encrypted files. This
- * is done because: 1) It allows for easy roll-back if the data is already in encrypted prefs
- * and we need to turn off the boot aware data feature & 2) It simplifies Backup/Restore, which
- * already points to encrypted storage.
- *
- * Returns a list of editors with all transactions added so that the caller can determine to use
- * .apply() or .commit()
- */
- private fun prepareToPutValues(
- updates: Array<out Pair<Item, Any>>
- ): List<SharedPreferences.Editor> {
- val updatesPerPrefFile =
- updates
- .filter { it.first.encryptionType != EncryptionType.DEVICE_PROTECTED }
- .groupBy { it.first.encryptedPrefs }
- .toMutableMap()
-
- val bootAwareUpdates =
- updates.filter { it.first.encryptionType == EncryptionType.DEVICE_PROTECTED }
- if (bootAwareUpdates.isNotEmpty()) {
- updatesPerPrefFile[bootAwarePrefs] = bootAwareUpdates
- }
-
- return updatesPerPrefFile.map { prefToItemValueList ->
- prefToItemValueList.key.edit().apply {
- prefToItemValueList.value.forEach { itemToValue: Pair<Item, Any> ->
- putValue(itemToValue.first, itemToValue.second)
- }
- }
- }
- }
-
- /**
- * Handles adding values to `SharedPreferences` regardless of type. This method is especially
- * helpful for updating `SharedPreferences` values for `List<<Item>Any>` that have multiple
- * types of Item values.
- */
- @Suppress("UNCHECKED_CAST")
- private fun SharedPreferences.Editor.putValue(
- item: Item,
- value: Any?,
- ): SharedPreferences.Editor =
- when (item.type) {
- String::class.java -> putString(item.sharedPrefKey, value as? String)
- Boolean::class.java,
- java.lang.Boolean::class.java -> putBoolean(item.sharedPrefKey, value as Boolean)
- Int::class.java,
- java.lang.Integer::class.java -> putInt(item.sharedPrefKey, value as Int)
- Float::class.java,
- java.lang.Float::class.java -> putFloat(item.sharedPrefKey, value as Float)
- Long::class.java,
- java.lang.Long::class.java -> putLong(item.sharedPrefKey, value as Long)
- Set::class.java -> putStringSet(item.sharedPrefKey, value as? Set<String>)
- else ->
- throw IllegalArgumentException(
- "item type: ${item.type} is not compatible with sharedPref methods"
- )
- }
-
- /**
- * After calling this method, the listener will be notified of any future updates to the
- * `SharedPreferences` files associated with the provided list of items. The listener will need
- * to filter update notifications so they don't activate for non-relevant updates.
- */
- override fun addListener(listener: LauncherPrefChangeListener, vararg items: Item) {
- items
- .map { chooseSharedPreferences(it) }
- .distinct()
- .forEach { it.registerOnSharedPreferenceChangeListener(listener) }
- }
-
- /**
- * Stops the listener from getting notified of any more updates to any of the
- * `SharedPreferences` files associated with any of the provided list of [Item].
- */
- override fun removeListener(listener: LauncherPrefChangeListener, vararg items: Item) {
- // If a listener is not registered to a SharedPreference, unregistering it does nothing
- items
- .map { chooseSharedPreferences(it) }
- .distinct()
- .forEach { it.unregisterOnSharedPreferenceChangeListener(listener) }
- }
-
- /**
- * Checks if all the provided [Item] have values stored in their corresponding
- * `SharedPreferences` files.
- */
- override fun has(vararg items: Item): Boolean {
- items
- .groupBy { chooseSharedPreferences(it) }
- .forEach { (prefs, itemsSublist) ->
- if (!itemsSublist.none { !prefs.contains(it.sharedPrefKey) }) return false
- }
- return true
- }
-
- /**
- * Asynchronously removes the [Item]'s value from its corresponding `SharedPreferences` file.
- */
- override fun remove(vararg items: Item) = prepareToRemove(items).forEach { it.apply() }
-
- /** Synchronously removes the [Item]'s value from its corresponding `SharedPreferences` file. */
- override fun removeSync(vararg items: Item) = prepareToRemove(items).forEach { it.commit() }
-
- /**
- * Removes the key value pairs stored in `SharedPreferences` for each corresponding Item. If the
- * item is boot aware, this method removes the data from both the boot aware and encrypted
- * files.
- *
- * @return a list of editors with all transactions added so that the caller can determine to use
- * .apply() or .commit()
- */
- private fun prepareToRemove(items: Array<out Item>): List<SharedPreferences.Editor> {
- val itemsPerFile =
- items
- .filter { it.encryptionType != EncryptionType.DEVICE_PROTECTED }
- .groupBy { it.encryptedPrefs }
- .toMutableMap()
-
- val bootAwareUpdates = items.filter { it.encryptionType == EncryptionType.DEVICE_PROTECTED }
- if (bootAwareUpdates.isNotEmpty()) {
- itemsPerFile[bootAwarePrefs] = bootAwareUpdates
- }
-
- return itemsPerFile.map { (prefs, items) ->
- prefs.edit().also { editor ->
- items.forEach { item -> editor.remove(item.sharedPrefKey) }
- }
- }
- }
-
- override fun close() {}
-}
-
abstract class Item {
abstract val sharedPrefKey: String
abstract val isBackedUp: Boolean
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 6e2d357..a526b89 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -24,25 +24,38 @@
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
+import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.os.Binder;
+import android.os.Bundle;
import android.os.Process;
import android.text.TextUtils;
import android.util.Log;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.model.ModelDbController;
+import com.android.launcher3.util.LayoutImportExportHelper;
import com.android.launcher3.widget.LauncherWidgetHolder;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
import java.util.function.ToIntFunction;
public class LauncherProvider extends ContentProvider {
private static final String TAG = "LauncherProvider";
+ // Method API For Provider#call method.
+ private static final String METHOD_EXPORT_LAYOUT_XML = "EXPORT_LAYOUT_XML";
+ private static final String METHOD_IMPORT_LAYOUT_XML = "IMPORT_LAYOUT_XML";
+ private static final String KEY_RESULT = "KEY_RESULT";
+ private static final String KEY_LAYOUT = "KEY_LAYOUT";
+ private static final String SUCCESS = "success";
+ private static final String FAILURE = "failure";
+
/**
* $ adb shell dumpsys activity provider com.android.launcher3
*/
@@ -142,6 +155,43 @@
return executeControllerTask(c -> c.update(args.table, values, args.where, args.args));
}
+ @Override
+ public Bundle call(String method, String arg, Bundle extras) {
+ Bundle b = new Bundle();
+
+ // The caller must have the read or write permission for this content provider to
+ // access the "call" method at all. We also enforce the appropriate per-method permissions.
+ switch(method) {
+ case METHOD_EXPORT_LAYOUT_XML:
+ if (getContext().checkCallingOrSelfPermission(getReadPermission())
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Caller doesn't have read permission");
+ }
+
+ CompletableFuture<String> resultFuture = LayoutImportExportHelper.INSTANCE
+ .exportModelDbAsXmlFuture(getContext());
+ try {
+ b.putString(KEY_LAYOUT, resultFuture.get());
+ b.putString(KEY_RESULT, SUCCESS);
+ } catch (ExecutionException | InterruptedException e) {
+ b.putString(KEY_RESULT, FAILURE);
+ }
+ return b;
+
+ case METHOD_IMPORT_LAYOUT_XML:
+ if (getContext().checkCallingOrSelfPermission(getWritePermission())
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Caller doesn't have write permission");
+ }
+
+ LayoutImportExportHelper.INSTANCE.importModelFromXml(getContext(), arg);
+ b.putString(KEY_RESULT, SUCCESS);
+ return b;
+ default:
+ return null;
+ }
+ }
+
private int executeControllerTask(ToIntFunction<ModelDbController> task) {
if (Binder.getCallingPid() == Process.myPid()) {
throw new IllegalArgumentException("Same process should call model directly");
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index b05a46d..5072e37 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -718,12 +718,14 @@
}
/**
- * Queues the given callback to be run once {@code mPageScrolls} has been initialized.
+ * Run the given `callback` immediately once {@code mPageScrolls} has been initialized,
+ * otherwise queue the callback to `mOnPageScrollsInitializedCallbacks`.
*/
public void runOnPageScrollsInitialized(Runnable callback) {
- mOnPageScrollsInitializedCallbacks.add(callback);
if (isPageScrollsInitialized()) {
- onPageScrollsInitialized();
+ callback.run();
+ } else {
+ mOnPageScrollsInitializedCallbacks.add(callback);
}
}
@@ -903,14 +905,12 @@
@Override
public void onViewAdded(View child) {
super.onViewAdded(child);
- mPageScrolls = null;
dispatchPageCountChanged();
}
@Override
public void onViewRemoved(View child) {
super.onViewRemoved(child);
- mPageScrolls = null;
runOnPageScrollsInitialized(() -> {
mCurrentPage = validateNewPage(mCurrentPage);
mCurrentScrollOverPage = mCurrentPage;
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 9060691..7563493 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -74,9 +74,11 @@
import androidx.core.graphics.ColorUtils;
import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
+import com.android.launcher3.graphics.ThemeManager;
import com.android.launcher3.graphics.TintedDrawableSpan;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.CacheableShortcutInfo;
+import com.android.launcher3.icons.IconThemeController;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
@@ -88,7 +90,6 @@
import com.android.launcher3.util.FlagOp;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
-import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.BaseDragLayer;
import com.android.launcher3.widget.PendingAddShortcutInfo;
@@ -626,7 +627,6 @@
@WorkerThread
public static <T extends Context & ActivityContext> Pair<AdaptiveIconDrawable, Drawable>
getFullDrawable(T context, ItemInfo info, int width, int height, boolean useTheme) {
- useTheme &= Themes.isThemedIconEnabled(context);
LauncherAppState appState = LauncherAppState.getInstance(context);
Drawable mainIcon = null;
@@ -681,7 +681,7 @@
} else {
// Wrap the main icon in AID
try (LauncherIcons li = LauncherIcons.obtain(context)) {
- result = li.wrapToAdaptiveIcon(mainIcon, null);
+ result = li.wrapToAdaptiveIcon(mainIcon);
}
}
if (result == null) {
@@ -690,15 +690,15 @@
// Inject theme icon drawable
if (ATLEAST_T && useTheme) {
- try (LauncherIcons li = LauncherIcons.obtain(context)) {
- if (li.getThemeController() != null) {
- AdaptiveIconDrawable themed = li.getThemeController().createThemedAdaptiveIcon(
- context,
- result,
- info instanceof ItemInfoWithIcon iiwi ? iiwi.bitmap : null);
- if (themed != null) {
- result = themed;
- }
+ IconThemeController themeController =
+ ThemeManager.INSTANCE.get(context).getThemeController();
+ if (themeController != null) {
+ AdaptiveIconDrawable themed = themeController.createThemedAdaptiveIcon(
+ context,
+ result,
+ info instanceof ItemInfoWithIcon iiwi ? iiwi.bitmap : null);
+ if (themed != null) {
+ result = themed;
}
}
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index a064c88..0d050b2 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -70,6 +70,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import androidx.core.view.ViewCompat;
import com.android.app.animation.Interpolators;
import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
@@ -108,6 +109,7 @@
import com.android.launcher3.pageindicators.PageIndicator;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.statemanager.StateManager.StateHandler;
+import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.touch.WorkspaceTouchListener;
import com.android.launcher3.util.EdgeEffectCompat;
@@ -303,6 +305,8 @@
private final StatsLogManager mStatsLogManager;
private final MSDLPlayerWrapper mMSDLPlayerWrapper;
+ @Nullable
+ private DragController.DragListener mAccessibilityDragListener;
/**
* Used to inflate the Workspace from XML.
@@ -512,6 +516,9 @@
}
}
+ if (mAccessibilityDragListener != null) {
+ mAccessibilityDragListener.onDragStart(dragObject, options);
+ }
if (!mLauncher.isInState(EDIT_MODE)) {
mLauncher.getStateManager().goToState(SPRING_LOADED);
}
@@ -548,6 +555,9 @@
}
});
+ if (mAccessibilityDragListener != null) {
+ mAccessibilityDragListener.onDragEnd();
+ }
mDragInfo = null;
mDragSourceInternal = null;
}
@@ -1656,7 +1666,7 @@
child.setVisibility(INVISIBLE);
if (options.isAccessibleDrag) {
- mDragController.addDragListener(
+ mAccessibilityDragListener =
new AccessibleDragListenerAdapter(this, WorkspaceAccessibilityHelper::new) {
@Override
protected void enableAccessibleDrag(boolean enable,
@@ -1669,7 +1679,7 @@
IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
}
}
- });
+ };
}
beginDragShared(child, this, options);
@@ -2232,6 +2242,23 @@
}
mStatsLogManager.logger().withItemInfo(d.dragInfo).withInstanceId(d.logInstanceId)
.log(LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED);
+
+ if (mAccessibilityDragListener != null) {
+ // This code needs to be called after StateManager.cancelAnimation. Before changing
+ // the order of operations in this method related to the StateListener below, please
+ // test that accessibility moves retain focus after accessibility dropping an item.
+ // Accessibility focus must be requested after launcher is back to a normal state
+ mLauncher.getStateManager().addStateListener(new StateListener<LauncherState>() {
+ @Override
+ public void onStateTransitionComplete(LauncherState finalState) {
+ if (finalState == NORMAL) {
+ cell.performAccessibilityAction(
+ AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
+ mLauncher.getStateManager().removeStateListener(this);
+ }
+ }
+ });
+ }
}
if (d.stateAnnouncer != null && !droppedOnOriginalCell) {
@@ -3519,8 +3546,15 @@
@Override
protected boolean canAnnouncePageDescription() {
- // b/383247157: Disable disruptive home screen page announcement
- return false;
+ return Float.compare(mOverlayProgress, 0f) == 0;
+ }
+
+ @Override
+ protected void announcePageForAccessibility() {
+ // Talkback focuses on AccessibilityActionView by default, so we need to modify the state
+ // description there in order for the change in page scroll to be announced.
+ ViewCompat.setStateDescription(mLauncher.getAccessibilityActionView(),
+ getCurrentPageDescription());
}
@Override
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 81d6631..78b53a9 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -199,6 +199,8 @@
host.requestFocus();
host.sendAccessibilityEvent(TYPE_VIEW_FOCUSED);
host.performAccessibilityAction(ACTION_ACCESSIBILITY_FOCUS, null);
+ AbstractFloatingView.closeOpenViews(mContext, /* animate= */ false,
+ AbstractFloatingView.TYPE_WIDGET_RESIZE_FRAME);
});
return true;
} else if (action == DEEP_SHORTCUTS) {
diff --git a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
index d115f9f..c91e783 100644
--- a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
@@ -24,7 +24,6 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.R;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.shortcuts.DeepShortcutView;
@@ -67,7 +66,6 @@
screenId, coordinates[0], coordinates[1]);
mContext.bindItems(Collections.singletonList(info), true);
AbstractFloatingView.closeAllOpenViews(mContext);
- announceConfirmation(R.string.item_added_to_workspace);
}));
return true;
}
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
index 60bf3ea..e8b7247 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
@@ -40,8 +40,6 @@
import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.Flags;
-import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.R;
import com.android.launcher3.allapps.search.SearchAdapterProvider;
import com.android.launcher3.model.data.AppInfo;
@@ -219,9 +217,7 @@
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case VIEW_TYPE_ICON:
- int layout = (Flags.enableTwolineToggle()
- && LauncherPrefs.ENABLE_TWOLINE_ALLAPPS_TOGGLE.get(
- mActivityContext.getApplicationContext()))
+ int layout = mActivityContext.getDeviceProfile().inv.enableTwoLinesInAllApps
? R.layout.all_apps_icon_twoline : R.layout.all_apps_icon;
BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
layout, parent, false);
diff --git a/src/com/android/launcher3/anim/AnimatedPropertySetter.java b/src/com/android/launcher3/anim/AnimatedPropertySetter.java
index 82e645a..0f1b8ad 100644
--- a/src/com/android/launcher3/anim/AnimatedPropertySetter.java
+++ b/src/com/android/launcher3/anim/AnimatedPropertySetter.java
@@ -20,6 +20,7 @@
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
+import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
@@ -121,19 +122,31 @@
* Adds a listener to be run on every frame of the animation
*/
public void addOnFrameListener(ValueAnimator.AnimatorUpdateListener listener) {
- if (mProgressAnimator == null) {
- mProgressAnimator = ValueAnimator.ofFloat(0, 1);
- }
-
- mProgressAnimator.addUpdateListener(listener);
+ getProgressAnimator().addUpdateListener(listener);
}
@Override
public void addEndListener(Consumer<Boolean> listener) {
+ getProgressAnimator().addListener(AnimatorListeners.forEndCallback(listener));
+ }
+
+ /**
+ * Add a callback to run on progress start.
+ */
+ public void addStartListener(Runnable listener) {
+ getProgressAnimator().addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ listener.run();
+ }
+ });
+ }
+
+ private ValueAnimator getProgressAnimator() {
if (mProgressAnimator == null) {
mProgressAnimator = ValueAnimator.ofFloat(0, 1);
}
- mProgressAnimator.addListener(AnimatorListeners.forEndCallback(listener));
+ return mProgressAnimator;
}
/**
diff --git a/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt b/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt
index 034b686..81a92f6 100644
--- a/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt
+++ b/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt
@@ -27,7 +27,6 @@
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener
import com.android.launcher3.icons.BitmapInfo
import com.android.launcher3.model.data.AppPairInfo
-import com.android.launcher3.util.Themes
import com.android.launcher3.views.ActivityContext
/**
@@ -46,12 +45,11 @@
@JvmStatic
fun composeDrawable(
appPairInfo: AppPairInfo,
- p: AppPairIconDrawingParams
+ 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)
- val appIcon2 = appPairInfo.getSecondApp().newIcon(p.context, flags)
+ // Generate new icons, using themed flag since the icon is drawn on homescreen
+ val appIcon1 = appPairInfo.getFirstApp().newIcon(p.context, BitmapInfo.FLAG_THEMED)
+ val appIcon2 = appPairInfo.getSecondApp().newIcon(p.context, BitmapInfo.FLAG_THEMED)
appIcon1.setBounds(0, 0, p.memberIconSize.toInt(), p.memberIconSize.toInt())
appIcon2.setBounds(0, 0, p.memberIconSize.toInt(), p.memberIconSize.toInt())
@@ -125,7 +123,7 @@
((parentIcon.width - drawParams.backgroundSize) / 2).toInt(),
// y-coordinate in parent's coordinate system
(parentIcon.paddingTop + drawParams.standardIconPadding + drawParams.outerPadding)
- .toInt()
+ .toInt(),
)
}
@@ -140,17 +138,13 @@
drawable.draw(canvas)
}
- /**
- * Sets the scale of the icon background while hovered.
- */
+ /** Sets the scale of the icon background while hovered. */
fun setHoverScale(scale: Float) {
drawParams.hoverScale = scale
redraw()
}
- /**
- * Gets the scale of the icon background while hovered.
- */
+ /** Gets the scale of the icon background while hovered. */
fun getHoverScale(): Float {
return drawParams.hoverScale
}
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 9e38824..1fe42f7 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -201,15 +201,6 @@
"USE_LOCAL_ICON_OVERRIDES", ENABLED,
"Use inbuilt monochrome icons if app doesn't provide one");
- // Aconfig migration complete for ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.
- public static final BooleanFlag ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE = getDebugFlag(
- 270393453, "ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE", DISABLED,
- "Enable initiating split screen from workspace to workspace.");
- public static boolean enableSplitContextually() {
- return ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get() ||
- com.android.wm.shell.Flags.enableSplitContextual();
- }
-
// TODO(Block 29): Clean up flags
// Aconfig migration complete for ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT.
public static final BooleanFlag ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT = getDebugFlag(270393897,
diff --git a/src/com/android/launcher3/contextualeducation/ContextualEduStatsManager.java b/src/com/android/launcher3/contextualeducation/ContextualEduStatsManager.java
deleted file mode 100644
index 5664174..0000000
--- a/src/com/android/launcher3/contextualeducation/ContextualEduStatsManager.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 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.contextualeducation;
-
-import com.android.launcher3.dagger.LauncherAppSingleton;
-import com.android.launcher3.dagger.LauncherBaseAppComponent;
-import com.android.launcher3.util.DaggerSingletonObject;
-import com.android.systemui.contextualeducation.GestureType;
-
-import javax.inject.Inject;
-
-/**
- * A class to update contextual education data. It is a no-op implementation and could be
- * overridden through dagger modules to provide a real implementation.
- */
-@LauncherAppSingleton
-public class ContextualEduStatsManager {
- public static final DaggerSingletonObject<ContextualEduStatsManager> INSTANCE =
- new DaggerSingletonObject<>(LauncherBaseAppComponent::getContextualEduStatsManager);
-
- @Inject
- public ContextualEduStatsManager() { }
-
-
- /**
- * Updates contextual education stats when a gesture is triggered
- * @param isTrackpadGesture indicates if the gesture is triggered by trackpad
- * @param gestureType type of gesture triggered
- */
- public void updateEduStats(boolean isTrackpadGesture, GestureType gestureType) {
- }
-}
diff --git a/quickstep/dagger/com/android/launcher3/dagger/LauncherAppModule.java b/src/com/android/launcher3/dagger/LauncherAppModule.java
similarity index 79%
rename from quickstep/dagger/com/android/launcher3/dagger/LauncherAppModule.java
rename to src/com/android/launcher3/dagger/LauncherAppModule.java
index 1711fc1..ef136d0 100644
--- a/quickstep/dagger/com/android/launcher3/dagger/LauncherAppModule.java
+++ b/src/com/android/launcher3/dagger/LauncherAppModule.java
@@ -16,9 +16,12 @@
package com.android.launcher3.dagger;
-import com.android.quickstep.dagger.QuickStepModule;
-
import dagger.Module;
-@Module(includes = QuickStepModule.class)
-public class LauncherAppModule {}
+@Module(includes = {
+ WindowManagerProxyModule.class,
+ ApiWrapperModule.class,
+ PluginManagerWrapperModule.class
+})
+public class LauncherAppModule {
+}
diff --git a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
index 340fb02..5883a88 100644
--- a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
+++ b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
@@ -18,12 +18,14 @@
import android.content.Context;
-import com.android.launcher3.contextualeducation.ContextualEduStatsManager;
+import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.graphics.IconShape;
+import com.android.launcher3.graphics.ThemeManager;
import com.android.launcher3.model.ItemInstallQueue;
import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.util.ApiWrapper;
import com.android.launcher3.util.DaggerSingletonTracker;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DynamicResource;
import com.android.launcher3.util.MSDLPlayerWrapper;
import com.android.launcher3.util.PackageManagerHelper;
@@ -48,7 +50,6 @@
public interface LauncherBaseAppComponent {
DaggerSingletonTracker getDaggerSingletonTracker();
ApiWrapper getApiWrapper();
- ContextualEduStatsManager getContextualEduStatsManager();
CustomWidgetManager getCustomWidgetManager();
DynamicResource getDynamicResource();
IconShape getIconShape();
@@ -62,6 +63,9 @@
VibratorWrapper getVibratorWrapper();
MSDLPlayerWrapper getMSDLPlayerWrapper();
WindowManagerProxy getWmProxy();
+ LauncherPrefs getLauncherPrefs();
+ ThemeManager getThemeManager();
+ DisplayController getDisplayController();
/** Builder for LauncherBaseAppComponent. */
interface Builder {
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 67fe889..f1b461b 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -264,8 +264,7 @@
try (LauncherIcons li = LauncherIcons.obtain(mActivity)) {
// Since we just want the scale, avoid heavy drawing operations
Utilities.scaleRectAboutCenter(bounds, li.getNormalizer().getScale(
- new AdaptiveIconDrawable(new ColorDrawable(Color.BLACK), null),
- null, null, null));
+ new AdaptiveIconDrawable(new ColorDrawable(Color.BLACK), null)));
}
// Shrink very tiny bit so that the clip path is smaller than the original bitmap
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index e68e3c9..b76e098 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -110,7 +110,6 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
@@ -517,8 +516,6 @@
mInfo = info;
mFromTitle = info.title;
mFromLabelState = info.getFromLabelState();
- ArrayList<ItemInfo> children = info.getContents();
- Collections.sort(children, ITEM_POS_COMPARATOR);
updateItemLocationsInDatabaseBatch(true);
BaseDragLayer.LayoutParams lp = (BaseDragLayer.LayoutParams) getLayoutParams();
@@ -700,15 +697,23 @@
}
}
+ Log.d("b/383526431", "animateOpen: content child count before: "
+ + mContent.getTotalChildCount());
+
mContent.completePendingPageChanges();
mContent.setCurrentPage(pageNo);
+ Log.d("b/383526431", "animateOpen: content child count after pending page"
+ + " changes: " + mContent.getTotalChildCount());
+
// This is set to true in close(), but isn't reset to false until onDropCompleted(). This
// leads to an inconsistent state if you drag out of the folder and drag back in without
// dropping. One resulting issue is that replaceFolderWithFinalItem() can be called twice.
mDeleteFolderOnDropCompleted = false;
cancelRunningAnimations();
+ Log.d("b/383526431", "animateOpen: content child count after cancelling"
+ + " animation: " + mContent.getTotalChildCount());
FolderAnimationManager fam = new FolderAnimationManager(this, true /* isOpening */);
AnimatorSet anim = fam.getAnimator();
anim.addListener(new AnimatorListenerAdapter() {
diff --git a/src/com/android/launcher3/folder/FolderGridOrganizer.java b/src/com/android/launcher3/folder/FolderGridOrganizer.java
index a7ab7b9..06286d6 100644
--- a/src/com/android/launcher3/folder/FolderGridOrganizer.java
+++ b/src/com/android/launcher3/folder/FolderGridOrganizer.java
@@ -19,6 +19,7 @@
import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
import android.graphics.Point;
+import android.util.Log;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.model.data.FolderInfo;
@@ -178,6 +179,14 @@
break;
}
}
+
+ if (result.isEmpty()) {
+ // Log specifics since we are getting empty result
+ Log.d("b/383526431", "previewItemsForPage: "
+ + "mCountX = " + mCountX
+ + ", mCountY = " + mCountY
+ + ", content size = " + contents.size());
+ }
return result;
}
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index de1bcc3..2481a1a 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -816,6 +816,10 @@
* Returns a formatted accessibility title for folder
*/
public String getAccessiblityTitle(CharSequence title) {
+ if (title == null) {
+ // Avoids "Talkback -> Folder: null" announcement.
+ title = getContext().getString(R.string.unnamed_folder);
+ }
int size = mInfo.getContents().size();
if (size < MAX_NUM_ITEMS_IN_PREVIEW) {
return getContext().getString(R.string.folder_name_format_exact, title, size);
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index 8d751e6..bebe1a4 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -24,7 +24,6 @@
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Path;
-import android.graphics.drawable.Drawable;
import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.Log;
@@ -59,6 +58,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.ToIntFunction;
import java.util.stream.Collectors;
@@ -532,6 +532,16 @@
verifyVisibleHighResIcons(getCurrentPage() + 1);
}
+ int getTotalChildCount() {
+ AtomicInteger count = new AtomicInteger();
+ iterateOverItems((i, v) -> {
+ count.getAndIncrement();
+ return false;
+ });
+
+ return count.get();
+ }
+
/**
* Ensures that all the icons on the given page are of high-res
*/
@@ -541,19 +551,10 @@
ShortcutAndWidgetContainer parent = page.getShortcutsAndWidgets();
for (int i = parent.getChildCount() - 1; i >= 0; i--) {
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
- if (d != null) {
- d.setCallback(iconView);
}
}
}
diff --git a/src/com/android/launcher3/folder/PreviewItemManager.java b/src/com/android/launcher3/folder/PreviewItemManager.java
index 2276ac7..4cf618d 100644
--- a/src/com/android/launcher3/folder/PreviewItemManager.java
+++ b/src/com/android/launcher3/folder/PreviewItemManager.java
@@ -17,6 +17,7 @@
package com.android.launcher3.folder;
import static com.android.launcher3.BubbleTextView.DISPLAY_FOLDER;
+import static com.android.launcher3.LauncherSettings.Favorites.DESKTOP_ICON_FLAG;
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;
@@ -43,14 +44,15 @@
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.Flags;
+import com.android.launcher3.LauncherAppState;
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.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;
import com.android.launcher3.views.ActivityContext;
import java.util.ArrayList;
@@ -445,8 +447,7 @@
if (isActivePendingIcon(wii)) {
p.drawable = newPendingIcon(mContext, wii);
} else {
- p.drawable = wii.newIcon(mContext,
- Themes.isThemedIconEnabled(mContext) ? FLAG_THEMED : 0);
+ p.drawable = wii.newIcon(mContext, FLAG_THEMED);
}
p.drawable.setBounds(0, 0, mIconSize, mIconSize);
} else if (item instanceof AppPairInfo api) {
@@ -460,6 +461,18 @@
// Set the callback to FolderIcon as it is responsible to drawing the icon. The
// callback will be released when the folder is opened.
p.drawable.setCallback(mIcon);
+
+ // Verify high res
+ if (item instanceof ItemInfoWithIcon info
+ && info.getMatchingLookupFlag().isVisuallyLessThan(DESKTOP_ICON_FLAG)) {
+ LauncherAppState.getInstance(mContext).getIconCache().updateIconInBackground(
+ newInfo -> {
+ if (p.item == newInfo) {
+ setDrawable(p, newInfo);
+ mIcon.invalidate();
+ }
+ }, info);
+ }
}
/**
diff --git a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
index eaca6c5..ad176dc 100644
--- a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
+++ b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
@@ -15,10 +15,10 @@
*/
package com.android.launcher3.graphics;
-import static com.android.launcher3.LauncherPrefs.THEMED_ICONS;
+
+import static com.android.launcher3.graphics.IconShape.PREF_ICON_SHAPE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-import static com.android.launcher3.util.Themes.isThemedIconEnabled;
import android.content.ContentProvider;
import android.content.ContentValues;
@@ -44,8 +44,8 @@
import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.model.BgDataModel;
-import com.android.launcher3.shapes.AppShape;
-import com.android.launcher3.shapes.AppShapesProvider;
+import com.android.launcher3.shapes.IconShapeModel;
+import com.android.launcher3.shapes.IconShapesProvider;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.RunnableList;
@@ -53,7 +53,6 @@
import java.util.Collections;
import java.util.List;
-import java.util.Optional;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.ExecutionException;
@@ -139,19 +138,26 @@
switch (path) {
case KEY_SHAPE_OPTIONS: {
- if (Flags.newCustomizationPickerUi() && Flags.enableLauncherIconShapes()) {
+ if (Flags.newCustomizationPickerUi()) {
MatrixCursor cursor = new MatrixCursor(new String[]{
KEY_SHAPE_KEY, KEY_SHAPE_TITLE, KEY_PATH, KEY_IS_DEFAULT});
- List<AppShape> shapes = AppShapesProvider.INSTANCE.getShapes();
+ List<IconShapeModel> shapes = IconShapesProvider.INSTANCE.getShapes()
+ .values()
+ .stream()
+ .toList();
+ String currentPath = LauncherPrefs.get(context).get(PREF_ICON_SHAPE);
+ IconShapeModel currentShape = shapes.stream()
+ .filter(shape -> currentPath.equals(shape.getPathString()))
+ .findFirst()
+ .orElse(IconShapesProvider.INSTANCE.getShapes().get("circle"));
+
for (int i = 0; i < shapes.size(); i++) {
- AppShape shape = shapes.get(i);
+ IconShapeModel shape = shapes.get(i);
cursor.newRow()
.add(KEY_SHAPE_KEY, shape.getKey())
.add(KEY_SHAPE_TITLE, shape.getTitle())
- .add(KEY_PATH, shape.getPath())
- // TODO (b/348664593): We should fetch the currently-set shape
- // option from the preferences.
- .add(KEY_IS_DEFAULT, i == 0);
+ .add(KEY_PATH, shape.getPathString())
+ .add(KEY_IS_DEFAULT, shape.equals(currentShape));
}
return cursor;
} else {
@@ -178,7 +184,8 @@
case GET_ICON_THEMED:
case ICON_THEMED: {
MatrixCursor cursor = new MatrixCursor(new String[]{BOOLEAN_VALUE});
- cursor.newRow().add(BOOLEAN_VALUE, isThemedIconEnabled(getContext()) ? 1 : 0);
+ cursor.newRow().add(BOOLEAN_VALUE,
+ ThemeManager.INSTANCE.get(getContext()).isMonoThemeEnabled() ? 1 : 0);
return cursor;
}
default:
@@ -212,11 +219,8 @@
case KEY_DEFAULT_GRID: {
if (Flags.newCustomizationPickerUi()) {
String shapeKey = values.getAsString(KEY_SHAPE_KEY);
- Optional<AppShape> optionalShape = AppShapesProvider.INSTANCE.getShapes()
- .stream().filter(shape -> shape.getKey().equals(shapeKey)).findFirst();
- String pathToSet = optionalShape.map(AppShape::getPath).orElse(null);
- // TODO (b/348664593): Apply shapeName to the system. This needs to be a
- // synchronous call.
+ IconShapeModel shape = IconShapesProvider.INSTANCE.getShapes().get(shapeKey);
+ IconShape.INSTANCE.get(context).setShapeOverride(shape);
}
String gridName = values.getAsString(KEY_NAME);
InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(context);
@@ -247,8 +251,8 @@
}
case ICON_THEMED:
case SET_ICON_THEMED: {
- LauncherPrefs.get(context)
- .put(THEMED_ICONS, values.getAsBoolean(BOOLEAN_VALUE));
+ ThemeManager.INSTANCE.get(context)
+ .setMonoThemeEnabled(values.getAsBoolean(BOOLEAN_VALUE));
context.getContentResolver().notifyChange(uri, null);
return 1;
}
@@ -355,14 +359,12 @@
renderer.hideBottomRow(message.getData().getBoolean(KEY_HIDE_BOTTOM_ROW));
break;
case MESSAGE_ID_UPDATE_SHAPE:
- if (Flags.newCustomizationPickerUi() && Flags.enableLauncherIconShapes()) {
+ if (Flags.newCustomizationPickerUi()
+ && com.android.launcher3.Flags.enableLauncherIconShapes()) {
String shapeKey = message.getData().getString(KEY_SHAPE_KEY);
- Optional<AppShape> optionalShape = AppShapesProvider.INSTANCE.getShapes()
- .stream()
- .filter(shape -> shape.getKey().equals(shapeKey))
- .findFirst();
- String pathToSet = optionalShape.map(AppShape::getPath).orElse(null);
- // TODO (b/348664593): Update launcher preview with the given shape
+ IconShapeModel shape =
+ IconShapesProvider.INSTANCE.getShapes().get(shapeKey);
+ renderer.updateShape(shape);
}
break;
case MESSAGE_ID_UPDATE_GRID:
diff --git a/src/com/android/launcher3/graphics/IconShape.java b/src/com/android/launcher3/graphics/IconShape.java
deleted file mode 100644
index cb14587..0000000
--- a/src/com/android/launcher3/graphics/IconShape.java
+++ /dev/null
@@ -1,482 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.graphics;
-
-import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.FloatArrayEvaluator;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.graphics.Region.Op;
-import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.ColorDrawable;
-import android.util.AttributeSet;
-import android.util.Xml;
-import android.view.View;
-import android.view.ViewOutlineProvider;
-
-import com.android.launcher3.R;
-import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
-import com.android.launcher3.dagger.ApplicationContext;
-import com.android.launcher3.dagger.LauncherAppSingleton;
-import com.android.launcher3.dagger.LauncherBaseAppComponent;
-import com.android.launcher3.icons.GraphicsUtils;
-import com.android.launcher3.icons.IconNormalizer;
-import com.android.launcher3.util.DaggerSingletonObject;
-import com.android.launcher3.views.ClipPathView;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.inject.Inject;
-
-/**
- * Abstract representation of the shape of an icon shape
- */
-@LauncherAppSingleton
-public final class IconShape {
-
- public static DaggerSingletonObject<IconShape> INSTANCE =
- new DaggerSingletonObject<>(LauncherBaseAppComponent::getIconShape);
-
- private ShapeDelegate mDelegate = new Circle();
- private float mNormalizationScale = ICON_VISIBLE_AREA_FACTOR;
-
- @Inject
- public IconShape(@ApplicationContext Context context) {
- pickBestShape(context);
- }
-
- public ShapeDelegate getShape() {
- return mDelegate;
- }
-
- public float getNormalizationScale() {
- return mNormalizationScale;
- }
-
- /**
- * Initializes the shape which is closest to the {@link AdaptiveIconDrawable}
- */
- public void pickBestShape(Context context) {
- // Pick any large size
- final int size = 200;
-
- Region full = new Region(0, 0, size, size);
- Region iconR = new Region();
- AdaptiveIconDrawable drawable = new AdaptiveIconDrawable(
- new ColorDrawable(Color.BLACK), new ColorDrawable(Color.BLACK));
- drawable.setBounds(0, 0, size, size);
- iconR.setPath(drawable.getIconMask(), full);
-
- Path shapePath = new Path();
- Region shapeR = new Region();
-
- // Find the shape with minimum area of divergent region.
- int minArea = Integer.MAX_VALUE;
- ShapeDelegate closestShape = null;
- for (ShapeDelegate shape : getAllShapes(context)) {
- shapePath.reset();
- shape.addToPath(shapePath, 0, 0, size / 2f);
- shapeR.setPath(shapePath, full);
- shapeR.op(iconR, Op.XOR);
-
- int area = GraphicsUtils.getArea(shapeR);
- if (area < minArea) {
- minArea = area;
- closestShape = shape;
- }
- }
-
- if (closestShape != null) {
- mDelegate = closestShape;
- }
-
- // Initialize shape properties
- mNormalizationScale = IconNormalizer.normalizeAdaptiveIcon(drawable, size, null);
- }
-
-
-
- public interface ShapeDelegate {
-
- default boolean enableShapeDetection() {
- return false;
- }
-
- void drawShape(Canvas canvas, float offsetX, float offsetY, float radius, Paint paint);
-
- void addToPath(Path path, float offsetX, float offsetY, float radius);
-
- <T extends View & ClipPathView> ValueAnimator createRevealAnimator(T target,
- Rect startRect, Rect endRect, float endRadius, boolean isReversed);
- }
-
- /**
- * Abstract shape where the reveal animation is a derivative of a round rect animation
- */
- private static abstract class SimpleRectShape implements ShapeDelegate {
-
- @Override
- public final <T extends View & ClipPathView> ValueAnimator createRevealAnimator(T target,
- Rect startRect, Rect endRect, float endRadius, boolean isReversed) {
- return new RoundedRectRevealOutlineProvider(
- getStartRadius(startRect), endRadius, startRect, endRect) {
- @Override
- public boolean shouldRemoveElevationDuringAnimation() {
- return true;
- }
- }.createRevealAnimator(target, isReversed);
- }
-
- protected abstract float getStartRadius(Rect startRect);
- }
-
- /**
- * Abstract shape which draws using {@link Path}
- */
- private static abstract class PathShape implements ShapeDelegate {
-
- private final Path mTmpPath = new Path();
-
- @Override
- public final void drawShape(Canvas canvas, float offsetX, float offsetY, float radius,
- Paint paint) {
- mTmpPath.reset();
- addToPath(mTmpPath, offsetX, offsetY, radius);
- canvas.drawPath(mTmpPath, paint);
- }
-
- protected abstract AnimatorUpdateListener newUpdateListener(
- Rect startRect, Rect endRect, float endRadius, Path outPath);
-
- @Override
- public final <T extends View & ClipPathView> ValueAnimator createRevealAnimator(T target,
- Rect startRect, Rect endRect, float endRadius, boolean isReversed) {
- Path path = new Path();
- AnimatorUpdateListener listener =
- newUpdateListener(startRect, endRect, endRadius, path);
-
- ValueAnimator va =
- isReversed ? ValueAnimator.ofFloat(1f, 0f) : ValueAnimator.ofFloat(0f, 1f);
- va.addListener(new AnimatorListenerAdapter() {
- private ViewOutlineProvider mOldOutlineProvider;
-
- public void onAnimationStart(Animator animation) {
- mOldOutlineProvider = target.getOutlineProvider();
- target.setOutlineProvider(null);
-
- target.setTranslationZ(-target.getElevation());
- }
-
- public void onAnimationEnd(Animator animation) {
- target.setTranslationZ(0);
- target.setClipPath(null);
- target.setOutlineProvider(mOldOutlineProvider);
- }
- });
-
- va.addUpdateListener((anim) -> {
- path.reset();
- listener.onAnimationUpdate(anim);
- target.setClipPath(path);
- });
-
- return va;
- }
- }
-
- public static final class Circle extends PathShape {
-
- private final float[] mTempRadii = new float[8];
-
- protected AnimatorUpdateListener newUpdateListener(Rect startRect, Rect endRect,
- float endRadius, Path outPath) {
- float r1 = getStartRadius(startRect);
-
- float[] startValues = new float[] {
- startRect.left, startRect.top, startRect.right, startRect.bottom, r1, r1};
- float[] endValues = new float[] {
- endRect.left, endRect.top, endRect.right, endRect.bottom, endRadius, endRadius};
-
- FloatArrayEvaluator evaluator = new FloatArrayEvaluator(new float[6]);
-
- return (anim) -> {
- float progress = (Float) anim.getAnimatedValue();
- float[] values = evaluator.evaluate(progress, startValues, endValues);
- outPath.addRoundRect(
- values[0], values[1], values[2], values[3],
- getRadiiArray(values[4], values[5]), Path.Direction.CW);
- };
- }
-
- private float[] getRadiiArray(float r1, float r2) {
- mTempRadii[0] = mTempRadii [1] = mTempRadii[2] = mTempRadii[3] =
- mTempRadii[6] = mTempRadii[7] = r1;
- mTempRadii[4] = mTempRadii[5] = r2;
- return mTempRadii;
- }
-
-
- @Override
- public void addToPath(Path path, float offsetX, float offsetY, float radius) {
- path.addCircle(radius + offsetX, radius + offsetY, radius, Path.Direction.CW);
- }
-
- protected float getStartRadius(Rect startRect) {
- return startRect.width() / 2f;
- }
-
- @Override
- public boolean enableShapeDetection() {
- return true;
- }
- }
-
- private static class RoundedSquare extends SimpleRectShape {
-
- /**
- * Ratio of corner radius to half size.
- */
- private final float mRadiusRatio;
-
- public RoundedSquare(float radiusRatio) {
- mRadiusRatio = radiusRatio;
- }
-
- @Override
- public void drawShape(Canvas canvas, float offsetX, float offsetY, float radius, Paint p) {
- float cx = radius + offsetX;
- float cy = radius + offsetY;
- float cr = radius * mRadiusRatio;
- canvas.drawRoundRect(cx - radius, cy - radius, cx + radius, cy + radius, cr, cr, p);
- }
-
- @Override
- public void addToPath(Path path, float offsetX, float offsetY, float radius) {
- float cx = radius + offsetX;
- float cy = radius + offsetY;
- float cr = radius * mRadiusRatio;
- path.addRoundRect(cx - radius, cy - radius, cx + radius, cy + radius, cr, cr,
- Path.Direction.CW);
- }
-
- @Override
- protected float getStartRadius(Rect startRect) {
- return (startRect.width() / 2f) * mRadiusRatio;
- }
- }
-
- private static class TearDrop extends PathShape {
-
- /**
- * Radio of short radius to large radius, based on the shape options defined in the config.
- */
- private final float mRadiusRatio;
- private final float[] mTempRadii = new float[8];
-
- public TearDrop(float radiusRatio) {
- mRadiusRatio = radiusRatio;
- }
-
- @Override
- public void addToPath(Path p, float offsetX, float offsetY, float r1) {
- float r2 = r1 * mRadiusRatio;
- float cx = r1 + offsetX;
- float cy = r1 + offsetY;
-
- p.addRoundRect(cx - r1, cy - r1, cx + r1, cy + r1, getRadiiArray(r1, r2),
- Path.Direction.CW);
- }
-
- private float[] getRadiiArray(float r1, float r2) {
- mTempRadii[0] = mTempRadii [1] = mTempRadii[2] = mTempRadii[3] =
- mTempRadii[6] = mTempRadii[7] = r1;
- mTempRadii[4] = mTempRadii[5] = r2;
- return mTempRadii;
- }
-
- @Override
- protected AnimatorUpdateListener newUpdateListener(Rect startRect, Rect endRect,
- float endRadius, Path outPath) {
- float r1 = startRect.width() / 2f;
- float r2 = r1 * mRadiusRatio;
-
- float[] startValues = new float[] {
- startRect.left, startRect.top, startRect.right, startRect.bottom, r1, r2};
- float[] endValues = new float[] {
- endRect.left, endRect.top, endRect.right, endRect.bottom, endRadius, endRadius};
-
- FloatArrayEvaluator evaluator = new FloatArrayEvaluator(new float[6]);
-
- return (anim) -> {
- float progress = (Float) anim.getAnimatedValue();
- float[] values = evaluator.evaluate(progress, startValues, endValues);
- outPath.addRoundRect(
- values[0], values[1], values[2], values[3],
- getRadiiArray(values[4], values[5]), Path.Direction.CW);
- };
- }
- }
-
- private static class Squircle extends PathShape {
-
- /**
- * Radio of radius to circle radius, based on the shape options defined in the config.
- */
- private final float mRadiusRatio;
-
- public Squircle(float radiusRatio) {
- mRadiusRatio = radiusRatio;
- }
-
- @Override
- public void addToPath(Path p, float offsetX, float offsetY, float r) {
- float cx = r + offsetX;
- float cy = r + offsetY;
- float control = r - r * mRadiusRatio;
-
- p.moveTo(cx, cy - r);
- addLeftCurve(cx, cy, r, control, p);
- addRightCurve(cx, cy, r, control, p);
- addLeftCurve(cx, cy, -r, -control, p);
- addRightCurve(cx, cy, -r, -control, p);
- p.close();
- }
-
- private void addLeftCurve(float cx, float cy, float r, float control, Path path) {
- path.cubicTo(
- cx - control, cy - r,
- cx - r, cy - control,
- cx - r, cy);
- }
-
- private void addRightCurve(float cx, float cy, float r, float control, Path path) {
- path.cubicTo(
- cx - r, cy + control,
- cx - control, cy + r,
- cx, cy + r);
- }
-
- @Override
- protected AnimatorUpdateListener newUpdateListener(Rect startRect, Rect endRect,
- float endR, Path outPath) {
-
- float startCX = startRect.exactCenterX();
- float startCY = startRect.exactCenterY();
- float startR = startRect.width() / 2f;
- float startControl = startR - startR * mRadiusRatio;
- float startHShift = 0;
- float startVShift = 0;
-
- float endCX = endRect.exactCenterX();
- float endCY = endRect.exactCenterY();
- // Approximate corner circle using bezier curves
- // http://spencermortensen.com/articles/bezier-circle/
- float endControl = endR * 0.551915024494f;
- float endHShift = endRect.width() / 2f - endR;
- float endVShift = endRect.height() / 2f - endR;
-
- return (anim) -> {
- float progress = (Float) anim.getAnimatedValue();
-
- float cx = (1 - progress) * startCX + progress * endCX;
- float cy = (1 - progress) * startCY + progress * endCY;
- float r = (1 - progress) * startR + progress * endR;
- float control = (1 - progress) * startControl + progress * endControl;
- float hShift = (1 - progress) * startHShift + progress * endHShift;
- float vShift = (1 - progress) * startVShift + progress * endVShift;
-
- outPath.moveTo(cx, cy - vShift - r);
- outPath.rLineTo(-hShift, 0);
-
- addLeftCurve(cx - hShift, cy - vShift, r, control, outPath);
- outPath.rLineTo(0, vShift + vShift);
-
- addRightCurve(cx - hShift, cy + vShift, r, control, outPath);
- outPath.rLineTo(hShift + hShift, 0);
-
- addLeftCurve(cx + hShift, cy + vShift, -r, -control, outPath);
- outPath.rLineTo(0, -vShift - vShift);
-
- addRightCurve(cx + hShift, cy - vShift, -r, -control, outPath);
- outPath.close();
- };
- }
- }
-
- private static ShapeDelegate getShapeDefinition(String type, float radius) {
- switch (type) {
- case "Circle":
- return new Circle();
- case "RoundedSquare":
- return new RoundedSquare(radius);
- case "TearDrop":
- return new TearDrop(radius);
- case "Squircle":
- return new Squircle(radius);
- default:
- throw new IllegalArgumentException("Invalid shape type: " + type);
- }
- }
-
- private static List<ShapeDelegate> getAllShapes(Context context) {
- ArrayList<ShapeDelegate> result = new ArrayList<>();
- try (XmlResourceParser parser = context.getResources().getXml(R.xml.folder_shapes)) {
-
- // Find the root tag
- int type;
- while ((type = parser.next()) != XmlPullParser.END_TAG
- && type != XmlPullParser.END_DOCUMENT
- && !"shapes".equals(parser.getName()));
-
- final int depth = parser.getDepth();
- int[] radiusAttr = new int[] {R.attr.folderIconRadius};
-
- while (((type = parser.next()) != XmlPullParser.END_TAG ||
- parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
-
- if (type == XmlPullParser.START_TAG) {
- AttributeSet attrs = Xml.asAttributeSet(parser);
- TypedArray a = context.obtainStyledAttributes(attrs, radiusAttr);
- ShapeDelegate shape = getShapeDefinition(parser.getName(), a.getFloat(0, 1));
- a.recycle();
-
- result.add(shape);
- }
- }
- } catch (IOException | XmlPullParserException e) {
- throw new RuntimeException(e);
- }
- return result;
- }
-
-}
diff --git a/src/com/android/launcher3/graphics/IconShape.kt b/src/com/android/launcher3/graphics/IconShape.kt
new file mode 100644
index 0000000..2d2ee30
--- /dev/null
+++ b/src/com/android/launcher3/graphics/IconShape.kt
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.graphics
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.ValueAnimator
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Matrix
+import android.graphics.Matrix.ScaleToFit.FILL
+import android.graphics.Paint
+import android.graphics.Path
+import android.graphics.Rect
+import android.graphics.RectF
+import android.graphics.Region
+import android.graphics.drawable.AdaptiveIconDrawable
+import android.graphics.drawable.ColorDrawable
+import android.util.Log
+import android.view.View
+import android.view.ViewOutlineProvider
+import androidx.annotation.VisibleForTesting
+import androidx.graphics.shapes.CornerRounding
+import androidx.graphics.shapes.Morph
+import androidx.graphics.shapes.RoundedPolygon
+import androidx.graphics.shapes.SvgPathParser
+import androidx.graphics.shapes.toPath
+import androidx.graphics.shapes.transformed
+import com.android.launcher3.EncryptionType
+import com.android.launcher3.LauncherPrefs
+import com.android.launcher3.LauncherPrefs.Companion.backedUpItem
+import com.android.launcher3.anim.RoundedRectRevealOutlineProvider
+import com.android.launcher3.dagger.ApplicationContext
+import com.android.launcher3.dagger.LauncherAppComponent
+import com.android.launcher3.dagger.LauncherAppSingleton
+import com.android.launcher3.graphics.LauncherPreviewRenderer.PreviewContext
+import com.android.launcher3.graphics.ThemeManager.ThemeChangeListener
+import com.android.launcher3.icons.GraphicsUtils
+import com.android.launcher3.icons.IconNormalizer
+import com.android.launcher3.shapes.IconShapeModel
+import com.android.launcher3.shapes.IconShapesProvider
+import com.android.launcher3.util.DaggerSingletonObject
+import com.android.launcher3.util.DaggerSingletonTracker
+import com.android.launcher3.views.ClipPathView
+import javax.inject.Inject
+
+/** Abstract representation of the shape of an icon shape */
+@LauncherAppSingleton
+class IconShape
+@Inject
+constructor(
+ @ApplicationContext private val context: Context,
+ private val prefs: LauncherPrefs,
+ themeManager: ThemeManager,
+ lifeCycle: DaggerSingletonTracker,
+) {
+
+ var shapeOverride: IconShapeModel? = getShapeFromPathString(prefs.get(PREF_ICON_SHAPE))
+ set(value) {
+ field = value
+ if (context !is PreviewContext) {
+ value?.let { prefs.put(PREF_ICON_SHAPE, value.pathString) }
+ }
+ }
+
+ var normalizationScale: Float = IconNormalizer.ICON_VISIBLE_AREA_FACTOR
+ private set
+
+ var shape: ShapeDelegate = pickBestShape(themeManager)
+ private set
+
+ init {
+ val changeListener = ThemeChangeListener { shape = pickBestShape(themeManager) }
+ themeManager.addChangeListener(changeListener)
+ lifeCycle.addCloseable { themeManager.removeChangeListener(changeListener) }
+ }
+
+ /** Initializes the shape which is closest to the [AdaptiveIconDrawable] */
+ private fun pickBestShape(themeManager: ThemeManager): ShapeDelegate {
+ val drawable =
+ AdaptiveIconDrawable(null, ColorDrawable(Color.BLACK)).apply {
+ setBounds(0, 0, AREA_CALC_SIZE, AREA_CALC_SIZE)
+ }
+
+ normalizationScale = IconNormalizer.normalizeAdaptiveIcon(drawable, AREA_CALC_SIZE)
+ return pickBestShape(drawable.iconMask, themeManager.iconState.iconMask)
+ }
+
+ interface ShapeDelegate {
+ fun drawShape(canvas: Canvas, offsetX: Float, offsetY: Float, radius: Float, paint: Paint)
+
+ fun addToPath(path: Path, offsetX: Float, offsetY: Float, radius: Float)
+
+ fun <T> createRevealAnimator(
+ target: T,
+ startRect: Rect,
+ endRect: Rect,
+ endRadius: Float,
+ isReversed: Boolean,
+ ): ValueAnimator where T : View, T : ClipPathView
+ }
+
+ @VisibleForTesting
+ class Circle : RoundedSquare(1f) {
+
+ override fun drawShape(
+ canvas: Canvas,
+ offsetX: Float,
+ offsetY: Float,
+ radius: Float,
+ paint: Paint,
+ ) = canvas.drawCircle(radius + offsetX, radius + offsetY, radius, paint)
+
+ override fun addToPath(path: Path, offsetX: Float, offsetY: Float, radius: Float) =
+ path.addCircle(radius + offsetX, radius + offsetY, radius, Path.Direction.CW)
+ }
+
+ /** Rounded square with [radiusRatio] as a ratio of its half edge size */
+ @VisibleForTesting
+ open class RoundedSquare(val radiusRatio: Float) : ShapeDelegate {
+
+ override fun drawShape(
+ canvas: Canvas,
+ offsetX: Float,
+ offsetY: Float,
+ radius: Float,
+ paint: Paint,
+ ) {
+ val cx = radius + offsetX
+ val cy = radius + offsetY
+ val cr = radius * radiusRatio
+ canvas.drawRoundRect(cx - radius, cy - radius, cx + radius, cy + radius, cr, cr, paint)
+ }
+
+ override fun addToPath(path: Path, offsetX: Float, offsetY: Float, radius: Float) {
+ val cx = radius + offsetX
+ val cy = radius + offsetY
+ val cr = radius * radiusRatio
+ path.addRoundRect(
+ cx - radius,
+ cy - radius,
+ cx + radius,
+ cy + radius,
+ cr,
+ cr,
+ Path.Direction.CW,
+ )
+ }
+
+ override fun <T> createRevealAnimator(
+ target: T,
+ startRect: Rect,
+ endRect: Rect,
+ endRadius: Float,
+ isReversed: Boolean,
+ ): ValueAnimator where T : View, T : ClipPathView {
+ return object :
+ RoundedRectRevealOutlineProvider(
+ (startRect.width() / 2f) * radiusRatio,
+ endRadius,
+ startRect,
+ endRect,
+ ) {
+ override fun shouldRemoveElevationDuringAnimation() = true
+ }
+ .createRevealAnimator(target, isReversed)
+ }
+ }
+
+ /** Generic shape delegate with pathString in bounds [0, 0, 100, 100] */
+ class GenericPathShape(pathString: String) : ShapeDelegate {
+ private val poly =
+ RoundedPolygon(
+ features = SvgPathParser.parseFeatures(pathString),
+ centerX = 50f,
+ centerY = 50f,
+ )
+ // This ensures that a valid morph is possible from the provided path
+ private val basePath =
+ Path().apply {
+ Morph(poly, createRoundedRect(0f, 0f, 100f, 100f, 25f)).toPath(0f, this)
+ }
+ private val tmpPath = Path()
+ private val tmpMatrix = Matrix()
+
+ override fun drawShape(
+ canvas: Canvas,
+ offsetX: Float,
+ offsetY: Float,
+ radius: Float,
+ paint: Paint,
+ ) {
+ tmpPath.reset()
+ addToPath(tmpPath, offsetX, offsetY, radius)
+ canvas.drawPath(tmpPath, paint)
+ }
+
+ override fun addToPath(path: Path, offsetX: Float, offsetY: Float, radius: Float) {
+ tmpMatrix.setScale(radius / 50, radius / 50)
+ tmpMatrix.postTranslate(offsetX, offsetY)
+ basePath.transform(tmpMatrix, path)
+ }
+
+ override fun <T> createRevealAnimator(
+ target: T,
+ startRect: Rect,
+ endRect: Rect,
+ endRadius: Float,
+ isReversed: Boolean,
+ ): ValueAnimator where T : View, T : ClipPathView {
+ // End poly is defined as a rectangle starting at top/center so that the
+ // transformation has minimum motion
+ val morph =
+ Morph(
+ start =
+ poly.transformed(
+ Matrix().apply {
+ setRectToRect(RectF(0f, 0f, 100f, 100f), RectF(startRect), FILL)
+ }
+ ),
+ end =
+ createRoundedRect(
+ left = endRect.left.toFloat(),
+ top = endRect.top.toFloat(),
+ right = endRect.right.toFloat(),
+ bottom = endRect.bottom.toFloat(),
+ cornerR = endRadius,
+ ),
+ )
+
+ val va =
+ if (isReversed) ValueAnimator.ofFloat(1f, 0f) else ValueAnimator.ofFloat(0f, 1f)
+ va.addListener(
+ object : AnimatorListenerAdapter() {
+ private var oldOutlineProvider: ViewOutlineProvider? = null
+
+ override fun onAnimationStart(animation: Animator) {
+ target.apply {
+ oldOutlineProvider = outlineProvider
+ outlineProvider = null
+ translationZ = -target.elevation
+ }
+ }
+
+ override fun onAnimationEnd(animation: Animator) {
+ target.apply {
+ translationZ = 0f
+ setClipPath(null)
+ outlineProvider = oldOutlineProvider
+ }
+ }
+ }
+ )
+
+ val path = Path()
+ va.addUpdateListener { anim: ValueAnimator ->
+ path.reset()
+ morph.toPath(anim.animatedValue as Float, path)
+ target.setClipPath(path)
+ }
+ return va
+ }
+ }
+
+ companion object {
+ @JvmField var INSTANCE = DaggerSingletonObject(LauncherAppComponent::getIconShape)
+
+ const val TAG = "IconShape"
+ const val KEY_ICON_SHAPE = "icon_shape"
+ const val AREA_CALC_SIZE = 1000
+ // .1% error margin
+ const val AREA_DIFF_THRESHOLD = AREA_CALC_SIZE * AREA_CALC_SIZE / 1000
+
+ @JvmField val PREF_ICON_SHAPE = backedUpItem(KEY_ICON_SHAPE, "", EncryptionType.ENCRYPTED)
+
+ private fun getShapeFromPathString(pathString: String): IconShapeModel? {
+ return IconShapesProvider.shapes.values.firstOrNull { shape: IconShapeModel ->
+ shape.pathString == pathString
+ }
+ }
+
+ /** Returns a function to calculate area diff from [base] */
+ @VisibleForTesting
+ fun areaDiffCalculator(base: Path): (ShapeDelegate) -> Int {
+ val fullRegion = Region(0, 0, AREA_CALC_SIZE, AREA_CALC_SIZE)
+ val iconRegion = Region().apply { setPath(base, fullRegion) }
+
+ val shapePath = Path()
+ val shapeRegion = Region()
+ return fun(shape: ShapeDelegate): Int {
+ shapePath.reset()
+ shape.addToPath(shapePath, 0f, 0f, AREA_CALC_SIZE / 2f)
+ shapeRegion.setPath(shapePath, fullRegion)
+ shapeRegion.op(iconRegion, Region.Op.XOR)
+ return GraphicsUtils.getArea(shapeRegion)
+ }
+ }
+
+ @VisibleForTesting
+ fun pickBestShape(baseShape: Path, shapeStr: String): ShapeDelegate {
+ val calcAreaDiff = areaDiffCalculator(baseShape)
+
+ // Find the shape with minimum area of divergent region.
+ var closestShape: ShapeDelegate = Circle()
+ var minAreaDiff = calcAreaDiff(closestShape)
+
+ // Try some common rounded rect edges
+ for (f in 0..20) {
+ val rectShape = RoundedSquare(f.toFloat() / 20)
+ val rectArea = calcAreaDiff(rectShape)
+ if (rectArea < minAreaDiff) {
+ minAreaDiff = rectArea
+ closestShape = rectShape
+ }
+ }
+
+ // Use the generic shape only if we have more than .1% error
+ if (shapeStr.isNotEmpty() && minAreaDiff > AREA_DIFF_THRESHOLD) {
+ try {
+ val generic = GenericPathShape(shapeStr)
+ closestShape = generic
+ } catch (e: Exception) {
+ Log.e(TAG, "Error converting mask to generic shape", e)
+ }
+ }
+ return closestShape
+ }
+
+ /**
+ * Creates a rounded rect with the start point at the center of the top edge. This ensures a
+ * better animation since our shape paths also start at top-center of the bounding box.
+ */
+ fun createRoundedRect(
+ left: Float,
+ top: Float,
+ right: Float,
+ bottom: Float,
+ cornerR: Float,
+ ) =
+ RoundedPolygon(
+ vertices =
+ floatArrayOf(
+ // x1, y1
+ (left + right) / 2,
+ top,
+ // x2, y2
+ right,
+ top,
+ // x3, y3
+ right,
+ bottom,
+ // x4, y4
+ left,
+ bottom,
+ // x5, y5
+ left,
+ top,
+ ),
+ centerX = (left + right) / 2,
+ centerY = (top + bottom) / 2,
+ rounding = CornerRounding(cornerR),
+ )
+ }
+}
diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
index 3000b25..6afac71 100644
--- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
+++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
@@ -62,6 +62,7 @@
import com.android.launcher3.model.LoaderTask;
import com.android.launcher3.model.ModelDbController;
import com.android.launcher3.provider.LauncherDbUtils;
+import com.android.launcher3.shapes.IconShapeModel;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.Themes;
@@ -77,9 +78,7 @@
public class PreviewSurfaceRenderer {
private static final String TAG = "PreviewSurfaceRenderer";
-
private static final int FADE_IN_ANIMATION_DURATION = 200;
-
private static final String KEY_HOST_TOKEN = "host_token";
private static final String KEY_VIEW_WIDTH = "width";
private static final String KEY_VIEW_HEIGHT = "height";
@@ -90,25 +89,25 @@
private static final String KEY_DARK_MODE = "use_dark_mode";
private Context mContext;
- private final IBinder mHostToken;
- private final int mWidth;
- private final int mHeight;
- private String mGridName;
-
- private final int mDisplayId;
- private final Display mDisplay;
- private final WallpaperColors mWallpaperColors;
private SparseIntArray mPreviewColorOverride;
+ private String mGridName;
+ private IconShapeModel mShape;
@Nullable private Boolean mDarkMode;
- private final RunnableList mLifeCycleTracker;
-
- private final SurfaceControlViewHost mSurfaceControlViewHost;
-
private boolean mDestroyed = false;
private LauncherPreviewRenderer mRenderer;
private boolean mHideQsb;
@Nullable private FrameLayout mViewRoot = null;
+ private final IBinder mHostToken;
+ private final int mWidth;
+ private final int mHeight;
+ private final int mDisplayId;
+ private final Display mDisplay;
+ private final WallpaperColors mWallpaperColors;
+ private final RunnableList mLifeCycleTracker;
+ private final SurfaceControlViewHost mSurfaceControlViewHost;
+
+
public PreviewSurfaceRenderer(
Context context, RunnableList lifecycleTracker, Bundle bundle) throws Exception {
mContext = context;
@@ -135,10 +134,10 @@
}
mSurfaceControlViewHost = MAIN_EXECUTOR.submit(() -> new MySurfaceControlViewHost(
- mContext,
- context.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY),
- mHostToken,
- mLifeCycleTracker))
+ mContext,
+ context.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY),
+ mHostToken,
+ mLifeCycleTracker))
.get(5, TimeUnit.SECONDS);
mLifeCycleTracker.add(this::destroy);
}
@@ -219,6 +218,20 @@
}
/**
+ * Update the shapes of the launcher preview
+ *
+ * @param shape path for shapes
+ */
+ public void updateShape(@NonNull IconShapeModel shape) {
+ if (shape.equals(mShape)) {
+ Log.w(TAG, "Preview shape already set, skipping. shape=" + shape);
+ return;
+ }
+ mShape = shape;
+ loadAsync();
+ }
+
+ /**
* Hides the components in the bottom row.
*
* @param hide True to hide and false to show.
@@ -303,9 +316,13 @@
private void loadModelData() {
final Context inflationContext = getPreviewContext();
final InvariantDeviceProfile idp = new InvariantDeviceProfile(inflationContext, mGridName);
- if (GridSizeMigrationDBController.needsToMigrate(inflationContext, idp)) {
+ if (GridSizeMigrationDBController.needsToMigrate(inflationContext, idp)
+ || mShape != null) {
// Start the migration
PreviewContext previewContext = new PreviewContext(inflationContext, idp);
+ if (mShape != null) {
+ IconShape.INSTANCE.get(previewContext).setShapeOverride(mShape);
+ }
// Copy existing data to preview DB
LauncherDbUtils.copyTable(LauncherAppState.getInstance(mContext)
.getModel().getModelDbController().getDb(),
diff --git a/src/com/android/launcher3/graphics/ThemeManager.kt b/src/com/android/launcher3/graphics/ThemeManager.kt
new file mode 100644
index 0000000..989471f
--- /dev/null
+++ b/src/com/android/launcher3/graphics/ThemeManager.kt
@@ -0,0 +1,132 @@
+/*
+ * 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.graphics
+
+import android.content.Context
+import android.content.res.Resources
+import com.android.launcher3.EncryptionType
+import com.android.launcher3.LauncherPrefChangeListener
+import com.android.launcher3.LauncherPrefs
+import com.android.launcher3.LauncherPrefs.Companion.backedUpItem
+import com.android.launcher3.dagger.ApplicationContext
+import com.android.launcher3.dagger.LauncherAppComponent
+import com.android.launcher3.dagger.LauncherAppSingleton
+import com.android.launcher3.graphics.IconShape.Companion.KEY_ICON_SHAPE
+import com.android.launcher3.graphics.IconShape.Companion.PREF_ICON_SHAPE
+import com.android.launcher3.icons.IconThemeController
+import com.android.launcher3.icons.mono.MonoIconThemeController
+import com.android.launcher3.util.DaggerSingletonObject
+import com.android.launcher3.util.DaggerSingletonTracker
+import com.android.launcher3.util.Executors.MAIN_EXECUTOR
+import com.android.launcher3.util.SimpleBroadcastReceiver
+import java.util.concurrent.CopyOnWriteArrayList
+import javax.inject.Inject
+
+/** Centralized class for managing Launcher icon theming */
+@LauncherAppSingleton
+open class ThemeManager
+@Inject
+constructor(
+ @ApplicationContext private val context: Context,
+ private val prefs: LauncherPrefs,
+ lifecycle: DaggerSingletonTracker,
+) {
+
+ /** Representation of the current icon state */
+ var iconState = parseIconState()
+ private set
+
+ var isMonoThemeEnabled
+ set(value) = prefs.put(THEMED_ICONS, value)
+ get() = prefs.get(THEMED_ICONS)
+
+ var themeController: IconThemeController? =
+ if (isMonoThemeEnabled) MonoIconThemeController() else null
+ private set
+
+ private val listeners = CopyOnWriteArrayList<ThemeChangeListener>()
+
+ init {
+ val receiver = SimpleBroadcastReceiver(MAIN_EXECUTOR) { verifyIconState() }
+ receiver.registerPkgActions(context, "android", ACTION_OVERLAY_CHANGED)
+
+ val prefListener = LauncherPrefChangeListener { key ->
+ when (key) {
+ KEY_THEMED_ICONS,
+ KEY_ICON_SHAPE -> verifyIconState()
+ }
+ }
+ prefs.addListener(prefListener, THEMED_ICONS, PREF_ICON_SHAPE)
+
+ lifecycle.addCloseable {
+ receiver.unregisterReceiverSafely(context)
+ prefs.removeListener(prefListener)
+ }
+ }
+
+ private fun verifyIconState() {
+ val newState = parseIconState()
+ if (newState == iconState) return
+
+ iconState = newState
+ themeController = if (isMonoThemeEnabled) MonoIconThemeController() else null
+
+ listeners.forEach { it.onThemeChanged() }
+ }
+
+ fun addChangeListener(listener: ThemeChangeListener) = listeners.add(listener)
+
+ fun removeChangeListener(listener: ThemeChangeListener) = listeners.remove(listener)
+
+ private fun parseIconState(): IconState {
+ val shapeOverride = prefs.get(PREF_ICON_SHAPE)
+ return IconState(
+ iconMask =
+ when {
+ shapeOverride.isNotEmpty() -> shapeOverride
+ CONFIG_ICON_MASK_RES_ID == Resources.ID_NULL -> ""
+ else -> context.resources.getString(CONFIG_ICON_MASK_RES_ID)
+ },
+ isMonoTheme = isMonoThemeEnabled,
+ )
+ }
+
+ data class IconState(
+ val iconMask: String,
+ val isMonoTheme: Boolean,
+ val themeCode: String = if (isMonoTheme) "with-theme" else "no-theme",
+ ) {
+ fun toUniqueId() = "${iconMask.hashCode()},$themeCode"
+ }
+
+ /** Interface for receiving theme change events */
+ fun interface ThemeChangeListener {
+ fun onThemeChanged()
+ }
+
+ companion object {
+
+ @JvmField val INSTANCE = DaggerSingletonObject(LauncherAppComponent::getThemeManager)
+
+ const val KEY_THEMED_ICONS = "themed_icons"
+ @JvmField val THEMED_ICONS = backedUpItem(KEY_THEMED_ICONS, false, EncryptionType.ENCRYPTED)
+
+ private const val ACTION_OVERLAY_CHANGED = "android.intent.action.OVERLAY_CHANGED"
+ private val CONFIG_ICON_MASK_RES_ID: Int =
+ Resources.getSystem().getIdentifier("config_icon_mask", "string", "android")
+ }
+}
diff --git a/src/com/android/launcher3/icons/LauncherIconProvider.java b/src/com/android/launcher3/icons/LauncherIconProvider.java
index 78a3128..e40f526 100644
--- a/src/com/android/launcher3/icons/LauncherIconProvider.java
+++ b/src/com/android/launcher3/icons/LauncherIconProvider.java
@@ -27,8 +27,8 @@
import com.android.launcher3.R;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.graphics.ThemeManager;
import com.android.launcher3.util.ApiWrapper;
-import com.android.launcher3.util.Themes;
import org.xmlpull.v1.XmlPullParser;
@@ -48,18 +48,16 @@
private static final Map<String, ThemeData> DISABLED_MAP = Collections.emptyMap();
private Map<String, ThemeData> mThemedIconMap;
- private boolean mSupportsIconTheme;
public LauncherIconProvider(Context context) {
super(context);
- setIconThemeSupported(Themes.isThemedIconEnabled(context));
+ setIconThemeSupported(ThemeManager.INSTANCE.get(context).isMonoThemeEnabled());
}
/**
* Enables or disables icon theme support
*/
public void setIconThemeSupported(boolean isSupported) {
- mSupportsIconTheme = isSupported;
mThemedIconMap = isSupported && FeatureFlags.USE_LOCAL_ICON_OVERRIDES.get()
? null : DISABLED_MAP;
}
@@ -70,8 +68,9 @@
}
@Override
- public String getSystemIconState() {
- return super.getSystemIconState() + (mSupportsIconTheme ? ",with-theme" : ",no-theme");
+ public void updateSystemState() {
+ super.updateSystemState();
+ mSystemState += "," + ThemeManager.INSTANCE.get(mContext).getIconState().toUniqueId();
}
@Override
diff --git a/src/com/android/launcher3/icons/LauncherIcons.java b/src/com/android/launcher3/icons/LauncherIcons.java
index 839dfb7..2ffbeb8 100644
--- a/src/com/android/launcher3/icons/LauncherIcons.java
+++ b/src/com/android/launcher3/icons/LauncherIcons.java
@@ -17,17 +17,24 @@
package com.android.launcher3.icons;
import android.content.Context;
+import android.graphics.Matrix;
+import android.graphics.Path;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.AdaptiveIconDrawable;
import android.os.UserHandle;
import androidx.annotation.NonNull;
+import androidx.core.graphics.PathParser;
+import com.android.launcher3.Flags;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.graphics.IconShape;
-import com.android.launcher3.icons.mono.MonoIconThemeController;
+import com.android.launcher3.graphics.ThemeManager;
import com.android.launcher3.pm.UserCache;
+import com.android.launcher3.shapes.IconShapeModel;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.SafeCloseable;
-import com.android.launcher3.util.Themes;
import com.android.launcher3.util.UserIconInfo;
import java.util.concurrent.ConcurrentLinkedQueue;
@@ -57,11 +64,8 @@
protected LauncherIcons(Context context, int fillResIconDpi, int iconBitmapSize,
ConcurrentLinkedQueue<LauncherIcons> pool) {
- super(context, fillResIconDpi, iconBitmapSize,
- IconShape.INSTANCE.get(context).getShape().enableShapeDetection());
- if (Themes.isThemedIconEnabled(context)) {
- mThemeController = new MonoIconThemeController();
- }
+ super(context, fillResIconDpi, iconBitmapSize);
+ mThemeController = ThemeManager.INSTANCE.get(context).getThemeController();
mPool = pool;
}
@@ -79,6 +83,28 @@
return UserCache.INSTANCE.get(mContext).getUserInfo(user);
}
+ @NonNull
+ @Override
+ public Path getShapePath(AdaptiveIconDrawable drawable, Rect iconBounds) {
+ if (!Flags.enableLauncherIconShapes()) return drawable.getIconMask();
+
+ IconShapeModel shapeOverride = IconShape.INSTANCE.get(mContext).getShapeOverride();
+ if (shapeOverride != null) {
+ Path maskPath = PathParser.createPathFromPathData(shapeOverride.getPathString());
+ Matrix matrix = new Matrix();
+ // Assuming Path is in [0, 0, 100, 100] coordinate space.
+ matrix.setRectToRect(
+ new RectF(0, 0, 100, 100),
+ new RectF(iconBounds),
+ Matrix.ScaleToFit.CENTER // Todo: CENTER or FILL?
+ );
+ maskPath.transform(matrix);
+ return maskPath;
+ } else {
+ return drawable.getIconMask();
+ }
+ }
+
@Override
public void close() {
recycle();
diff --git a/src/com/android/launcher3/model/GridSizeMigrationDBController.java b/src/com/android/launcher3/model/GridSizeMigrationDBController.java
index bfa00bd..0732379 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationDBController.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationDBController.java
@@ -120,39 +120,50 @@
@NonNull DeviceGridState destDeviceState,
@NonNull DatabaseHelper target,
@NonNull SQLiteDatabase source,
- boolean isDestNewDb) {
+ boolean isDestNewDb,
+ ModelDelegate modelDelegate) {
if (!needsToMigrate(srcDeviceState, destDeviceState)) {
return true;
}
- if (isDestNewDb
+ boolean shouldMigrateToStrictlyTallerGrid = isDestNewDb
&& srcDeviceState.getColumns().equals(destDeviceState.getColumns())
- && srcDeviceState.getRows() < destDeviceState.getRows()) {
- // Only use this strategy when comparing the previous grid to the new grid and the
- // columns are the same and the destination has more rows
+ && srcDeviceState.getRows() < destDeviceState.getRows();
+ if (shouldMigrateToStrictlyTallerGrid) {
copyTable(source, TABLE_NAME, target.getWritableDatabase(), TABLE_NAME, context);
-
- if (oneGridSpecs()) {
- DbReader destReader = new DbReader(
- target.getWritableDatabase(), TABLE_NAME, context);
- boolean shouldShiftCells = shouldShiftCells(destReader, srcDeviceState.getRows());
- if (shouldShiftCells) {
- shiftTableByXCells(
- target.getWritableDatabase(),
- (destDeviceState.getRows() - srcDeviceState.getRows()),
- TABLE_NAME);
- }
- }
-
- // Save current configuration, so that the migration does not run again.
- destDeviceState.writeToPrefs(context);
- return true;
+ } else {
+ copyTable(source, TABLE_NAME, target.getWritableDatabase(), TMP_TABLE, context);
}
- copyTable(source, TABLE_NAME, target.getWritableDatabase(), TMP_TABLE, context);
long migrationStartTime = System.currentTimeMillis();
try (SQLiteTransaction t = new SQLiteTransaction(target.getWritableDatabase())) {
+
+ if (shouldMigrateToStrictlyTallerGrid) {
+ // This is a special case where if the grid is the same amount of columns but a
+ // larger amount of rows we simply copy over the source grid to the destination
+ // grid, rather than undergoing the general grid migration. If there are more icons
+ // on the bottom of the first page then we shift the icons down to the bottom of the
+ // grid so that the icons remain bottom-anchored.
+ if (oneGridSpecs()) {
+ DbReader destReader = new DbReader(
+ target.getWritableDatabase(), TABLE_NAME, context);
+ boolean shouldShiftCells =
+ shouldShiftCells(destReader, srcDeviceState.getRows());
+ if (shouldShiftCells) {
+ shiftTableByXCells(
+ target.getWritableDatabase(),
+ (destDeviceState.getRows() - srcDeviceState.getRows()),
+ TABLE_NAME);
+ }
+ }
+
+ // Save current configuration, so that the migration does not run again.
+ destDeviceState.writeToPrefs(context);
+ t.commit();
+ return true;
+ }
+
DbReader srcReader = new DbReader(t.getDb(), TMP_TABLE, context);
DbReader destReader = new DbReader(t.getDb(), TABLE_NAME, context);
@@ -164,7 +175,6 @@
return true;
} catch (Exception e) {
Log.e(TAG, "Error during grid migration", e);
-
return false;
} finally {
Log.v(TAG, "Workspace migration completed in "
@@ -172,6 +182,8 @@
// Save current configuration, so that the migration does not run again.
destDeviceState.writeToPrefs(context);
+ // Notify if we've migrated successfully
+ modelDelegate.gridMigrationComplete(srcDeviceState, destDeviceState);
}
}
diff --git a/src/com/android/launcher3/model/GridSizeMigrationLogic.kt b/src/com/android/launcher3/model/GridSizeMigrationLogic.kt
index 6f86ae0..1729153 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationLogic.kt
+++ b/src/com/android/launcher3/model/GridSizeMigrationLogic.kt
@@ -53,6 +53,7 @@
target: DatabaseHelper,
source: SQLiteDatabase,
isDestNewDb: Boolean,
+ modelDelegate: ModelDelegate,
) {
if (!GridSizeMigrationDBController.needsToMigrate(srcDeviceState, destDeviceState)) {
return
@@ -65,33 +66,42 @@
"$srcDeviceState\ndestDeviceState: $destDeviceState\nisDestNewDb: $isDestNewDb",
)
- // This is a special case where if the grid is the same amount of columns but a larger
- // amount of rows we simply copy over the source grid to the destination grid, rather
- // than undergoing the general grid migration.
- if (shouldMigrateToStrictlyTallerGrid(isDestNewDb, srcDeviceState, destDeviceState)) {
- Log.d(TAG, "Migrating to strictly taller grid")
+ val shouldMigrateToStrtictlyTallerGrid =
+ shouldMigrateToStrictlyTallerGrid(isDestNewDb, srcDeviceState, destDeviceState)
+ if (shouldMigrateToStrtictlyTallerGrid) {
copyTable(source, TABLE_NAME, target.writableDatabase, TABLE_NAME, context)
- if (oneGridSpecs()) {
- val destReader = DbReader(target.writableDatabase, TABLE_NAME, context)
- val shouldShiftCells = shouldShiftCells(destReader, srcDeviceState.rows)
- if (shouldShiftCells) {
- shiftTableByXCells(
- target.writableDatabase,
- (destDeviceState.rows - srcDeviceState.rows),
- TABLE_NAME,
- )
- }
- }
- // Save current configuration, so that the migration does not run again.
- destDeviceState.writeToPrefs(context)
- return
+ } else {
+ copyTable(source, TABLE_NAME, target.writableDatabase, TMP_TABLE, context)
}
- copyTable(source, TABLE_NAME, target.writableDatabase, TMP_TABLE, context)
-
val migrationStartTime = System.currentTimeMillis()
try {
SQLiteTransaction(target.writableDatabase).use { t ->
+ // This is a special case where if the grid is the same amount of columns but a
+ // larger amount of rows we simply copy over the source grid to the destination
+ // grid, rather than undergoing the general grid migration. If there are more icons
+ // on the bottom of the first page then we shift the icons down to the bottom of the
+ // grid so that the icons remain bottom-anchored.
+ if (shouldMigrateToStrtictlyTallerGrid) {
+ Log.d(TAG, "Migrating to strictly taller grid")
+ if (oneGridSpecs()) {
+ val destReader = DbReader(target.writableDatabase, TABLE_NAME, context)
+ val shouldShiftCells = shouldShiftCells(destReader, srcDeviceState.rows)
+ if (shouldShiftCells) {
+ Log.i("TAGTAG", "should shift cells")
+ shiftTableByXCells(
+ target.writableDatabase,
+ (destDeviceState.rows - srcDeviceState.rows),
+ TABLE_NAME,
+ )
+ }
+ }
+ // Save current configuration, so that the migration does not run again.
+ destDeviceState.writeToPrefs(context)
+ t.commit()
+ return
+ }
+
val srcReader = DbReader(t.db, TMP_TABLE, context)
val destReader = DbReader(t.db, TABLE_NAME, context)
@@ -123,6 +133,9 @@
// Save current configuration, so that the migration does not run again.
destDeviceState.writeToPrefs(context)
+
+ // Notify if we've migrated successfully
+ modelDelegate.gridMigrationComplete(srcDeviceState, destDeviceState)
}
}
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 4e57944..44b7e8b 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -438,12 +438,12 @@
ModelDbController dbController = mApp.getModel().getModelDbController();
if (Flags.gridMigrationRefactor()) {
try {
- dbController.attemptMigrateDb(restoreEventLogger);
+ dbController.attemptMigrateDb(restoreEventLogger, mModelDelegate);
} catch (Exception e) {
FileLog.e(TAG, "Failed to migrate grid", e);
}
} else {
- dbController.tryMigrateDB(restoreEventLogger);
+ dbController.tryMigrateDB(restoreEventLogger, mModelDelegate);
}
Log.d(TAG, "loadWorkspace: loading default favorites if necessary");
dbController.loadDefaultFavoritesIfNecessary();
diff --git a/src/com/android/launcher3/model/ModelDbController.java b/src/com/android/launcher3/model/ModelDbController.java
index e76391f..0138390 100644
--- a/src/com/android/launcher3/model/ModelDbController.java
+++ b/src/com/android/launcher3/model/ModelDbController.java
@@ -361,7 +361,8 @@
/**
* Migrates the DB. If the migration failed, it clears the DB.
*/
- public void attemptMigrateDb(LauncherRestoreEventLogger restoreEventLogger) throws Exception {
+ public void attemptMigrateDb(LauncherRestoreEventLogger restoreEventLogger,
+ ModelDelegate modelDelegate) throws Exception {
createDbIfNotExists();
if (shouldResetDb()) {
@@ -389,7 +390,7 @@
boolean isDestNewDb = !existingDBs.contains(destDeviceState.getDbFile());
GridSizeMigrationLogic gridSizeMigrationLogic = new GridSizeMigrationLogic();
gridSizeMigrationLogic.migrateGrid(mContext, srcDeviceState, destDeviceState,
- mOpenHelper, oldHelper.getWritableDatabase(), isDestNewDb);
+ mOpenHelper, oldHelper.getWritableDatabase(), isDestNewDb, modelDelegate);
} catch (Exception e) {
resetLauncherDb(restoreEventLogger);
throw new Exception("attemptMigrateDb: Failed to migrate grid", e);
@@ -403,8 +404,9 @@
/**
* Migrates the DB if needed. If the migration failed, it clears the DB.
*/
- public void tryMigrateDB(@Nullable LauncherRestoreEventLogger restoreEventLogger) {
- if (!migrateGridIfNeeded()) {
+ public void tryMigrateDB(@Nullable LauncherRestoreEventLogger restoreEventLogger,
+ ModelDelegate modelDelegate) {
+ if (!migrateGridIfNeeded(modelDelegate)) {
if (restoreEventLogger != null) {
if (LauncherPrefs.get(mContext).get(NO_DB_FILES_RESTORED)) {
restoreEventLogger.logLauncherItemsRestoreFailed(DATA_TYPE_DB_FILE, 1,
@@ -434,7 +436,7 @@
* @return true if migration was success or ignored, false if migration failed
* and the DB should be reset.
*/
- private boolean migrateGridIfNeeded() {
+ private boolean migrateGridIfNeeded(ModelDelegate modelDelegate) {
createDbIfNotExists();
if (LauncherPrefs.get(mContext).get(getEmptyDbCreatedKey())) {
// If we have already create a new DB, ignore migration
@@ -468,7 +470,8 @@
DeviceGridState destDeviceState = new DeviceGridState(idp);
boolean isDestNewDb = !existingDBs.contains(destDeviceState.getDbFile());
return GridSizeMigrationDBController.migrateGridIfNeeded(mContext, srcDeviceState,
- destDeviceState, mOpenHelper, oldHelper.getWritableDatabase(), isDestNewDb);
+ destDeviceState, mOpenHelper, oldHelper.getWritableDatabase(), isDestNewDb,
+ modelDelegate);
} catch (Exception e) {
FileLog.e(TAG, "migrateGridIfNeeded: Failed to migrate grid", e);
return false;
diff --git a/src/com/android/launcher3/model/ModelDelegate.java b/src/com/android/launcher3/model/ModelDelegate.java
index 2264d35..5a2aef0 100644
--- a/src/com/android/launcher3/model/ModelDelegate.java
+++ b/src/com/android/launcher3/model/ModelDelegate.java
@@ -123,6 +123,11 @@
@WorkerThread
public void modelLoadComplete() { }
+ /** Called when grid migration has completed as part of grid size refactor. */
+ @WorkerThread
+ public void gridMigrationComplete(
+ @NonNull DeviceGridState src, @NonNull DeviceGridState dest) { }
+
/**
* Called when the delegate is no loner needed
*/
diff --git a/src/com/android/launcher3/model/data/FolderInfo.java b/src/com/android/launcher3/model/data/FolderInfo.java
index f0f2892..9656ac1 100644
--- a/src/com/android/launcher3/model/data/FolderInfo.java
+++ b/src/com/android/launcher3/model/data/FolderInfo.java
@@ -164,7 +164,7 @@
}
/**
- * Returns the folder's contents as an ArrayList of {@link ItemInfo}. Includes
+ * Returns the folder's contents as an unsorted ArrayList of {@link ItemInfo}. Includes
* {@link WorkspaceItemInfo} and {@link AppPairInfo}s.
*/
@NonNull
diff --git a/src/com/android/launcher3/shapes/AppShapesProvider.kt b/src/com/android/launcher3/shapes/AppShapesProvider.kt
deleted file mode 100644
index 8c2f181..0000000
--- a/src/com/android/launcher3/shapes/AppShapesProvider.kt
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.shapes
-
-import com.android.systemui.shared.Flags
-
-object AppShapesProvider {
-
- val shapes =
- if (Flags.newCustomizationPickerUi())
- listOf(
- AppShape(
- "arch",
- "arch",
- "M100 83.46C100 85.471 100 86.476 99.9 87.321 99.116 93.916 93.916 99.116 87.321 99.9 86.476 100 85.471 100 83.46 100H16.54C14.529 100 13.524 100 12.679 99.9 6.084 99.116.884 93.916.1 87.321 0 86.476 0 85.471 0 83.46L0 50C0 22.386 22.386 0 50 0 77.614 0 100 22.386 100 50V83.46Z",
- ),
- AppShape(
- "4_sided_cookie",
- "4 sided cookie",
- "M63.605 3C84.733-6.176 106.176 15.268 97 36.395L95.483 39.888C92.681 46.338 92.681 53.662 95.483 60.112L97 63.605C106.176 84.732 84.733 106.176 63.605 97L60.112 95.483C53.662 92.681 46.338 92.681 39.888 95.483L36.395 97C15.267 106.176-6.176 84.732 3 63.605L4.517 60.112C7.319 53.662 7.319 46.338 4.517 39.888L3 36.395C-6.176 15.268 15.267-6.176 36.395 3L39.888 4.517C46.338 7.319 53.662 7.319 60.112 4.517L63.605 3Z",
- ),
- AppShape(
- "seven_sided_cookie",
- "7 sided cookie",
- "M35.209 4.878C36.326 3.895 36.884 3.404 37.397 3.006 44.82-2.742 55.18-2.742 62.603 3.006 63.116 3.404 63.674 3.895 64.791 4.878 65.164 5.207 65.351 5.371 65.539 5.529 68.167 7.734 71.303 9.248 74.663 9.932 74.902 9.981 75.147 10.025 75.637 10.113 77.1 10.375 77.831 10.506 78.461 10.66 87.573 12.893 94.032 21.011 94.176 30.412 94.186 31.062 94.151 31.805 94.08 33.293 94.057 33.791 94.045 34.04 94.039 34.285 93.958 37.72 94.732 41.121 96.293 44.18 96.404 44.399 96.522 44.618 96.759 45.056 97.467 46.366 97.821 47.021 98.093 47.611 102.032 56.143 99.727 66.266 92.484 72.24 91.983 72.653 91.381 73.089 90.177 73.961 89.774 74.254 89.572 74.4 89.377 74.548 86.647 76.626 84.477 79.353 83.063 82.483 82.962 82.707 82.865 82.936 82.671 83.395 82.091 84.766 81.8 85.451 81.51 86.033 77.31 94.44 67.977 98.945 58.801 96.994 58.166 96.859 57.451 96.659 56.019 96.259 55.54 96.125 55.3 96.058 55.063 95.998 51.74 95.154 48.26 95.154 44.937 95.998 44.699 96.058 44.46 96.125 43.981 96.259 42.549 96.659 41.834 96.859 41.199 96.994 32.023 98.945 22.69 94.44 18.49 86.033 18.2 85.451 17.909 84.766 17.329 83.395 17.135 82.936 17.038 82.707 16.937 82.483 15.523 79.353 13.353 76.626 10.623 74.548 10.428 74.4 10.226 74.254 9.823 73.961 8.619 73.089 8.017 72.653 7.516 72.24.273 66.266-2.032 56.143 1.907 47.611 2.179 47.021 2.533 46.366 3.241 45.056 3.478 44.618 3.596 44.399 3.707 44.18 5.268 41.121 6.042 37.72 5.961 34.285 5.955 34.04 5.943 33.791 5.92 33.293 5.849 31.805 5.814 31.062 5.824 30.412 5.968 21.011 12.427 12.893 21.539 10.66 22.169 10.506 22.9 10.375 24.363 10.113 24.853 10.025 25.098 9.981 25.337 9.932 28.697 9.248 31.833 7.734 34.461 5.529 34.649 5.371 34.836 5.207 35.209 4.878Z",
- ),
- AppShape(
- "sunny",
- "sunny",
- "M42.846 4.873C46.084-.531 53.916-.531 57.154 4.873L60.796 10.951C62.685 14.103 66.414 15.647 69.978 14.754L76.851 13.032C82.962 11.5 88.5 17.038 86.968 23.149L85.246 30.022C84.353 33.586 85.897 37.315 89.049 39.204L95.127 42.846C100.531 46.084 100.531 53.916 95.127 57.154L89.049 60.796C85.897 62.685 84.353 66.414 85.246 69.978L86.968 76.851C88.5 82.962 82.962 88.5 76.851 86.968L69.978 85.246C66.414 84.353 62.685 85.898 60.796 89.049L57.154 95.127C53.916 100.531 46.084 100.531 42.846 95.127L39.204 89.049C37.315 85.898 33.586 84.353 30.022 85.246L23.149 86.968C17.038 88.5 11.5 82.962 13.032 76.851L14.754 69.978C15.647 66.414 14.103 62.685 10.951 60.796L4.873 57.154C-.531 53.916-.531 46.084 4.873 42.846L10.951 39.204C14.103 37.315 15.647 33.586 14.754 30.022L13.032 23.149C11.5 17.038 17.038 11.5 23.149 13.032L30.022 14.754C33.586 15.647 37.315 14.103 39.204 10.951L42.846 4.873Z",
- ),
- AppShape(
- "circle",
- "circle",
- "M99.18 50C99.18 77.162 77.162 99.18 50 99.18 22.838 99.18.82 77.162.82 50 .82 22.839 22.838.82 50 .82 77.162.82 99.18 22.839 99.18 50Z",
- ),
- AppShape(
- "square",
- "square",
- "M99.18 53.689C99.18 67.434 99.18 74.306 97.022 79.758 93.897 87.649 87.649 93.897 79.758 97.022 74.306 99.18 67.434 99.18 53.689 99.18H46.311C32.566 99.18 25.694 99.18 20.242 97.022 12.351 93.897 6.103 87.649 2.978 79.758.82 74.306.82 67.434.82 53.689L.82 46.311C.82 32.566.82 25.694 2.978 20.242 6.103 12.351 12.351 6.103 20.242 2.978 25.694.82 32.566.82 46.311.82L53.689.82C67.434.82 74.306.82 79.758 2.978 87.649 6.103 93.897 12.351 97.022 20.242 99.18 25.694 99.18 32.566 99.18 46.311V53.689Z\n",
- ),
- )
- else emptyList()
-}
diff --git a/src/com/android/launcher3/shapes/AppShape.kt b/src/com/android/launcher3/shapes/IconShapeModel.kt
similarity index 88%
rename from src/com/android/launcher3/shapes/AppShape.kt
rename to src/com/android/launcher3/shapes/IconShapeModel.kt
index 68200a0..10b7f91 100644
--- a/src/com/android/launcher3/shapes/AppShape.kt
+++ b/src/com/android/launcher3/shapes/IconShapeModel.kt
@@ -16,4 +16,4 @@
package com.android.launcher3.shapes
-class AppShape(val key: String, val title: String, val path: String)
+data class IconShapeModel(val key: String, val title: String, val pathString: String)
diff --git a/src/com/android/launcher3/shapes/IconShapesProvider.kt b/src/com/android/launcher3/shapes/IconShapesProvider.kt
new file mode 100644
index 0000000..a190e22
--- /dev/null
+++ b/src/com/android/launcher3/shapes/IconShapesProvider.kt
@@ -0,0 +1,78 @@
+/*
+ * 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.shapes
+
+import com.android.launcher3.Flags as LauncherFlags
+import com.android.systemui.shared.Flags
+
+object IconShapesProvider {
+ val shapes =
+ if (Flags.newCustomizationPickerUi() && LauncherFlags.enableLauncherIconShapes()) {
+ mapOf(
+ "arch" to
+ IconShapeModel(
+ key = "arch",
+ title = "arch",
+ pathString =
+ "M50 0C77.614 0 100 22.386 100 50C100 85.471 100 86.476 99.9 87.321 99.116 93.916 93.916 99.116 87.321 99.9 86.476 100 85.471 100 83.46 100H16.54C14.529 100 13.524 100 12.679 99.9 6.084 99.116.884 93.916.1 87.321 0 86.476 0 85.471 0 83.46L0 50C0 22.386 22.386 0 50 0Z",
+ ),
+ "4_sided_cookie" to
+ IconShapeModel(
+ key = "4_sided_cookie",
+ title = "4 sided cookie",
+ pathString =
+ "M39.888,4.517C46.338 7.319 53.662 7.319 60.112 4.517L63.605 3C84.733 -6.176 106.176 15.268 97 36.395L95.483 39.888C92.681 46.338 92.681 53.662 95.483 60.112L97 63.605C106.176 84.732 84.733 106.176 63.605 97L60.112 95.483C53.662 92.681 46.338 92.681 39.888 95.483L36.395 97C15.267 106.176 -6.176 84.732 3 63.605L4.517 60.112C7.319 53.662 7.319 46.338 4.517 39.888L3 36.395C -6.176 15.268 15.267 -6.176 36.395 3Z",
+ ),
+ "seven_sided_cookie" to
+ IconShapeModel(
+ key = "seven_sided_cookie",
+ title = "7 sided cookie",
+ pathString =
+ "M35.209 4.878C36.326 3.895 36.884 3.404 37.397 3.006 44.82 -2.742 55.18 -2.742 62.603 3.006 63.116 3.404 63.674 3.895 64.791 4.878 65.164 5.207 65.351 5.371 65.539 5.529 68.167 7.734 71.303 9.248 74.663 9.932 74.902 9.981 75.147 10.025 75.637 10.113 77.1 10.375 77.831 10.506 78.461 10.66 87.573 12.893 94.032 21.011 94.176 30.412 94.186 31.062 94.151 31.805 94.08 33.293 94.057 33.791 94.045 34.04 94.039 34.285 93.958 37.72 94.732 41.121 96.293 44.18 96.404 44.399 96.522 44.618 96.759 45.056 97.467 46.366 97.821 47.021 98.093 47.611 102.032 56.143 99.727 66.266 92.484 72.24 91.983 72.653 91.381 73.089 90.177 73.961 89.774 74.254 89.572 74.4 89.377 74.548 86.647 76.626 84.477 79.353 83.063 82.483 82.962 82.707 82.865 82.936 82.671 83.395 82.091 84.766 81.8 85.451 81.51 86.033 77.31 94.44 67.977 98.945 58.801 96.994 58.166 96.859 57.451 96.659 56.019 96.259 55.54 96.125 55.3 96.058 55.063 95.998 51.74 95.154 48.26 95.154 44.937 95.998 44.699 96.058 44.46 96.125 43.981 96.259 42.549 96.659 41.834 96.859 41.199 96.994 32.023 98.945 22.69 94.44 18.49 86.033 18.2 85.451 17.909 84.766 17.329 83.395 17.135 82.936 17.038 82.707 16.937 82.483 15.523 79.353 13.353 76.626 10.623 74.548 10.428 74.4 10.226 74.254 9.823 73.961 8.619 73.089 8.017 72.653 7.516 72.24.273 66.266 -2.032 56.143 1.907 47.611 2.179 47.021 2.533 46.366 3.241 45.056 3.478 44.618 3.596 44.399 3.707 44.18 5.268 41.121 6.042 37.72 5.961 34.285 5.955 34.04 5.943 33.791 5.92 33.293 5.849 31.805 5.814 31.062 5.824 30.412 5.968 21.011 12.427 12.893 21.539 10.66 22.169 10.506 22.9 10.375 24.363 10.113 24.853 10.025 25.098 9.981 25.337 9.932 28.697 9.248 31.833 7.734 34.461 5.529 34.649 5.371 34.836 5.207 35.209 4.878Z",
+ ),
+ "sunny" to
+ IconShapeModel(
+ key = "sunny",
+ title = "sunny",
+ pathString =
+ "M42.846 4.873C46.084 -.531 53.916 -.531 57.154 4.873L60.796 10.951C62.685 14.103 66.414 15.647 69.978 14.754L76.851 13.032C82.962 11.5 88.5 17.038 86.968 23.149L85.246 30.022C84.353 33.586 85.897 37.315 89.049 39.204L95.127 42.846C100.531 46.084 100.531 53.916 95.127 57.154L89.049 60.796C85.897 62.685 84.353 66.414 85.246 69.978L86.968 76.851C88.5 82.962 82.962 88.5 76.851 86.968L69.978 85.246C66.414 84.353 62.685 85.898 60.796 89.049L57.154 95.127C53.916 100.531 46.084 100.531 42.846 95.127L39.204 89.049C37.315 85.898 33.586 84.353 30.022 85.246L23.149 86.968C17.038 88.5 11.5 82.962 13.032 76.851L14.754 69.978C15.647 66.414 14.103 62.685 10.951 60.796L4.873 57.154C -.531 53.916 -.531 46.084 4.873 42.846L10.951 39.204C14.103 37.315 15.647 33.586 14.754 30.022L13.032 23.149C11.5 17.038 17.038 11.5 23.149 13.032L30.022 14.754C33.586 15.647 37.315 14.103 39.204 10.951L42.846 4.873Z",
+ ),
+ "circle" to
+ IconShapeModel(
+ key = "circle",
+ title = "circle",
+ pathString = "M50 0A50 50,0,1,1,50 100A50 50,0,1,1,50 0",
+ ),
+ "square" to
+ IconShapeModel(
+ key = "square",
+ title = "square",
+ pathString =
+ "M53.689 0.82L53.689.82C67.434.82 74.306.82 79.758 2.978 87.649 6.103 93.897 12.351 97.022 20.242 99.18 25.694 99.18 32.566 99.18 46.311V53.689C99.18 67.434 99.18 74.306 97.022 79.758 93.897 87.649 87.649 93.897 79.758 97.022 74.306 99.18 67.434 99.18 53.689 99.18H46.311C32.566 99.18 25.694 99.18 20.242 97.022 12.351 93.897 6.103 87.649 2.978 79.758.82 74.306.82 67.434.82 53.689L.82 46.311C.82 32.566.82 25.694 2.978 20.242 6.103 12.351 12.351 6.103 20.242 2.978 25.694.82 32.566.82 46.311.82Z",
+ ),
+ )
+ } else {
+ mapOf(
+ "circle" to
+ IconShapeModel(
+ key = "circle",
+ title = "circle",
+ pathString = "M50 0A50 50,0,1,1,50 100A50 50,0,1,1,50 0",
+ )
+ )
+ }
+}
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index fb5c8c7..daa6e67 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -15,12 +15,13 @@
*/
package com.android.launcher3.testing;
+import static com.android.launcher3.Flags.enableFallbackOverviewInWindow;
import static com.android.launcher3.Flags.enableGridOnlyOverview;
+import static com.android.launcher3.Flags.enableLauncherOverviewInWindow;
import static com.android.launcher3.allapps.AllAppsStore.DEFER_UPDATES_TEST;
import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION;
import static com.android.launcher3.config.FeatureFlags.FOLDABLE_SINGLE_PAGE;
import static com.android.launcher3.config.FeatureFlags.enableAppPairs;
-import static com.android.launcher3.config.FeatureFlags.enableSplitContextually;
import static com.android.launcher3.testing.shared.TestProtocol.TEST_INFO_RESPONSE_FIELD;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
@@ -243,8 +244,8 @@
}
case TestProtocol.REQUEST_GET_SPLIT_SELECTION_ACTIVE:
- response.putBoolean(TEST_INFO_RESPONSE_FIELD, enableSplitContextually()
- && Launcher.ACTIVITY_TRACKER.getCreatedContext().isSplitSelectionActive());
+ response.putBoolean(TEST_INFO_RESPONSE_FIELD,
+ Launcher.ACTIVITY_TRACKER.getCreatedContext().isSplitSelectionActive());
return response;
case TestProtocol.REQUEST_ENABLE_ROTATION:
@@ -330,6 +331,12 @@
return response;
}
+ case TestProtocol.REQUEST_IS_RECENTS_WINDOW_ENABLED: {
+ response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD,
+ enableLauncherOverviewInWindow() || enableFallbackOverviewInWindow());
+ return response;
+ }
+
case TestProtocol.REQUEST_APP_LIST_FREEZE_FLAGS: {
return getLauncherUIProperty(Bundle::putInt,
l -> l.getAppsView().getAppsStore().getDeferUpdatesFlags());
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 74a0966..3817563 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -40,13 +40,11 @@
import com.android.launcher3.LauncherState;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.contextualeducation.ContextualEduStatsManager;
import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.util.FlingBlockCheck;
import com.android.launcher3.util.TouchController;
-import com.android.systemui.contextualeducation.GestureType;
/**
* TouchController for handling state changes
@@ -390,7 +388,6 @@
} else {
logReachedState(mToState);
}
- updateContextualEduStats(targetState);
}
protected void goToTargetState(LauncherState targetState) {
@@ -406,18 +403,6 @@
.setDuration(0).start();
}
- private void updateContextualEduStats(LauncherState targetState) {
- if (targetState == OVERVIEW) {
- ContextualEduStatsManager.INSTANCE.get(
- mLauncher).updateEduStats(mDetector.isTrackpadGesture(), GestureType.OVERVIEW);
- } else if (targetState == ALL_APPS && !mDetector.isTrackpadGesture()) {
- // Only update if it is touch gesture as trackpad gesture is not relevant for all apps
- // which only provides keyboard education.
- ContextualEduStatsManager.INSTANCE.get(
- mLauncher).updateEduStats(/* isTrackpadGesture= */ false, GestureType.ALL_APPS);
- }
- }
-
private void logReachedState(LauncherState targetState) {
if (mStartState == targetState) {
return;
diff --git a/src/com/android/launcher3/touch/ItemLongClickListener.java b/src/com/android/launcher3/touch/ItemLongClickListener.java
index 89057a2..98ba8c7 100644
--- a/src/com/android/launcher3/touch/ItemLongClickListener.java
+++ b/src/com/android/launcher3/touch/ItemLongClickListener.java
@@ -188,7 +188,7 @@
// Return early if an item is already being dragged (e.g. when long-pressing two shortcuts)
if (launcher.getDragController().isDragging()) return false;
// Return early if user is in the middle of selecting split-screen apps
- if (FeatureFlags.enableSplitContextually() && launcher.isSplitSelectionActive()) {
+ if (launcher.isSplitSelectionActive()) {
return false;
}
diff --git a/src/com/android/launcher3/touch/WorkspaceTouchListener.java b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
index 0ff10c2..b69bc17 100644
--- a/src/com/android/launcher3/touch/WorkspaceTouchListener.java
+++ b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
@@ -207,7 +207,7 @@
HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
mLauncher.getStatsLogManager().logger().log(LAUNCHER_WORKSPACE_LONGPRESS);
mLauncher.showDefaultOptions(mTouchDownPoint.x, mTouchDownPoint.y);
- if (FeatureFlags.enableSplitContextually() && mLauncher.isSplitSelectionActive()) {
+ if (mLauncher.isSplitSelectionActive()) {
mLauncher.dismissSplitSelection(LAUNCHER_SPLIT_SELECTION_EXIT_INTERRUPTED);
}
} else {
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index 26912eb..9472f5f 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -53,9 +53,13 @@
import com.android.launcher3.LauncherPrefChangeListener;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.Utilities;
+import com.android.launcher3.dagger.ApplicationContext;
+import com.android.launcher3.dagger.LauncherAppComponent;
+import com.android.launcher3.dagger.LauncherAppSingleton;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.util.window.CachedDisplayInfo;
import com.android.launcher3.util.window.WindowManagerProxy;
+import com.android.launcher3.util.window.WindowManagerProxy.DesktopVisibilityListener;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -66,11 +70,15 @@
import java.util.Set;
import java.util.StringJoiner;
+import javax.inject.Inject;
+
/**
* Utility class to cache properties of default display to avoid a system RPC on every call.
*/
@SuppressLint("NewApi")
-public class DisplayController implements ComponentCallbacks, SafeCloseable {
+@LauncherAppSingleton
+public class DisplayController implements ComponentCallbacks,
+ DesktopVisibilityListener {
private static final String TAG = "DisplayController";
private static final boolean DEBUG = false;
@@ -80,8 +88,8 @@
// TODO(b/254119092) remove all logs with this tag
public static final String TASKBAR_NOT_DESTROYED_TAG = "b/254119092";
- public static final MainThreadInitializedObject<DisplayController> INSTANCE =
- new MainThreadInitializedObject<>(DisplayController::new);
+ public static final DaggerSingletonObject<DisplayController> INSTANCE =
+ new DaggerSingletonObject<>(LauncherAppComponent::getDisplayController);
public static final int CHANGE_ACTIVE_SCREEN = 1 << 0;
public static final int CHANGE_ROTATION = 1 << 1;
@@ -99,7 +107,7 @@
private static final String TARGET_OVERLAY_PACKAGE = "android";
private final Context mContext;
- private final DisplayManager mDM;
+ private final WindowManagerProxy mWMProxy;
// Null for SDK < S
private final Context mWindowContext;
@@ -116,48 +124,52 @@
private Info mInfo;
private boolean mDestroyed = false;
- private LauncherPrefChangeListener mTaskbarPinningPreferenceChangeListener;
-
- @VisibleForTesting
- protected DisplayController(Context context) {
+ @Inject
+ protected DisplayController(@ApplicationContext Context context,
+ WindowManagerProxy wmProxy,
+ LauncherPrefs prefs,
+ DaggerSingletonTracker lifecycle) {
mContext = context;
- mDM = context.getSystemService(DisplayManager.class);
+ mWMProxy = wmProxy;
if (enableTaskbarPinning()) {
- attachTaskbarPinningSharedPreferenceChangeListener(mContext);
+ LauncherPrefChangeListener prefListener = key -> {
+ boolean isTaskbarPinningChanged = TASKBAR_PINNING_KEY.equals(key)
+ && mInfo.mIsTaskbarPinned != prefs.get(TASKBAR_PINNING);
+ boolean isTaskbarPinningDesktopModeChanged =
+ TASKBAR_PINNING_DESKTOP_MODE_KEY.equals(key)
+ && mInfo.mIsTaskbarPinnedInDesktopMode != prefs.get(
+ TASKBAR_PINNING_IN_DESKTOP_MODE);
+ if (isTaskbarPinningChanged || isTaskbarPinningDesktopModeChanged) {
+ notifyConfigChange();
+ }
+ };
+
+ prefs.addListener(prefListener, TASKBAR_PINNING);
+ prefs.addListener(prefListener, TASKBAR_PINNING_IN_DESKTOP_MODE);
+ lifecycle.addCloseable(() -> prefs.removeListener(
+ prefListener, TASKBAR_PINNING, TASKBAR_PINNING_IN_DESKTOP_MODE));
}
- Display display = mDM.getDisplay(DEFAULT_DISPLAY);
+ Display display = context.getSystemService(DisplayManager.class)
+ .getDisplay(DEFAULT_DISPLAY);
mWindowContext = mContext.createWindowContext(display, TYPE_APPLICATION, null);
mWindowContext.registerComponentCallbacks(this);
// Initialize navigation mode change listener
mReceiver.registerPkgActions(mContext, TARGET_OVERLAY_PACKAGE, ACTION_OVERLAY_CHANGED);
- WindowManagerProxy wmProxy = WindowManagerProxy.INSTANCE.get(context);
mInfo = new Info(mWindowContext, wmProxy,
wmProxy.estimateInternalDisplayBounds(mWindowContext));
+ wmProxy.registerDesktopVisibilityListener(this);
FileLog.i(TAG, "(CTOR) perDisplayBounds: " + mInfo.mPerDisplayBounds);
- }
- private void attachTaskbarPinningSharedPreferenceChangeListener(Context context) {
- mTaskbarPinningPreferenceChangeListener = key -> {
- LauncherPrefs prefs = LauncherPrefs.get(mContext);
- boolean isTaskbarPinningChanged = TASKBAR_PINNING_KEY.equals(key)
- && mInfo.mIsTaskbarPinned != prefs.get(TASKBAR_PINNING);
- boolean isTaskbarPinningDesktopModeChanged =
- TASKBAR_PINNING_DESKTOP_MODE_KEY.equals(key)
- && mInfo.mIsTaskbarPinnedInDesktopMode != prefs.get(
- TASKBAR_PINNING_IN_DESKTOP_MODE);
- if (isTaskbarPinningChanged || isTaskbarPinningDesktopModeChanged) {
- notifyConfigChange();
- }
- };
-
- LauncherPrefs.get(context).addListener(
- mTaskbarPinningPreferenceChangeListener, TASKBAR_PINNING);
- LauncherPrefs.get(context).addListener(
- mTaskbarPinningPreferenceChangeListener, TASKBAR_PINNING_IN_DESKTOP_MODE);
+ lifecycle.addCloseable(() -> {
+ mDestroyed = true;
+ mWindowContext.unregisterComponentCallbacks(this);
+ mReceiver.unregisterReceiverSafely(mContext);
+ wmProxy.unregisterDesktopVisibilityListener(this);
+ });
}
/**
@@ -207,20 +219,8 @@
}
@Override
- public void close() {
- mDestroyed = true;
- if (enableTaskbarPinning()) {
- LauncherPrefs.get(mContext).removeListener(
- mTaskbarPinningPreferenceChangeListener, TASKBAR_PINNING);
- LauncherPrefs.get(mContext).removeListener(
- mTaskbarPinningPreferenceChangeListener, TASKBAR_PINNING_IN_DESKTOP_MODE);
- }
- if (mWindowContext != null) {
- mWindowContext.unregisterComponentCallbacks(this);
- } else {
- // TODO: unregister broadcast receiver
- }
- mReceiver.unregisterReceiverSafely(mContext);
+ public void onDesktopVisibilityChanged(boolean visible) {
+ notifyConfigChange();
}
/**
@@ -256,7 +256,7 @@
|| !mInfo.mScreenSizeDp.equals(
new PortraitSize(config.screenHeightDp, config.screenWidthDp))
|| mWindowContext.getDisplay().getRotation() != mInfo.rotation
- || WindowManagerProxy.INSTANCE.get(mContext).showLockedTaskbarOnHome(mWindowContext)
+ || mWMProxy.showLockedTaskbarOnHome(mWindowContext)
!= mInfo.showLockedTaskbarOnHome()) {
notifyConfigChange();
}
@@ -283,17 +283,16 @@
@AnyThread
public void notifyConfigChange() {
- WindowManagerProxy wmProxy = WindowManagerProxy.INSTANCE.get(mContext);
Info oldInfo = mInfo;
Context displayInfoContext = mWindowContext;
- Info newInfo = new Info(displayInfoContext, wmProxy, oldInfo.mPerDisplayBounds);
+ Info newInfo = new Info(displayInfoContext, mWMProxy, oldInfo.mPerDisplayBounds);
if (newInfo.densityDpi != oldInfo.densityDpi || newInfo.fontScale != oldInfo.fontScale
|| newInfo.getNavigationMode() != oldInfo.getNavigationMode()) {
// Cache may not be valid anymore, recreate without cache
- newInfo = new Info(displayInfoContext, wmProxy,
- wmProxy.estimateInternalDisplayBounds(displayInfoContext));
+ newInfo = new Info(displayInfoContext, mWMProxy,
+ mWMProxy.estimateInternalDisplayBounds(displayInfoContext));
}
int change = 0;
diff --git a/src/com/android/launcher3/util/LayoutImportExportHelper.kt b/src/com/android/launcher3/util/LayoutImportExportHelper.kt
new file mode 100644
index 0000000..4033f60
--- /dev/null
+++ b/src/com/android/launcher3/util/LayoutImportExportHelper.kt
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util
+
+import android.app.blob.BlobHandle.createWithSha256
+import android.app.blob.BlobStoreManager
+import android.content.Context
+import android.os.ParcelFileDescriptor.AutoCloseOutputStream
+import android.provider.Settings.Secure
+import com.android.launcher3.AutoInstallsLayout
+import com.android.launcher3.LauncherAppState
+import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP
+import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_FOLDER
+import com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_LABEL
+import com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_TAG
+import com.android.launcher3.LauncherSettings.Settings.LAYOUT_PROVIDER_KEY
+import com.android.launcher3.LauncherSettings.Settings.createBlobProviderKey
+import com.android.launcher3.model.data.FolderInfo
+import com.android.launcher3.model.data.ItemInfo
+import com.android.launcher3.model.data.LauncherAppWidgetInfo
+import com.android.launcher3.pm.UserCache
+import com.android.launcher3.shortcuts.ShortcutKey
+import com.android.launcher3.util.Executors.MAIN_EXECUTOR
+import com.android.launcher3.util.Executors.MODEL_EXECUTOR
+import com.android.launcher3.util.Executors.ORDERED_BG_EXECUTOR
+import java.nio.charset.StandardCharsets
+import java.security.MessageDigest
+import java.util.concurrent.CompletableFuture
+
+object LayoutImportExportHelper {
+ fun exportModelDbAsXmlFuture(context: Context): CompletableFuture<String> {
+ val future = CompletableFuture<String>()
+ exportModelDbAsXml(context) { xmlString -> future.complete(xmlString) }
+ return future
+ }
+
+ fun exportModelDbAsXml(context: Context, callback: (String) -> Unit) {
+ val model = LauncherAppState.getInstance(context).model
+
+ model.enqueueModelUpdateTask { _, dataModel, _ ->
+ val builder = LauncherLayoutBuilder()
+ dataModel.workspaceItems.forEach { info ->
+ val loc =
+ when (info.container) {
+ CONTAINER_DESKTOP ->
+ builder.atWorkspace(info.cellX, info.cellY, info.screenId)
+
+ CONTAINER_HOTSEAT -> builder.atHotseat(info.screenId)
+ else -> return@forEach
+ }
+ loc.addItem(context, info)
+ }
+ dataModel.appWidgets.forEach { info ->
+ builder.atWorkspace(info.cellX, info.cellY, info.screenId).addItem(context, info)
+ }
+
+ val layoutXml = builder.build()
+ callback(layoutXml)
+ }
+ }
+
+ fun importModelFromXml(context: Context, xmlString: String) {
+ importModelFromXml(context, xmlString.toByteArray(StandardCharsets.UTF_8))
+ }
+
+ fun importModelFromXml(context: Context, data: ByteArray) {
+ val model = LauncherAppState.getInstance(context).model
+
+ val digest = MessageDigest.getInstance("SHA-256").digest(data)
+ val handle = createWithSha256(digest, LAYOUT_DIGEST_LABEL, 0, LAYOUT_DIGEST_TAG)
+ val blobManager = context.getSystemService(BlobStoreManager::class.java)!!
+
+ val resolver = context.contentResolver
+
+ blobManager.openSession(blobManager.createSession(handle)).use { session ->
+ AutoCloseOutputStream(session.openWrite(0, -1)).use { it.write(data) }
+ session.allowPublicAccess()
+
+ session.commit(ORDERED_BG_EXECUTOR) {
+ Secure.putString(resolver, LAYOUT_PROVIDER_KEY, createBlobProviderKey(digest))
+
+ MODEL_EXECUTOR.submit { model.modelDbController.createEmptyDB() }.get()
+ MAIN_EXECUTOR.submit { model.forceReload() }.get()
+ MODEL_EXECUTOR.submit {}.get()
+ Secure.putString(resolver, LAYOUT_PROVIDER_KEY, null)
+ }
+ }
+ }
+
+ private fun LauncherLayoutBuilder.ItemTarget.addItem(context: Context, info: ItemInfo) {
+ val userType: String? =
+ when (UserCache.INSTANCE.get(context).getUserInfo(info.user).type) {
+ UserIconInfo.TYPE_WORK -> AutoInstallsLayout.USER_TYPE_WORK
+ UserIconInfo.TYPE_CLONED -> AutoInstallsLayout.USER_TYPE_CLONED
+ else -> null
+ }
+ when (info.itemType) {
+ ITEM_TYPE_APPLICATION ->
+ info.targetComponent?.let { c -> putApp(c.packageName, c.className, userType) }
+ ITEM_TYPE_DEEP_SHORTCUT ->
+ ShortcutKey.fromItemInfo(info).let { key ->
+ putShortcut(key.packageName, key.id, userType)
+ }
+ ITEM_TYPE_FOLDER ->
+ (info as FolderInfo).let { folderInfo ->
+ putFolder(folderInfo.title?.toString() ?: "").also { folderBuilder ->
+ folderInfo.getContents().forEach { folderContent ->
+ folderBuilder.addItem(context, folderContent)
+ }
+ }
+ }
+ ITEM_TYPE_APPWIDGET ->
+ putWidget(
+ (info as LauncherAppWidgetInfo).providerName.packageName,
+ info.providerName.className,
+ info.spanX,
+ info.spanY,
+ userType,
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/util/MultiTranslateDelegate.java b/src/com/android/launcher3/util/MultiTranslateDelegate.java
index 38c87c8..ce006c4 100644
--- a/src/com/android/launcher3/util/MultiTranslateDelegate.java
+++ b/src/com/android/launcher3/util/MultiTranslateDelegate.java
@@ -38,6 +38,7 @@
public static final int INDEX_TASKBAR_REVEAL_ANIM = 4;
public static final int INDEX_TASKBAR_PINNING_ANIM = 5;
public static final int INDEX_NAV_BAR_ANIM = 6;
+ public static final int INDEX_BUBBLE_BAR_ANIM = 7;
// Affect all items inside of a MultipageCellLayout
public static final int INDEX_CELLAYOUT_MULTIPAGE_SPACING = 3;
@@ -48,7 +49,7 @@
// Specific for hotseat items when adjusting for bubbles
public static final int INDEX_BUBBLE_ADJUSTMENT_ANIM = 3;
- public static final int COUNT = 7;
+ public static final int COUNT = 8;
private final MultiPropertyFactory<View> mTranslationX;
private final MultiPropertyFactory<View> mTranslationY;
diff --git a/src/com/android/launcher3/util/Themes.java b/src/com/android/launcher3/util/Themes.java
index 104040a..927a2a4 100644
--- a/src/com/android/launcher3/util/Themes.java
+++ b/src/com/android/launcher3/util/Themes.java
@@ -19,8 +19,6 @@
import static android.app.WallpaperColors.HINT_SUPPORTS_DARK_TEXT;
import static android.app.WallpaperColors.HINT_SUPPORTS_DARK_THEME;
-import static com.android.launcher3.LauncherPrefs.THEMED_ICONS;
-
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
@@ -32,7 +30,6 @@
import androidx.annotation.ColorInt;
-import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.icons.GraphicsUtils;
@@ -44,8 +41,6 @@
@SuppressWarnings("NewApi")
public class Themes {
- public static final String KEY_THEMED_ICONS = "themed_icons";
-
/** Gets the WallpaperColorHints and then uses those to get the correct activity theme res. */
public static int getActivityThemeRes(Context context) {
return getActivityThemeRes(context, WallpaperColorHints.get(context).getHints());
@@ -64,13 +59,6 @@
}
}
- /**
- * Returns true if workspace icon theming is enabled
- */
- public static boolean isThemedIconEnabled(Context context) {
- return LauncherPrefs.get(context).get(THEMED_ICONS);
- }
-
public static String getDefaultBodyFont(Context context) {
TypedArray ta = context.obtainStyledAttributes(android.R.style.TextAppearance_DeviceDefault,
new int[]{android.R.attr.fontFamily});
diff --git a/src/com/android/launcher3/util/coroutines/DispatcherProvider.kt b/src/com/android/launcher3/util/coroutines/DispatcherProvider.kt
index 8877535..1f01b07 100644
--- a/src/com/android/launcher3/util/coroutines/DispatcherProvider.kt
+++ b/src/com/android/launcher3/util/coroutines/DispatcherProvider.kt
@@ -33,7 +33,7 @@
override val default: CoroutineDispatcher = Dispatchers.Default
override val background: CoroutineDispatcher = bgDispatcher
- override val main: CoroutineDispatcher = Dispatchers.Main
+ override val main: CoroutineDispatcher = Dispatchers.Main.immediate
override val unconfined: CoroutineDispatcher = Dispatchers.Unconfined
}
diff --git a/src/com/android/launcher3/util/window/WindowManagerProxy.java b/src/com/android/launcher3/util/window/WindowManagerProxy.java
index 1d9751e..e568eed 100644
--- a/src/com/android/launcher3/util/window/WindowManagerProxy.java
+++ b/src/com/android/launcher3/util/window/WindowManagerProxy.java
@@ -485,4 +485,21 @@
return new Rect(cutout.getSafeInsetLeft(), cutout.getSafeInsetTop(),
cutout.getSafeInsetRight(), cutout.getSafeInsetBottom());
}
+
+ /** Registers a listener for Taskbar changes in Desktop Mode. */
+ public void registerDesktopVisibilityListener(DesktopVisibilityListener listener) { }
+
+ /** Removes a previously registered listener for Taskbar changes in Desktop Mode. */
+ public void unregisterDesktopVisibilityListener(DesktopVisibilityListener listener) { }
+
+ /** A listener for when the user enters/exits Desktop Mode. */
+ public interface DesktopVisibilityListener {
+ /**
+ * Callback for when the user enters or exits Desktop Mode
+ *
+ * @param visible whether Desktop Mode is now visible
+ */
+ void onDesktopVisibilityChanged(boolean visible);
+ }
+
}
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index b8481c5..b164b7f 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -105,13 +105,6 @@
return null;
}
- /**
- * For items with tree hierarchy, notifies the activity to invalidate the parent when a root
- * is invalidated
- * @param info info associated with a root node.
- */
- default void invalidateParent(ItemInfo info) { }
-
default AccessibilityDelegate getAccessibilityDelegate() {
return null;
}
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index 6739387..22857b1 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -465,8 +465,7 @@
bounds.inset(blurSizeOutline / 2, blurSizeOutline / 2);
try (LauncherIcons li = LauncherIcons.obtain(l)) {
- Utilities.scaleRectAboutCenter(bounds, li.getNormalizer().getScale(drawable, null,
- null, null));
+ Utilities.scaleRectAboutCenter(bounds, li.getNormalizer().getScale(drawable));
}
bounds.inset(
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
index b877d7a..94ae69c 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
@@ -1,5 +1,7 @@
package com.android.launcher3.widget;
+import static com.android.launcher3.InvariantDeviceProfile.TYPE_PHONE;
+
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.content.Context;
@@ -14,6 +16,7 @@
import androidx.annotation.Nullable;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Flags;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.icons.cache.BaseIconCache;
@@ -108,6 +111,13 @@
Point cellSize = new Point();
for (DeviceProfile dp : idp.supportedProfiles) {
+ // On phones we no longer support regular landscape, only fixed landscape for this
+ // reason we don't need to take regular landscape into account in phones
+ if (Flags.oneGridSpecs() && dp.inv.deviceType == TYPE_PHONE
+ && dp.inv.isFixedLandscape != dp.isLandscape) {
+ continue;
+ }
+
dp.getCellSize(cellSize);
Rect widgetPadding = dp.widgetPadding;
diff --git a/src_no_quickstep/com/android/launcher3/dagger/LauncherAppModule.java b/src_no_quickstep/com/android/launcher3/dagger/LauncherAppModule.java
deleted file mode 100644
index f7b8489..0000000
--- a/src_no_quickstep/com/android/launcher3/dagger/LauncherAppModule.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.dagger;
-
-import dagger.Module;
-
-@Module
-public class LauncherAppModule { }
diff --git a/quickstep/dagger/com/android/launcher3/dagger/LauncherAppModule.java b/src_no_quickstep/com/android/launcher3/dagger/Modules.kt
similarity index 66%
copy from quickstep/dagger/com/android/launcher3/dagger/LauncherAppModule.java
copy to src_no_quickstep/com/android/launcher3/dagger/Modules.kt
index 1711fc1..dab33a0 100644
--- a/quickstep/dagger/com/android/launcher3/dagger/LauncherAppModule.java
+++ b/src_no_quickstep/com/android/launcher3/dagger/Modules.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,11 +14,14 @@
* limitations under the License.
*/
-package com.android.launcher3.dagger;
+package com.android.launcher3.dagger
-import com.android.quickstep.dagger.QuickStepModule;
+import dagger.Module
-import dagger.Module;
+private object Modules {}
-@Module(includes = QuickStepModule.class)
-public class LauncherAppModule {}
+@Module abstract class WindowManagerProxyModule {}
+
+@Module abstract class ApiWrapperModule {}
+
+@Module abstract class PluginManagerWrapperModule {}
diff --git a/tests/Android.bp b/tests/Android.bp
index f666bba..4bc654c 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -226,6 +226,5 @@
],
instrumentation_for: "Launcher3",
plugins: ["dagger2-compiler"],
- upstream: true,
strict_mode: false,
}
diff --git a/tests/Launcher3Tests.xml b/tests/Launcher3Tests.xml
index a876860..56dd6a4 100644
--- a/tests/Launcher3Tests.xml
+++ b/tests/Launcher3Tests.xml
@@ -50,6 +50,8 @@
<option name="run-command" value="settings put system show_touches 1" />
<option name="run-command" value="setprop pixel_legal_joint_permission_v2 true" />
+
+ <option name="run-command" value="settings put global verifier_verify_adb_installs 0" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt b/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
index 8598917..9c64ec9 100644
--- a/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
@@ -29,9 +29,9 @@
import androidx.test.core.app.ApplicationProvider.getApplicationContext
import androidx.test.platform.app.InstrumentationRegistry
import com.android.launcher3.dagger.LauncherAppComponent
-import com.android.launcher3.dagger.LauncherAppModule
import com.android.launcher3.dagger.LauncherAppSingleton
import com.android.launcher3.testing.shared.ResourceUtils
+import com.android.launcher3.util.AllModulesMinusWMProxy
import com.android.launcher3.util.DisplayController
import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext
import com.android.launcher3.util.NavigationMode
@@ -69,7 +69,7 @@
protected lateinit var context: SandboxContext
protected open val runningContext: Context = getApplicationContext()
private val displayController: DisplayController = mock()
- private val windowManagerProxy: MyWmProxy = mock()
+ private val windowManagerProxy: WindowManagerProxy = mock()
private val launcherPrefs: LauncherPrefs = mock()
@get:Rule val setFlagsRule = SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT)
@@ -284,7 +284,6 @@
isFixedLandscape: Boolean = false,
) {
setFlagsRule.setFlags(true, Flags.FLAG_ENABLE_TWOLINE_TOGGLE)
- LauncherPrefs.get(testContext).put(LauncherPrefs.ENABLE_TWOLINE_ALLAPPS_TOGGLE, true)
val windowsBounds = perDisplayBoundsCache[displayInfo]!!
val realBounds = windowsBounds[rotation]
whenever(windowManagerProxy.getDisplayInfo(any())).thenReturn(displayInfo)
@@ -310,10 +309,11 @@
val configurationContext = runningContext.createConfigurationContext(config)
context = SandboxContext(configurationContext)
context.initDaggerComponent(
- DaggerAbsDPTestSandboxComponent.builder().bindWMProxy(windowManagerProxy)
+ DaggerAbsDPTestSandboxComponent.builder()
+ .bindWMProxy(windowManagerProxy)
+ .bindLauncherPrefs(launcherPrefs)
+ .bindDisplayController(displayController)
)
- context.putObject(DisplayController.INSTANCE, displayController)
- context.putObject(LauncherPrefs.INSTANCE, launcherPrefs)
whenever(launcherPrefs.get(LauncherPrefs.TASKBAR_PINNING)).thenReturn(false)
whenever(launcherPrefs.get(LauncherPrefs.TASKBAR_PINNING_IN_DESKTOP_MODE)).thenReturn(true)
@@ -322,6 +322,7 @@
whenever(launcherPrefs.get(LauncherPrefs.DEVICE_TYPE)).thenReturn(-1)
whenever(launcherPrefs.get(LauncherPrefs.WORKSPACE_SIZE)).thenReturn("")
whenever(launcherPrefs.get(LauncherPrefs.DB_FILE)).thenReturn("")
+ whenever(launcherPrefs.get(LauncherPrefs.ENABLE_TWOLINE_ALLAPPS_TOGGLE)).thenReturn(true)
val info = spy(DisplayController.Info(context, windowManagerProxy, perDisplayBoundsCache))
whenever(displayController.info).thenReturn(info)
whenever(info.isTransientTaskbar).thenReturn(isGestureMode)
@@ -363,17 +364,17 @@
}
}
-class MyWmProxy : WindowManagerProxy()
-
@LauncherAppSingleton
-@Component(modules = [LauncherAppModule::class])
+@Component(modules = [AllModulesMinusWMProxy::class])
interface AbsDPTestSandboxComponent : LauncherAppComponent {
- override fun getWmProxy(): MyWmProxy
-
@Component.Builder
interface Builder : LauncherAppComponent.Builder {
- @BindsInstance fun bindWMProxy(proxy: MyWmProxy): Builder
+ @BindsInstance fun bindWMProxy(proxy: WindowManagerProxy): Builder
+
+ @BindsInstance fun bindLauncherPrefs(prefs: LauncherPrefs): Builder
+
+ @BindsInstance fun bindDisplayController(displayController: DisplayController): Builder
override fun build(): AbsDPTestSandboxComponent
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/AutoInstallsLayoutTest.kt b/tests/multivalentTests/src/com/android/launcher3/AutoInstallsLayoutTest.kt
index fcbb94b..97ecafe 100644
--- a/tests/multivalentTests/src/com/android/launcher3/AutoInstallsLayoutTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/AutoInstallsLayoutTest.kt
@@ -42,10 +42,10 @@
import com.android.launcher3.LauncherSettings.Favorites.SPANY
import com.android.launcher3.LauncherSettings.Favorites._ID
import com.android.launcher3.dagger.LauncherAppComponent
-import com.android.launcher3.dagger.LauncherAppModule
import com.android.launcher3.dagger.LauncherAppSingleton
import com.android.launcher3.model.data.AppInfo
import com.android.launcher3.pm.UserCache
+import com.android.launcher3.util.AllModulesMinusApiWrapper
import com.android.launcher3.util.ApiWrapper
import com.android.launcher3.util.Executors
import com.android.launcher3.util.LauncherLayoutBuilder
@@ -224,17 +224,15 @@
}
}
-class MyApiWrapper : ApiWrapper(null) {}
+class MyApiWrapper : ApiWrapper(null)
@LauncherAppSingleton
-@Component(modules = [LauncherAppModule::class])
+@Component(modules = [AllModulesMinusApiWrapper::class])
interface AutoInstallsLayoutTestComponent : LauncherAppComponent {
- override fun getApiWrapper(): MyApiWrapper
-
@Component.Builder
interface Builder : LauncherAppComponent.Builder {
- @BindsInstance fun bindApiWrapper(wrapper: MyApiWrapper): Builder
+ @BindsInstance fun bindApiWrapper(wrapper: ApiWrapper): Builder
override fun build(): AutoInstallsLayoutTestComponent
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/FakeInvariantDeviceProfileTest.kt b/tests/multivalentTests/src/com/android/launcher3/FakeInvariantDeviceProfileTest.kt
index 954dc8f..bfbdb18 100644
--- a/tests/multivalentTests/src/com/android/launcher3/FakeInvariantDeviceProfileTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/FakeInvariantDeviceProfileTest.kt
@@ -58,7 +58,7 @@
@Rule @JvmField val limitDevicesRule = LimitDevicesRule()
@Before
- fun setUp() {
+ open fun setUp() {
context = ApplicationProvider.getApplicationContext()
// make sure to reset values
useTwoPanels = false
@@ -83,7 +83,7 @@
protected fun initializeVarsForPhone(
isGestureMode: Boolean = true,
- isVerticalBar: Boolean = false
+ isVerticalBar: Boolean = false,
) {
val (x, y) = if (isVerticalBar) Pair(2400, 1080) else Pair(1080, 2400)
@@ -94,8 +94,8 @@
if (isVerticalBar) 118 else 0,
if (isVerticalBar) 74 else 118,
if (!isGestureMode && isVerticalBar) 126 else 0,
- if (isGestureMode) 63 else if (isVerticalBar) 0 else 126
- )
+ if (isGestureMode) 63 else if (isVerticalBar) 0 else 126,
+ ),
)
whenever(info.isTablet(any())).thenReturn(false)
@@ -121,7 +121,7 @@
PointF(80f, 104f),
PointF(80f, 104f),
PointF(80f, 104f),
- PointF(80f, 104f)
+ PointF(80f, 104f),
)
.toTypedArray()
@@ -143,7 +143,7 @@
PointF(80f, 104f),
PointF(80f, 104f),
PointF(80f, 104f),
- PointF(80f, 104f)
+ PointF(80f, 104f),
)
.toTypedArray()
allAppsIconSize = floatArrayOf(60f, 60f, 60f, 60f)
@@ -174,7 +174,7 @@
protected fun initializeVarsForTablet(
isLandscape: Boolean = false,
- isGestureMode: Boolean = true
+ isGestureMode: Boolean = true,
) {
val (x, y) = if (isLandscape) Pair(2560, 1600) else Pair(1600, 2560)
@@ -203,7 +203,7 @@
PointF(102f, 120f),
PointF(120f, 104f),
PointF(102f, 120f),
- PointF(102f, 120f)
+ PointF(102f, 120f),
)
.toTypedArray()
@@ -225,7 +225,7 @@
PointF(96f, 142f),
PointF(126f, 126f),
PointF(96f, 142f),
- PointF(96f, 142f)
+ PointF(96f, 142f),
)
.toTypedArray()
allAppsIconSize = FloatArray(4) { 60f }
@@ -288,7 +288,7 @@
PointF(80f, 104f),
PointF(80f, 104f),
PointF(68f, 116f),
- PointF(80f, 102f)
+ PointF(80f, 102f),
)
.toTypedArray()
diff --git a/tests/multivalentTests/src/com/android/launcher3/FakeLauncherPrefs.kt b/tests/multivalentTests/src/com/android/launcher3/FakeLauncherPrefs.kt
index 946bbc5..7573d2f 100644
--- a/tests/multivalentTests/src/com/android/launcher3/FakeLauncherPrefs.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/FakeLauncherPrefs.kt
@@ -17,61 +17,24 @@
package com.android.launcher3
import android.content.Context
-import com.android.launcher3.util.Executors.MAIN_EXECUTOR
+import android.content.Context.MODE_PRIVATE
+import android.content.SharedPreferences
+import com.android.launcher3.dagger.ApplicationContext
+import com.android.launcher3.dagger.LauncherAppSingleton
+import java.io.File
+import javax.inject.Inject
/** Emulates Launcher preferences for a test environment. */
-class FakeLauncherPrefs(private val context: Context) : LauncherPrefs() {
- private val prefsMap = mutableMapOf<String, Any>()
- private val listeners = mutableSetOf<LauncherPrefChangeListener>()
+@LauncherAppSingleton
+class FakeLauncherPrefs @Inject constructor(@ApplicationContext context: Context) :
+ LauncherPrefs(context) {
- @Suppress("UNCHECKED_CAST")
- override fun <T> get(item: ContextualItem<T>): T {
- return prefsMap.getOrDefault(item.sharedPrefKey, item.defaultValueFromContext(context)) as T
- }
+ private val backingPrefs =
+ context.getSharedPreferences(
+ File.createTempFile("fake-pref", ".xml", context.filesDir),
+ MODE_PRIVATE,
+ )
- @Suppress("UNCHECKED_CAST")
- override fun <T> get(item: ConstantItem<T>): T {
- return prefsMap.getOrDefault(item.sharedPrefKey, item.defaultValue) as T
- }
-
- override fun put(vararg itemsToValues: Pair<Item, Any>) = putSync(*itemsToValues)
-
- override fun <T : Any> put(item: Item, value: T) = putSync(item to value)
-
- override fun putSync(vararg itemsToValues: Pair<Item, Any>) {
- itemsToValues
- .map { (i, v) -> i.sharedPrefKey to v }
- .forEach { (k, v) ->
- prefsMap[k] = v
- notifyChange(k)
- }
- }
-
- override fun addListener(listener: LauncherPrefChangeListener, vararg items: Item) {
- listeners.add(listener)
- }
-
- override fun removeListener(listener: LauncherPrefChangeListener, vararg items: Item) {
- listeners.remove(listener)
- }
-
- override fun has(vararg items: Item) = items.all { it.sharedPrefKey in prefsMap }
-
- override fun remove(vararg items: Item) = removeSync(*items)
-
- override fun removeSync(vararg items: Item) {
- items
- .filter { it.sharedPrefKey in prefsMap }
- .forEach {
- prefsMap.remove(it.sharedPrefKey)
- notifyChange(it.sharedPrefKey)
- }
- }
-
- override fun close() = Unit
-
- private fun notifyChange(key: String) {
- // Mimics SharedPreferencesImpl#notifyListeners main thread dispatching.
- MAIN_EXECUTOR.execute { listeners.forEach { it.onPrefChanged(key) } }
- }
+ override val Item.sharedPrefs: SharedPreferences
+ get() = backingPrefs
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/FakeLauncherPrefsTest.kt b/tests/multivalentTests/src/com/android/launcher3/FakeLauncherPrefsTest.kt
index 2463c93..c57c86f 100644
--- a/tests/multivalentTests/src/com/android/launcher3/FakeLauncherPrefsTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/FakeLauncherPrefsTest.kt
@@ -128,7 +128,7 @@
val listener = LauncherPrefChangeListener { changedKey = it }
launcherPrefs.addListener(listener, TEST_CONSTANT_ITEM)
- launcherPrefs.removeListener(listener)
+ launcherPrefs.removeListener(listener, TEST_CONSTANT_ITEM)
getInstrumentation().runOnMainSync { launcherPrefs.put(TEST_CONSTANT_ITEM, true) }
assertThat(changedKey).isNull()
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java b/tests/multivalentTests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java
index a9082e2..47110fa 100644
--- a/tests/multivalentTests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java
+++ b/tests/multivalentTests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java
@@ -61,9 +61,11 @@
ModelDbController controller = model.getModelDbController();
// Migrate any previous data so that the DB state is correct
if (Flags.gridMigrationRefactor()) {
- controller.attemptMigrateDb(null /* restoreEventLogger */);
+ controller.attemptMigrateDb(
+ null /* restoreEventLogger */, model.getModelDelegate());
} else {
- controller.tryMigrateDB(null /* restoreEventLogger */);
+ controller.tryMigrateDB(null /* restoreEventLogger */,
+ model.getModelDelegate());
}
// Create DB again to load fresh data
diff --git a/tests/multivalentTests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java b/tests/multivalentTests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java
index a62258c..bf8e8b1 100644
--- a/tests/multivalentTests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java
@@ -17,9 +17,6 @@
import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
-import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
-import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -41,7 +38,6 @@
import com.android.launcher3.celllayout.testgenerator.RandomMultiBoardGenerator;
import com.android.launcher3.util.ActivityContextWrapper;
import com.android.launcher3.util.rule.TestStabilityRule;
-import com.android.launcher3.util.rule.TestStabilityRule.Stability;
import com.android.launcher3.views.DoubleShadowBubbleTextView;
import org.junit.Rule;
@@ -82,7 +78,6 @@
* This test reads existing test cases and makes sure the CellLayout produces the same
* output for each of them for a given input.
*/
- @Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT)
@Test
public void testAllCases() throws IOException {
List<ReorderAlgorithmUnitTestCase> testCases = getTestCases(
@@ -125,7 +120,6 @@
/**
* Same as above but testing the Multipage CellLayout.
*/
- @Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT)
@Test
public void generateValidTests_Multi() {
Random generator = new Random(SEED);
diff --git a/tests/multivalentTests/src/com/android/launcher3/celllayout/board/TestWorkspaceBuilder.kt b/tests/multivalentTests/src/com/android/launcher3/celllayout/board/TestWorkspaceBuilder.kt
index 8952b85..2e556e8 100644
--- a/tests/multivalentTests/src/com/android/launcher3/celllayout/board/TestWorkspaceBuilder.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/celllayout/board/TestWorkspaceBuilder.kt
@@ -80,6 +80,15 @@
)
}
+ /**
+ * Sets the test app for app icons to the specified Component
+ *
+ * @param testAppComponent ComponentName to use for app icons
+ */
+ fun setTestAppComponent(testAppComponent: ComponentName) {
+ appComponentName = testAppComponent
+ }
+
private fun addCorrespondingWidgetRect(
widgetRect: WidgetRect,
transaction: FavoriteItemsTransaction,
diff --git a/tests/multivalentTests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt b/tests/multivalentTests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt
index 7f481b7..553d08c 100644
--- a/tests/multivalentTests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt
@@ -17,21 +17,21 @@
package com.android.launcher3.folder
import android.R
-import android.content.Context
import android.graphics.Bitmap
import android.os.Process
-import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
-import com.android.launcher3.LauncherPrefs.Companion.THEMED_ICONS
-import com.android.launcher3.LauncherPrefs.Companion.get
+import com.android.launcher3.LauncherAppState
import com.android.launcher3.graphics.PreloadIconDrawable
+import com.android.launcher3.graphics.ThemeManager
+import com.android.launcher3.icons.BitmapInfo
import com.android.launcher3.icons.FastBitmapDrawable
+import com.android.launcher3.icons.IconCache
+import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver
+import com.android.launcher3.icons.PlaceHolderIconDrawable
import com.android.launcher3.icons.UserBadgeDrawable
import com.android.launcher3.icons.mono.MonoThemedBitmap
import com.android.launcher3.model.data.FolderInfo
-import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_ARCHIVED
import com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE
import com.android.launcher3.model.data.WorkspaceItemInfo
@@ -40,6 +40,7 @@
import com.android.launcher3.util.FlagOp
import com.android.launcher3.util.LauncherLayoutBuilder
import com.android.launcher3.util.LauncherModelHelper
+import com.android.launcher3.util.LauncherModelHelper.SandboxModelContext
import com.android.launcher3.util.TestUtil
import com.android.launcher3.util.UserIconInfo
import com.google.common.truth.Truth.assertThat
@@ -47,6 +48,13 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
/** Tests for [PreviewItemManager] */
@SmallTest
@@ -54,22 +62,30 @@
class PreviewItemManagerTest {
private lateinit var previewItemManager: PreviewItemManager
- private lateinit var context: Context
- private lateinit var folderItems: ArrayList<ItemInfo>
+ private lateinit var context: SandboxModelContext
+ private lateinit var folderItems: ArrayList<WorkspaceItemInfo>
private lateinit var modelHelper: LauncherModelHelper
private lateinit var folderIcon: FolderIcon
+ private lateinit var iconCache: IconCache
private var defaultThemedIcons = false
+ private val themeManager: ThemeManager
+ get() = ThemeManager.INSTANCE.get(context)
+
@Before
fun setup() {
- getInstrumentation().runOnMainSync {
- folderIcon =
- FolderIcon(ActivityContextWrapper(ApplicationProvider.getApplicationContext()))
- }
- context = getInstrumentation().targetContext
- previewItemManager = PreviewItemManager(folderIcon)
modelHelper = LauncherModelHelper()
+ context = modelHelper.sandboxContext
+ folderIcon = FolderIcon(ActivityContextWrapper(context))
+
+ val app = spy(LauncherAppState.getInstance(context))
+ iconCache = spy(app.iconCache)
+ doReturn(iconCache).whenever(app).iconCache
+ context.putObject(LauncherAppState.INSTANCE, app)
+ doReturn(null).whenever(iconCache).updateIconInBackground(any(), any())
+
+ previewItemManager = PreviewItemManager(folderIcon)
modelHelper
.setupDefaultLayoutProvider(
LauncherLayoutBuilder()
@@ -82,47 +98,49 @@
.build()
)
.loadModelSync()
- folderItems = modelHelper.bgDataModel.collections.valueAt(0).getContents()
+
+ // Use getAppContents() to "cast" contents to WorkspaceItemInfo so we can set bitmaps
+ folderItems = modelHelper.bgDataModel.collections.valueAt(0).getAppContents()
folderIcon.mInfo = modelHelper.bgDataModel.collections.valueAt(0) as FolderInfo
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.
- folderApps[0].bitmap.themedBitmap =
+ folderItems[0].bitmap.themedBitmap =
MonoThemedBitmap(
- folderApps[0].bitmap.icon,
+ folderItems[0].bitmap.icon,
Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888),
)
// Set second icon to be non-themed.
- folderApps[1].bitmap.themedBitmap = null
+ folderItems[1].bitmap.themedBitmap = null
// Set third icon to be themed with badge.
- folderApps[2].bitmap.themedBitmap =
+ folderItems[2].bitmap.themedBitmap =
MonoThemedBitmap(
- folderApps[2].bitmap.icon,
+ folderItems[2].bitmap.icon,
Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888),
)
- folderApps[2].bitmap = folderApps[2].bitmap.withFlags(profileFlagOp(UserIconInfo.TYPE_WORK))
+ folderItems[2].bitmap =
+ folderItems[2].bitmap.withFlags(profileFlagOp(UserIconInfo.TYPE_WORK))
// Set fourth icon to be non-themed with badge.
- folderApps[3].bitmap = folderApps[3].bitmap.withFlags(profileFlagOp(UserIconInfo.TYPE_WORK))
- folderApps[3].bitmap.themedBitmap = null
+ folderItems[3].bitmap =
+ folderItems[3].bitmap.withFlags(profileFlagOp(UserIconInfo.TYPE_WORK))
+ folderItems[3].bitmap.themedBitmap = null
- defaultThemedIcons = get(context).get(THEMED_ICONS)
+ defaultThemedIcons = themeManager.isMonoThemeEnabled
}
@After
@Throws(Exception::class)
fun tearDown() {
- get(context).put(THEMED_ICONS, defaultThemedIcons)
+ themeManager.isMonoThemeEnabled = defaultThemedIcons
modelHelper.destroy()
}
@Test
fun checkThemedIconWithThemingOn_iconShouldBeThemed() {
- get(context).put(THEMED_ICONS, true)
+ themeManager.isMonoThemeEnabled = true
val drawingParams = PreviewItemDrawingParams(0f, 0f, 0f)
previewItemManager.setDrawable(drawingParams, folderItems[0])
@@ -132,7 +150,7 @@
@Test
fun checkThemedIconWithThemingOff_iconShouldNotBeThemed() {
- get(context).put(THEMED_ICONS, false)
+ themeManager.isMonoThemeEnabled = false
val drawingParams = PreviewItemDrawingParams(0f, 0f, 0f)
previewItemManager.setDrawable(drawingParams, folderItems[0])
@@ -142,7 +160,7 @@
@Test
fun checkUnthemedIconWithThemingOn_iconShouldNotBeThemed() {
- get(context).put(THEMED_ICONS, true)
+ themeManager.isMonoThemeEnabled = true
val drawingParams = PreviewItemDrawingParams(0f, 0f, 0f)
previewItemManager.setDrawable(drawingParams, folderItems[1])
@@ -152,7 +170,7 @@
@Test
fun checkUnthemedIconWithThemingOff_iconShouldNotBeThemed() {
- get(context).put(THEMED_ICONS, false)
+ themeManager.isMonoThemeEnabled = false
val drawingParams = PreviewItemDrawingParams(0f, 0f, 0f)
previewItemManager.setDrawable(drawingParams, folderItems[1])
@@ -162,7 +180,7 @@
@Test
fun checkThemedIconWithBadgeWithThemingOn_iconAndBadgeShouldBeThemed() {
- get(context).put(THEMED_ICONS, true)
+ themeManager.isMonoThemeEnabled = true
val drawingParams = PreviewItemDrawingParams(0f, 0f, 0f)
previewItemManager.setDrawable(drawingParams, folderItems[2])
@@ -175,7 +193,7 @@
@Test
fun checkUnthemedIconWithBadgeWithThemingOn_badgeShouldBeThemed() {
- get(context).put(THEMED_ICONS, true)
+ themeManager.isMonoThemeEnabled = true
val drawingParams = PreviewItemDrawingParams(0f, 0f, 0f)
previewItemManager.setDrawable(drawingParams, folderItems[3])
@@ -188,7 +206,7 @@
@Test
fun checkUnthemedIconWithBadgeWithThemingOff_iconAndBadgeShouldNotBeThemed() {
- get(context).put(THEMED_ICONS, false)
+ themeManager.isMonoThemeEnabled = false
val drawingParams = PreviewItemDrawingParams(0f, 0f, 0f)
previewItemManager.setDrawable(drawingParams, folderItems[3])
@@ -232,6 +250,31 @@
assertThat(drawingParams.drawable).isInstanceOf(PreloadIconDrawable::class.java)
}
+ @Test
+ fun `Preview item loads and apply high res icon`() {
+ val drawingParams = PreviewItemDrawingParams(0f, 0f, 0f)
+ val originalBitmap = folderItems[3].bitmap
+ folderItems[3].bitmap = BitmapInfo.LOW_RES_INFO
+
+ previewItemManager.setDrawable(drawingParams, folderItems[3])
+ assertThat(drawingParams.drawable).isInstanceOf(PlaceHolderIconDrawable::class.java)
+
+ val callbackCaptor = argumentCaptor<ItemInfoUpdateReceiver>()
+ verify(iconCache).updateIconInBackground(callbackCaptor.capture(), eq(folderItems[3]))
+
+ // Restore high-res icon
+ folderItems[3].bitmap = originalBitmap
+
+ // Calling with a different item info will ignore the update
+ callbackCaptor.firstValue.reapplyItemInfo(folderItems[2])
+ assertThat(drawingParams.drawable).isInstanceOf(PlaceHolderIconDrawable::class.java)
+
+ // Calling with correct value will update the drawable to high-res
+ callbackCaptor.firstValue.reapplyItemInfo(folderItems[3])
+ assertThat(drawingParams.drawable).isNotInstanceOf(PlaceHolderIconDrawable::class.java)
+ assertThat(drawingParams.drawable).isInstanceOf(FastBitmapDrawable::class.java)
+ }
+
private fun profileFlagOp(type: Int) =
UserIconInfo(Process.myUserHandle(), type).applyBitmapInfoFlags(FlagOp.NO_OP)
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/graphics/IconShapeTest.kt b/tests/multivalentTests/src/com/android/launcher3/graphics/IconShapeTest.kt
new file mode 100644
index 0000000..311676a
--- /dev/null
+++ b/tests/multivalentTests/src/com/android/launcher3/graphics/IconShapeTest.kt
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.graphics
+
+import android.graphics.Matrix
+import android.graphics.Matrix.ScaleToFit.FILL
+import android.graphics.Path
+import android.graphics.Path.Direction
+import android.graphics.Rect
+import android.graphics.RectF
+import android.graphics.Region
+import android.platform.uiautomatorhelpers.DeviceHelpers.context
+import android.view.View
+import androidx.core.graphics.PathParser
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.graphics.IconShape.Circle
+import com.android.launcher3.graphics.IconShape.Companion.AREA_CALC_SIZE
+import com.android.launcher3.graphics.IconShape.Companion.AREA_DIFF_THRESHOLD
+import com.android.launcher3.graphics.IconShape.Companion.areaDiffCalculator
+import com.android.launcher3.graphics.IconShape.Companion.pickBestShape
+import com.android.launcher3.graphics.IconShape.GenericPathShape
+import com.android.launcher3.graphics.IconShape.RoundedSquare
+import com.android.launcher3.icons.GraphicsUtils
+import com.android.launcher3.views.ClipPathView
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class IconShapeTest {
+
+ @Test
+ fun `areaDiffCalculator increases with outwards shape`() {
+ val diffCalculator =
+ areaDiffCalculator(
+ Path().apply {
+ addCircle(
+ AREA_CALC_SIZE / 2f,
+ AREA_CALC_SIZE / 2f,
+ AREA_CALC_SIZE / 2f,
+ Direction.CW,
+ )
+ }
+ )
+ assertThat(diffCalculator(Circle())).isLessThan(AREA_DIFF_THRESHOLD)
+ assertThat(diffCalculator(Circle())).isLessThan(diffCalculator(RoundedSquare(.9f)))
+ assertThat(diffCalculator(RoundedSquare(.9f)))
+ .isLessThan(diffCalculator(RoundedSquare(.8f)))
+ assertThat(diffCalculator(RoundedSquare(.8f)))
+ .isLessThan(diffCalculator(RoundedSquare(.7f)))
+ assertThat(diffCalculator(RoundedSquare(.7f)))
+ .isLessThan(diffCalculator(RoundedSquare(.6f)))
+ assertThat(diffCalculator(RoundedSquare(.6f)))
+ .isLessThan(diffCalculator(RoundedSquare(.5f)))
+ }
+
+ @Test
+ fun `areaDiffCalculator increases with inwards shape`() {
+ val diffCalculator = areaDiffCalculator(roundedRectPath(0.5f))
+ assertThat(diffCalculator(RoundedSquare(.5f))).isLessThan(AREA_DIFF_THRESHOLD)
+ assertThat(diffCalculator(RoundedSquare(.5f)))
+ .isLessThan(diffCalculator(RoundedSquare(.6f)))
+ assertThat(diffCalculator(RoundedSquare(.5f)))
+ .isLessThan(diffCalculator(RoundedSquare(.4f)))
+ }
+
+ @Test
+ fun `pickBestShape picks circle`() {
+ val r = AREA_CALC_SIZE / 2
+ val pathStr = "M 50 0 a 50 50 0 0 1 0 100 a 50 50 0 0 1 0 -100"
+ val path = Path().apply { addCircle(r.toFloat(), r.toFloat(), r.toFloat(), Direction.CW) }
+ assertThat(pickBestShape(path, pathStr)).isInstanceOf(Circle::class.java)
+ }
+
+ @Test
+ fun `pickBestShape picks rounded rect`() {
+ val factor = 0.5f
+ var shape = pickBestShape(roundedRectPath(factor), roundedRectString(factor))
+ assertThat(shape).isInstanceOf(RoundedSquare::class.java)
+ assertThat((shape as RoundedSquare).radiusRatio).isEqualTo(factor)
+
+ val factor2 = 0.2f
+ shape = pickBestShape(roundedRectPath(factor2), roundedRectString(factor2))
+ assertThat(shape).isInstanceOf(RoundedSquare::class.java)
+ assertThat((shape as RoundedSquare).radiusRatio).isEqualTo(factor2)
+ }
+
+ @Test
+ fun `pickBestShape picks generic shape`() {
+ val path = cookiePath(Rect(0, 0, AREA_CALC_SIZE, AREA_CALC_SIZE))
+ val pathStr = FOUR_SIDED_COOKIE
+ val shape = pickBestShape(path, pathStr)
+ assertThat(shape).isInstanceOf(GenericPathShape::class.java)
+
+ val diffCalculator = areaDiffCalculator(path)
+ assertThat(diffCalculator(shape)).isLessThan(AREA_DIFF_THRESHOLD)
+ }
+
+ @Test
+ fun `generic shape creates smooth animation`() {
+ val shape = GenericPathShape(FOUR_SIDED_COOKIE)
+ val target = TestClipView()
+ val anim =
+ shape.createRevealAnimator(
+ target,
+ Rect(0, 0, AREA_CALC_SIZE, AREA_CALC_SIZE),
+ Rect(0, 0, AREA_CALC_SIZE, AREA_CALC_SIZE),
+ AREA_CALC_SIZE * .25f,
+ false,
+ )
+
+ // Verify that the start rect is similar to initial path
+ anim.setCurrentFraction(0f)
+ assertThat(
+ getAreaDiff(
+ target.currentClip!!,
+ cookiePath(Rect(0, 0, AREA_CALC_SIZE, AREA_CALC_SIZE)),
+ )
+ )
+ .isLessThan(AREA_CALC_SIZE)
+
+ // Verify that end rect is similar to end path
+ anim.setCurrentFraction(1f)
+ assertThat(getAreaDiff(target.currentClip!!, roundedRectPath(0.5f)))
+ .isLessThan(AREA_CALC_SIZE)
+
+ // Ensure that when running animation, area increases smoothly. We run the animation over
+ // [steps] and verify increase of max 5 times the linear diff increase
+ val steps = 1000
+ val incrementalDiff =
+ getAreaDiff(
+ cookiePath(Rect(0, 0, AREA_CALC_SIZE, AREA_CALC_SIZE)),
+ roundedRectPath(0.5f),
+ ) * 5 / steps
+ var lastPath = cookiePath(Rect(0, 0, AREA_CALC_SIZE, AREA_CALC_SIZE))
+ for (progress in 1..steps) {
+ anim.setCurrentFraction(progress / 1000f)
+ val currentPath = Path(target.currentClip!!)
+ assertThat(getAreaDiff(lastPath, currentPath)).isLessThan(incrementalDiff)
+ lastPath = currentPath
+ }
+ assertThat(getAreaDiff(lastPath, roundedRectPath(0.5f))).isLessThan(AREA_CALC_SIZE)
+ }
+
+ private fun roundedRectPath(factor: Float) =
+ Path().apply {
+ val r = factor * AREA_CALC_SIZE / 2
+ addRoundRect(
+ 0f,
+ 0f,
+ AREA_CALC_SIZE.toFloat(),
+ AREA_CALC_SIZE.toFloat(),
+ r,
+ r,
+ Direction.CW,
+ )
+ }
+
+ private fun roundedRectString(factor: Float): String {
+ val s = 100f
+ val r = (factor * s / 2)
+ val t = s - r
+ return "M $r 0 " +
+ "L $t 0 " +
+ "A $r $r 0 0 1 $s $r " +
+ "L $s $t " +
+ "A $r $r 0 0 1 $t $s " +
+ "L $r $s " +
+ "A $r $r 0 0 1 0 $t " +
+ "L 0 $r " +
+ "A $r $r 0 0 1 $r 0 Z"
+ }
+
+ private fun getAreaDiff(p1: Path, p2: Path): Int {
+ val fullRegion = Region(0, 0, AREA_CALC_SIZE, AREA_CALC_SIZE)
+ val iconRegion = Region().apply { setPath(p1, fullRegion) }
+ val shapeRegion = Region().apply { setPath(p2, fullRegion) }
+ shapeRegion.op(iconRegion, Region.Op.XOR)
+ return GraphicsUtils.getArea(shapeRegion)
+ }
+
+ class TestClipView : View(context), ClipPathView {
+
+ var currentClip: Path? = null
+
+ override fun setClipPath(clipPath: Path?) {
+ currentClip = clipPath
+ }
+ }
+
+ companion object {
+ const val FOUR_SIDED_COOKIE =
+ "M63.605 3C84.733 -6.176 106.176 15.268 97 36.395L95.483 39.888C92.681 46.338 92.681 53.662 95.483 60.112L97 63.605C106.176 84.732 84.733 106.176 63.605 97L60.112 95.483C53.662 92.681 46.338 92.681 39.888 95.483L36.395 97C15.267 106.176 -6.176 84.732 3 63.605L4.517 60.112C7.319 53.662 7.319 46.338 4.517 39.888L3 36.395C -6.176 15.268 15.267 -6.176 36.395 3L39.888 4.517C46.338 7.319 53.662 7.319 60.112 4.517L63.605 3Z"
+
+ private fun cookiePath(bounds: Rect) =
+ PathParser.createPathFromPathData(FOUR_SIDED_COOKIE).apply {
+ transform(
+ Matrix().apply { setRectToRect(RectF(0f, 0f, 100f, 100f), RectF(bounds), FILL) }
+ )
+ }
+ }
+}
diff --git a/tests/multivalentTests/src/com/android/launcher3/graphics/ThemeManagerTest.kt b/tests/multivalentTests/src/com/android/launcher3/graphics/ThemeManagerTest.kt
new file mode 100644
index 0000000..43b7b68
--- /dev/null
+++ b/tests/multivalentTests/src/com/android/launcher3/graphics/ThemeManagerTest.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.graphics
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.FakeLauncherPrefs
+import com.android.launcher3.dagger.LauncherAppComponent
+import com.android.launcher3.dagger.LauncherAppSingleton
+import com.android.launcher3.util.AllModulesForTest
+import com.android.launcher3.util.Executors.MAIN_EXECUTOR
+import com.android.launcher3.util.FakePrefsModule
+import com.android.launcher3.util.SandboxApplication
+import com.android.launcher3.util.TestUtil
+import dagger.Component
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNotEquals
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ThemeManagerTest {
+
+ @get:Rule val context = SandboxApplication()
+
+ lateinit var themeManager: ThemeManager
+
+ @Before
+ fun setUp() {
+ context.initDaggerComponent(DaggerThemeManagerComponent.builder())
+ themeManager = ThemeManager.INSTANCE[context]
+ }
+
+ @Test
+ fun `isMonoThemeEnabled get and set`() {
+ themeManager.isMonoThemeEnabled = true
+ TestUtil.runOnExecutorSync(MAIN_EXECUTOR) {}
+ assertTrue(themeManager.isMonoThemeEnabled)
+ assertTrue(themeManager.iconState.isMonoTheme)
+
+ themeManager.isMonoThemeEnabled = false
+ TestUtil.runOnExecutorSync(MAIN_EXECUTOR) {}
+ assertFalse(themeManager.isMonoThemeEnabled)
+ assertFalse(themeManager.iconState.isMonoTheme)
+ }
+
+ @Test
+ fun `callback called on theme change`() {
+ themeManager.isMonoThemeEnabled = false
+
+ var callbackCalled = false
+ themeManager.addChangeListener { callbackCalled = true }
+ themeManager.isMonoThemeEnabled = true
+ TestUtil.runOnExecutorSync(MAIN_EXECUTOR) {}
+
+ assertTrue(callbackCalled)
+ }
+
+ @Test
+ fun `iconState changes with theme`() {
+ themeManager.isMonoThemeEnabled = false
+ TestUtil.runOnExecutorSync(MAIN_EXECUTOR) {}
+ val disabledIconState = themeManager.iconState
+
+ themeManager.isMonoThemeEnabled = true
+ TestUtil.runOnExecutorSync(MAIN_EXECUTOR) {}
+ assertNotEquals(disabledIconState, themeManager.iconState)
+
+ themeManager.isMonoThemeEnabled = false
+ TestUtil.runOnExecutorSync(MAIN_EXECUTOR) {}
+ assertEquals(disabledIconState, themeManager.iconState)
+ }
+}
+
+@LauncherAppSingleton
+@Component(modules = [AllModulesForTest::class, FakePrefsModule::class])
+interface ThemeManagerComponent : LauncherAppComponent {
+
+ override fun getLauncherPrefs(): FakeLauncherPrefs
+
+ @Component.Builder
+ interface Builder : LauncherAppComponent.Builder {
+
+ override fun build(): ThemeManagerComponent
+ }
+}
diff --git a/tests/multivalentTests/src/com/android/launcher3/ui/BubbleTextViewTest.java b/tests/multivalentTests/src/com/android/launcher3/ui/BubbleTextViewTest.java
index b933ed2..f51871b 100644
--- a/tests/multivalentTests/src/com/android/launcher3/ui/BubbleTextViewTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/ui/BubbleTextViewTest.java
@@ -20,8 +20,6 @@
import static android.graphics.fonts.FontStyle.FONT_WEIGHT_NORMAL;
import static android.text.style.DynamicDrawableSpan.ALIGN_CENTER;
-import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
-
import static com.android.launcher3.BubbleTextView.DISPLAY_ALL_APPS;
import static com.android.launcher3.BubbleTextView.DISPLAY_PREDICTION_ROW;
import static com.android.launcher3.BubbleTextView.DISPLAY_SEARCH_RESULT;
@@ -44,6 +42,7 @@
import android.graphics.Typeface;
import android.os.Build;
import android.os.UserHandle;
+import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.text.SpannedString;
@@ -64,8 +63,10 @@
import com.android.launcher3.util.ActivityContextWrapper;
import com.android.launcher3.util.FlagOp;
import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.LauncherModelHelper.SandboxModelContext;
import com.android.launcher3.views.BaseDragLayer;
+import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -111,19 +112,22 @@
private static final float SPACE_MULTIPLIER = 1;
private static final float SPACE_EXTRA = 0;
+ private SandboxModelContext mModelContext;
+
private BubbleTextView mBubbleTextView;
private ItemInfoWithIcon mItemInfoWithIcon;
private Context mContext;
private int mLimitedWidth;
private AppInfo mGmailAppInfo;
- private LauncherPrefs mLauncherPrefs;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
Utilities.enableRunningInTestHarnessForTests();
- mContext = new ActivityContextWrapper(getApplicationContext());
- mLauncherPrefs = LauncherPrefs.get(mContext);
+ mModelContext = new SandboxModelContext();
+ LauncherPrefs.get(mModelContext).put(ENABLE_TWOLINE_ALLAPPS_TOGGLE, true);
+
+ mContext = new ActivityContextWrapper(mModelContext);
mBubbleTextView = new BubbleTextView(mContext);
mBubbleTextView.reset();
@@ -149,10 +153,14 @@
mGmailAppInfo = new AppInfo(componentName, "Gmail", WORK_HANDLE, new Intent());
}
+ @After
+ public void tearDown() {
+ mModelContext.onDestroy();
+ }
+
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE)
public void testEmptyString_flagOn() {
- mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
- mLauncherPrefs.put(ENABLE_TWOLINE_ALLAPPS_TOGGLE, true);
mItemInfoWithIcon.title = EMPTY_STRING;
mBubbleTextView.setDisplay(DISPLAY_ALL_APPS);
mBubbleTextView.applyLabel(mItemInfoWithIcon);
@@ -165,8 +173,8 @@
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE)
public void testEmptyString_flagOff() {
- mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
mItemInfoWithIcon.title = EMPTY_STRING;
mBubbleTextView.setDisplay(DISPLAY_ALL_APPS);
mBubbleTextView.applyLabel(mItemInfoWithIcon);
@@ -179,9 +187,8 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE)
public void testStringWithSpaceLongerThanCharLimit_flagOn() {
- mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
- mLauncherPrefs.put(ENABLE_TWOLINE_ALLAPPS_TOGGLE, true);
// test string: "Battery Stats"
mItemInfoWithIcon.title = TEST_STRING_WITH_SPACE_LONGER_THAN_CHAR_LIMIT;
mBubbleTextView.applyLabel(mItemInfoWithIcon);
@@ -195,8 +202,8 @@
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE)
public void testStringWithSpaceLongerThanCharLimit_flagOff() {
- mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
// test string: "Battery Stats"
mItemInfoWithIcon.title = TEST_STRING_WITH_SPACE_LONGER_THAN_CHAR_LIMIT;
mBubbleTextView.applyLabel(mItemInfoWithIcon);
@@ -210,9 +217,8 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE)
public void testLongStringNoSpaceLongerThanCharLimit_flagOn() {
- mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
- mLauncherPrefs.put(ENABLE_TWOLINE_ALLAPPS_TOGGLE, true);
// test string: "flutterappflorafy"
mItemInfoWithIcon.title = TEST_LONG_STRING_NO_SPACE_LONGER_THAN_CHAR_LIMIT;
mBubbleTextView.applyLabel(mItemInfoWithIcon);
@@ -226,8 +232,8 @@
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE)
public void testLongStringNoSpaceLongerThanCharLimit_flagOff() {
- mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
// test string: "flutterappflorafy"
mItemInfoWithIcon.title = TEST_LONG_STRING_NO_SPACE_LONGER_THAN_CHAR_LIMIT;
mBubbleTextView.applyLabel(mItemInfoWithIcon);
@@ -241,9 +247,8 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE)
public void testLongStringWithSpaceLongerThanCharLimit_flagOn() {
- mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
- mLauncherPrefs.put(ENABLE_TWOLINE_ALLAPPS_TOGGLE, true);
// test string: "System UWB Field Test"
mItemInfoWithIcon.title = TEST_LONG_STRING_WITH_SPACE_LONGER_THAN_CHAR_LIMIT;
mBubbleTextView.applyLabel(mItemInfoWithIcon);
@@ -257,8 +262,8 @@
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE)
public void testLongStringWithSpaceLongerThanCharLimit_flagOff() {
- mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
// test string: "System UWB Field Test"
mItemInfoWithIcon.title = TEST_LONG_STRING_WITH_SPACE_LONGER_THAN_CHAR_LIMIT;
mBubbleTextView.applyLabel(mItemInfoWithIcon);
@@ -272,9 +277,8 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE)
public void testLongStringSymbolLongerThanCharLimit_flagOn() {
- mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
- mLauncherPrefs.put(ENABLE_TWOLINE_ALLAPPS_TOGGLE, true);
// test string: "LEGO®Builder"
mItemInfoWithIcon.title = TEST_LONG_STRING_SYMBOL_LONGER_THAN_CHAR_LIMIT;
mBubbleTextView.applyLabel(mItemInfoWithIcon);
@@ -288,8 +292,8 @@
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE)
public void testLongStringSymbolLongerThanCharLimit_flagOff() {
- mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
// test string: "LEGO®Builder"
mItemInfoWithIcon.title = TEST_LONG_STRING_SYMBOL_LONGER_THAN_CHAR_LIMIT;
mBubbleTextView.applyLabel(mItemInfoWithIcon);
@@ -359,9 +363,8 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE)
public void testEnsurePredictionRowIsTwoLine() {
- mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
- mLauncherPrefs.put(ENABLE_TWOLINE_ALLAPPS_TOGGLE, true);
// test string: "Battery Stats"
mItemInfoWithIcon.title = TEST_STRING_WITH_SPACE_LONGER_THAN_CHAR_LIMIT;
mBubbleTextView.setDisplay(DISPLAY_PREDICTION_ROW);
@@ -375,9 +378,8 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE)
public void modifyTitleToSupportMultiLine_whenLimitedHeight_shouldBeOneLine() {
- mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
- mLauncherPrefs.put(ENABLE_TWOLINE_ALLAPPS_TOGGLE, true);
// test string: "LEGO®Builder"
mItemInfoWithIcon.title = TEST_LONG_STRING_SYMBOL_LONGER_THAN_CHAR_LIMIT;
mBubbleTextView.applyLabel(mItemInfoWithIcon);
@@ -390,9 +392,8 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE)
public void modifyTitleToSupportMultiLine_whenUnlimitedHeight_shouldBeTwoLine() {
- mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
- mLauncherPrefs.put(ENABLE_TWOLINE_ALLAPPS_TOGGLE, true);
// test string: "LEGO®Builder"
mItemInfoWithIcon.title = TEST_LONG_STRING_SYMBOL_LONGER_THAN_CHAR_LIMIT;
mBubbleTextView.setDisplay(DISPLAY_ALL_APPS);
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/DaggerGraphs.kt b/tests/multivalentTests/src/com/android/launcher3/util/DaggerGraphs.kt
new file mode 100644
index 0000000..68da9ff
--- /dev/null
+++ b/tests/multivalentTests/src/com/android/launcher3/util/DaggerGraphs.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util
+
+import com.android.launcher3.FakeLauncherPrefs
+import com.android.launcher3.LauncherPrefs
+import com.android.launcher3.dagger.ApiWrapperModule
+import com.android.launcher3.dagger.WindowManagerProxyModule
+import dagger.Binds
+import dagger.Module
+
+private class DaggerGraphs {}
+
+@Module
+abstract class FakePrefsModule {
+ @Binds abstract fun bindLauncherPrefs(prefs: FakeLauncherPrefs): LauncherPrefs
+}
+
+/** All modules. We also exclude the plugin module from tests */
+@Module(includes = [ApiWrapperModule::class, WindowManagerProxyModule::class])
+class AllModulesForTest
+
+/** All modules except the WMProxy */
+@Module(includes = [ApiWrapperModule::class]) class AllModulesMinusWMProxy
+
+/** All modules except the ApiWrapper */
+@Module(includes = [WindowManagerProxyModule::class]) class AllModulesMinusApiWrapper
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/DisplayControllerTest.kt b/tests/multivalentTests/src/com/android/launcher3/util/DisplayControllerTest.kt
index d0cf610..7e76e19 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/DisplayControllerTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/util/DisplayControllerTest.kt
@@ -30,7 +30,6 @@
import com.android.launcher3.LauncherPrefs.Companion.TASKBAR_PINNING
import com.android.launcher3.LauncherPrefs.Companion.TASKBAR_PINNING_IN_DESKTOP_MODE
import com.android.launcher3.dagger.LauncherAppComponent
-import com.android.launcher3.dagger.LauncherAppModule
import com.android.launcher3.dagger.LauncherAppSingleton
import com.android.launcher3.util.DisplayController.CHANGE_DENSITY
import com.android.launcher3.util.DisplayController.CHANGE_ROTATION
@@ -97,9 +96,10 @@
@Before
fun setUp() {
context.initDaggerComponent(
- DaggerDisplayControllerTestComponent.builder().bindWMProxy(windowManagerProxy)
+ DaggerDisplayControllerTestComponent.builder()
+ .bindWMProxy(windowManagerProxy)
+ .bindLauncherPrefs(launcherPrefs)
)
- context.putObject(LauncherPrefs.INSTANCE, launcherPrefs)
displayManager = context.spyService(DisplayManager::class.java)
whenever(launcherPrefs.get(TASKBAR_PINNING)).thenReturn(false)
@@ -138,7 +138,7 @@
whenever(context.resources).thenReturn(resources)
// Initialize DisplayController
- displayController = DisplayController(context)
+ displayController = DisplayController.INSTANCE.get(context)
displayController.addChangeListener(displayInfoChangeListener)
}
@@ -234,14 +234,14 @@
class MyWmProxy : WindowManagerProxy()
@LauncherAppSingleton
-@Component(modules = [LauncherAppModule::class])
+@Component(modules = [AllModulesMinusWMProxy::class])
interface DisplayControllerTestComponent : LauncherAppComponent {
- override fun getWmProxy(): MyWmProxy
-
@Component.Builder
interface Builder : LauncherAppComponent.Builder {
- @BindsInstance fun bindWMProxy(proxy: MyWmProxy): Builder
+ @BindsInstance fun bindWMProxy(proxy: WindowManagerProxy): Builder
+
+ @BindsInstance fun bindLauncherPrefs(prefs: LauncherPrefs): Builder
override fun build(): DisplayControllerTestComponent
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/ModelTestExtensions.kt b/tests/multivalentTests/src/com/android/launcher3/util/ModelTestExtensions.kt
index 8d072d8..547d05e 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/ModelTestExtensions.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/util/ModelTestExtensions.kt
@@ -31,8 +31,9 @@
loadModelSync()
TestUtil.runOnExecutorSync(Executors.MODEL_EXECUTOR) {
modelDbController.run {
- if (Flags.gridMigrationRefactor()) attemptMigrateDb(null /* restoreEventLogger */)
- else tryMigrateDB(null /* restoreEventLogger */)
+ if (Flags.gridMigrationRefactor())
+ attemptMigrateDb(null /* restoreEventLogger */, modelDelegate)
+ else tryMigrateDB(null /* restoreEventLogger */, modelDelegate)
createEmptyDB()
clearEmptyDbFlag()
}
@@ -74,7 +75,7 @@
loadModelSync()
TestUtil.runOnExecutorSync(Executors.MODEL_EXECUTOR) {
val controller: ModelDbController = modelDbController
- controller.attemptMigrateDb(null /* restoreEventLogger */)
+ controller.attemptMigrateDb(null /* restoreEventLogger */, modelDelegate)
modelDbController.newTransaction().use { transaction ->
val values =
ContentValues().apply {
diff --git a/tests/src/com/android/launcher3/backuprestore/BackupAndRestoreDBSelectionTest.kt b/tests/src/com/android/launcher3/backuprestore/BackupAndRestoreDBSelectionTest.kt
index b4ee090..38fad6b 100644
--- a/tests/src/com/android/launcher3/backuprestore/BackupAndRestoreDBSelectionTest.kt
+++ b/tests/src/com/android/launcher3/backuprestore/BackupAndRestoreDBSelectionTest.kt
@@ -25,6 +25,7 @@
import com.android.launcher3.Flags
import com.android.launcher3.LauncherPrefs
import com.android.launcher3.model.ModelDbController
+import com.android.launcher3.model.ModelDelegate
import com.android.launcher3.provider.RestoreDbTask
import com.android.launcher3.util.Executors.MODEL_EXECUTOR
import com.android.launcher3.util.TestUtil
@@ -34,6 +35,7 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
/**
* Makes sure to test {@code RestoreDbTask#removeOldDBs}, we need to remove all the dbs that are not
@@ -49,6 +51,8 @@
@Rule
val setFlagsRule = SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT)
+ val modelDelegate = mock<ModelDelegate>()
+
@Before
fun setUp() {
setFlagsRule.setFlags(true, Flags.FLAG_ENABLE_NARROW_GRID_RESTORE)
@@ -68,9 +72,9 @@
fun oldDatabasesNotPresentAfterRestore() {
val dbController = ModelDbController(getInstrumentation().targetContext)
if (Flags.gridMigrationRefactor()) {
- dbController.attemptMigrateDb(null)
+ dbController.attemptMigrateDb(null, modelDelegate)
} else {
- dbController.tryMigrateDB(null)
+ dbController.tryMigrateDB(null, modelDelegate)
}
TestUtil.runOnExecutorSync(MODEL_EXECUTOR) {
assert(backAndRestoreRule.getDatabaseFiles().size == 1) {
diff --git a/tests/src/com/android/launcher3/dragging/TaplDragTest.java b/tests/src/com/android/launcher3/dragging/TaplDragTest.java
index e2f9feb9a..2e02eb0 100644
--- a/tests/src/com/android/launcher3/dragging/TaplDragTest.java
+++ b/tests/src/com/android/launcher3/dragging/TaplDragTest.java
@@ -64,6 +64,7 @@
@Test
@PortraitLandscape
@PlatinumTest(focusArea = "launcher")
+ @ScreenRecordRule.ScreenRecord // b/383917141
public void testDragToFolder() {
// TODO: add the use case to drag an icon to an existing folder. Currently it either fails
// on tablets or phones due to difference in resolution.
diff --git a/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java b/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java
index 1816030..f6aa31a 100644
--- a/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java
+++ b/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java
@@ -55,7 +55,6 @@
*/
@Test
@PortraitLandscape
- @ScreenRecordRule.ScreenRecord // b/349439239
public void testDeleteFromWorkspace() {
for (String appName : new String[]{GMAIL_APP_NAME, STORE_APP_NAME, TEST_APP_NAME}) {
final HomeAppIcon homeAppIcon = createShortcutInCenterIfNotExist(appName);
@@ -110,6 +109,9 @@
@PortraitLandscape
@PlatinumTest(focusArea = "launcher")
public void testUninstallFromAllApps() throws Exception {
+ // Ensure no existing app icons on the workspace cause scroll to all apps interruptions
+ mLauncher.clearLauncherData();
+
installDummyAppAndWaitForUIUpdate();
try {
Workspace workspace = mLauncher.getWorkspace();
diff --git a/tests/src/com/android/launcher3/model/GridMigrationTest.kt b/tests/src/com/android/launcher3/model/GridMigrationTest.kt
index b8ffe74..8bd0c60 100644
--- a/tests/src/com/android/launcher3/model/GridMigrationTest.kt
+++ b/tests/src/com/android/launcher3/model/GridMigrationTest.kt
@@ -32,6 +32,8 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
private val phoneContext = InstrumentationRegistry.getInstrumentation().targetContext
@@ -87,6 +89,8 @@
@Rule
val setFlagsRule = SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT)
+ val modelDelegate = mock<ModelDelegate>()
+
@Before
fun setup() {
setFlagsRule.setFlags(true, Flags.FLAG_ONE_GRID_SPECS)
@@ -102,6 +106,7 @@
dst.dbHelper,
src.dbHelper.readableDatabase,
true,
+ modelDelegate,
)
} else {
GridSizeMigrationDBController.migrateGridIfNeeded(
@@ -111,6 +116,7 @@
dst.dbHelper,
src.dbHelper.readableDatabase,
true,
+ modelDelegate,
)
}
}
@@ -149,11 +155,12 @@
}
/**
- * Migrate src into dst and compare to target. This method validates 3 things:
+ * Migrate src into dst and compare to target. This method validates 4 things:
* 1. dst has the same number of items as src after the migration, meaning, none of the items
* were removed during the migration.
* 2. dst is valid, meaning that none of the items overlap with each other.
* 3. dst is equal to target to ensure we don't unintentionally change the migration logic.
+ * 4. migration notifies the complete callback.
*/
private fun runTest(src: GridMigrationData, dst: GridMigrationData, target: GridMigrationData) {
migrate(src, dst)
@@ -162,6 +169,7 @@
}
validateDb(dst)
compare(dst, target, src)
+ verify(modelDelegate).gridMigrationComplete(src.gridState, dst.gridState)
}
// Copying the src db for all tests.
diff --git a/tests/src/com/android/launcher3/tablet/TaplIsTabletTest.kt b/tests/src/com/android/launcher3/tablet/TaplIsTabletTest.kt
deleted file mode 100644
index a6de607..0000000
--- a/tests/src/com/android/launcher3/tablet/TaplIsTabletTest.kt
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.tablet
-
-import android.platform.test.rule.AllowedDevices
-import android.platform.test.rule.DeviceProduct
-import com.android.launcher3.Launcher
-import com.android.launcher3.ui.AbstractLauncherUiTest
-import junit.framework.TestCase.assertFalse
-import junit.framework.TestCase.assertTrue
-import org.junit.Test
-
-class TaplIsTabletTest : AbstractLauncherUiTest<Launcher>() {
-
- /** Investigating b/366237798 by isolating and seeing flake rate of mLauncher.isTablet */
- @Test
- @AllowedDevices(
- DeviceProduct.CF_FOLDABLE,
- DeviceProduct.CF_TABLET,
- DeviceProduct.TANGORPRO,
- DeviceProduct.FELIX,
- DeviceProduct.COMET,
- )
- fun isTabletShouldBeTrue() {
- assertTrue(mLauncher.isTablet)
- }
-
- /** Investigating b/366237798 by isolating and seeing flake rate of mLauncher.isTablet */
- @Test
- @AllowedDevices(DeviceProduct.CF_PHONE, DeviceProduct.CHEETAH)
- fun isTabletShouldBeFalse() {
- assertFalse(mLauncher.isTablet)
- }
-}
diff --git a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
index d49168f..124c18f 100644
--- a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
+++ b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
@@ -76,8 +76,8 @@
mTest.mDevice.setOrientationNatural();
mTest.executeOnLauncher(launcher ->
{
- LauncherPrefs.get(launcher).put(FIXED_LANDSCAPE_MODE, false);
if (launcher != null) {
+ LauncherPrefs.get(launcher).put(FIXED_LANDSCAPE_MODE, false);
launcher.getRotationHelper().forceAllowRotationForTesting(false);
}
});
diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
index 2fb7987..7e8d759 100644
--- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
@@ -51,6 +51,7 @@
import com.android.launcher3.util.BlockingBroadcastReceiver;
import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator;
import com.android.launcher3.util.TestUtil;
+import com.android.launcher3.util.rule.ScreenRecordRule;
import com.android.launcher3.util.rule.ShellCommandRule;
import org.junit.Before;
@@ -74,6 +75,9 @@
@Rule
public ShellCommandRule mDefaultLauncherRule = ShellCommandRule.setDefaultLauncher();
+ @Rule
+ public ScreenRecordRule mScreenRecordRule = new ScreenRecordRule();
+
private String mCallbackAction;
private String mShortcutId;
private int mAppWidgetId;
@@ -87,6 +91,7 @@
@Test
public void testEmpty() throws Throwable { /* needed while the broken tests are being fixed */ }
+ @ScreenRecordRule.ScreenRecord // b/386243192
@Test
public void testPinWidgetNoConfig() throws Throwable {
runTest("pinWidgetNoConfig", true, (info, view) -> info instanceof LauncherAppWidgetInfo
@@ -95,6 +100,7 @@
.equals(AppWidgetNoConfig.class.getName()));
}
+ @ScreenRecordRule.ScreenRecord // b/386243192
@Test
public void testPinWidgetNoConfig_customPreview() throws Throwable {
// Command to set custom preview
@@ -108,6 +114,7 @@
.equals(AppWidgetNoConfig.class.getName()), command);
}
+ @ScreenRecordRule.ScreenRecord // b/386243192
@Test
public void testPinWidgetWithConfig() throws Throwable {
runTest("pinWidgetWithConfig", true,
@@ -117,6 +124,7 @@
.equals(AppWidgetWithConfig.class.getName()));
}
+ @ScreenRecordRule.ScreenRecord // b/386243192
@Test
public void testPinShortcut() throws Throwable {
// Command to set the shortcut id
diff --git a/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java
index 460ffc4..3e3e643 100644
--- a/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java
@@ -51,7 +51,6 @@
@Test
@PortraitLandscape
public void testDragIcon() throws Throwable {
- mLauncher.enableDebugTracing(); // b/289161193
commitTransactionAndLoadHome(new FavoriteItemsTransaction(mTargetContext));
waitForLauncherCondition("Workspace didn't finish loading", l -> !l.isWorkspaceLoading());
@@ -72,7 +71,6 @@
TestUtil.DEFAULT_UI_TIMEOUT);
assertNotNull("Widget not found on the workspace", widget);
widget.launch(getAppPackageName());
- mLauncher.disableDebugTracing(); // b/289161193
}
/**
diff --git a/tests/src/com/android/launcher3/ui/workspace/TaplTwoPanelWorkspaceTest.java b/tests/src/com/android/launcher3/ui/workspace/TaplTwoPanelWorkspaceTest.java
index 2edd129..e544b19 100644
--- a/tests/src/com/android/launcher3/ui/workspace/TaplTwoPanelWorkspaceTest.java
+++ b/tests/src/com/android/launcher3/ui/workspace/TaplTwoPanelWorkspaceTest.java
@@ -37,7 +37,6 @@
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;
@@ -283,7 +282,6 @@
@Test
@PortraitLandscape
- @ScreenRecordRule.ScreenRecord // b/330232490
public void testEmptyPagesGetRemovedIfBothPagesAreEmpty() {
Workspace workspace = mLauncher.getWorkspace();
diff --git a/tests/src/com/android/launcher3/ui/workspace/TaplWorkspaceTest.java b/tests/src/com/android/launcher3/ui/workspace/TaplWorkspaceTest.java
index 237f2a9..cb04e13 100644
--- a/tests/src/com/android/launcher3/ui/workspace/TaplWorkspaceTest.java
+++ b/tests/src/com/android/launcher3/ui/workspace/TaplWorkspaceTest.java
@@ -114,6 +114,7 @@
* Similar to {@link TaplWorkspaceTest#testWorkspace} but here we also make sure we can delete
* the pages.
*/
+ @ScreenRecord // b/381918059
@Test
public void testAddAndDeletePageAndFling() {
Workspace workspace = mLauncher.getWorkspace();
diff --git a/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java b/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java
index c852729..a123170 100644
--- a/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java
+++ b/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java
@@ -61,8 +61,7 @@
setThemeEnabled(false);
new FavoriteItemsTransaction(targetContext()).commit();
loadLauncherSync();
- goToState(LauncherState.ALL_APPS);
- freezeAllApps();
+ switchToAllApps();
scrollToAppIcon(APP_NAME);
BubbleTextView btv = getFromLauncher(
@@ -76,8 +75,7 @@
setThemeEnabled(false);
new FavoriteItemsTransaction(targetContext()).commit();
loadLauncherSync();
- goToState(LauncherState.ALL_APPS);
- freezeAllApps();
+ switchToAllApps();
scrollToAppIcon(TEST_APP_NAME);
BubbleTextView btv = getFromLauncher(l -> findBtv(TEST_APP_NAME, l.getAppsView()));
@@ -95,8 +93,7 @@
setThemeEnabled(true);
new FavoriteItemsTransaction(targetContext()).commit();
loadLauncherSync();
- goToState(LauncherState.ALL_APPS);
- freezeAllApps();
+ switchToAllApps();
scrollToAppIcon(APP_NAME);
BubbleTextView btv = getFromLauncher(l ->
@@ -109,8 +106,7 @@
public void testShortcutIconWithTheme() throws Exception {
setThemeEnabled(true);
loadLauncherSync();
- goToState(LauncherState.ALL_APPS);
- freezeAllApps();
+ switchToAllApps();
scrollToAppIcon(TEST_APP_NAME);
BubbleTextView btv = getFromLauncher(l -> findBtv(TEST_APP_NAME, l.getAppsView()));
@@ -158,6 +154,13 @@
}
}
+ private void switchToAllApps() {
+ goToState(LauncherState.ALL_APPS);
+ waitForState("Launcher internal state didn't switch to All Apps",
+ () -> LauncherState.ALL_APPS);
+ freezeAllApps();
+ }
+
private void scrollToAppIcon(String appName) {
executeOnLauncher(l -> {
l.hideKeyboard();
diff --git a/tests/tapl/com/android/launcher3/tapl/Home.java b/tests/tapl/com/android/launcher3/tapl/Home.java
index 85e28e8..4055100 100644
--- a/tests/tapl/com/android/launcher3/tapl/Home.java
+++ b/tests/tapl/com/android/launcher3/tapl/Home.java
@@ -60,7 +60,8 @@
@Override
protected boolean zeroButtonToOverviewGestureStateTransitionWhileHolding() {
- return true;
+ return !mLauncher.isRecentsWindowEnabled()
+ || super.zeroButtonToOverviewGestureStateTransitionWhileHolding();
}
@Override
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index bdfe2ab..0d9f5ce 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -954,7 +954,7 @@
waitUntilLauncherObjectGone(APPS_RES_ID);
waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
waitUntilLauncherObjectGone(WIDGETS_RES_ID);
- if (isTablet() && !is3PLauncher()) {
+ if (isTablet() && !is3PLauncher() && !isRecentsWindowEnabled()) {
waitForSystemLauncherObject(TASKBAR_RES_ID);
} else {
waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
@@ -1008,6 +1008,11 @@
}
}
+ boolean isRecentsWindowEnabled() {
+ return getTestInfo(TestProtocol.REQUEST_IS_RECENTS_WINDOW_ENABLED)
+ .getBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ }
+
public void waitForModelQueueCleared() {
getTestInfo(TestProtocol.REQUEST_MODEL_QUEUE_CLEARED);
}
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
index 8512d73..70bfb60 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -24,7 +24,6 @@
import android.graphics.Rect;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.UiObject2;
@@ -281,10 +280,8 @@
/**
* Returns whether the given String is contained in this Task's contentDescription. Also returns
* true if both Strings are null.
- *
- * TODO(b/342627272): remove Nullable support once the bug causing it to be null is fixed.
*/
- public boolean containsContentDescription(@Nullable String expected,
+ public boolean containsContentDescription(String expected,
OverviewTaskContainer overviewTaskContainer) {
String actual = findObjectInTask(overviewTaskContainer.snapshotRes).getContentDescription();
if (actual == null && expected == null) {
@@ -300,7 +297,7 @@
* Returns whether the given String is contained in this Task's contentDescription. Also returns
* true if both Strings are null
*/
- public boolean containsContentDescription(@Nullable String expected) {
+ public boolean containsContentDescription(String expected) {
return containsContentDescription(expected, DEFAULT);
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index d615879..4a7caf8 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -843,7 +843,9 @@
@Override
protected String getSwipeHeightRequestName() {
- return TestProtocol.REQUEST_HOME_TO_OVERVIEW_SWIPE_HEIGHT;
+ return mLauncher.isRecentsWindowEnabled()
+ ? super.getSwipeHeightRequestName()
+ : TestProtocol.REQUEST_HOME_TO_OVERVIEW_SWIPE_HEIGHT;
}
@Override