Merge "Focus on newly added app for all Accessibility use cases when adding an app to the home screen." into main
diff --git a/Android.bp b/Android.bp
index 6bd8602..5b986ab 100644
--- a/Android.bp
+++ b/Android.bp
@@ -389,6 +389,7 @@
"//frameworks/libs/systemui:contextualeducationlib",
"//frameworks/libs/systemui:msdl",
"SystemUI-statsd",
+ "WindowManager-Shell-shared-AOSP",
"launcher-testing-shared",
"androidx.lifecycle_lifecycle-common-java8",
"androidx.lifecycle_lifecycle-extensions",
diff --git a/aconfig/Android.bp b/aconfig/Android.bp
index 5413601..bca7494 100644
--- a/aconfig/Android.bp
+++ b/aconfig/Android.bp
@@ -20,7 +20,7 @@
aconfig_declarations {
name: "com_android_launcher3_flags",
package: "com.android.launcher3",
- container: "system",
+ container: "system_ext",
srcs: ["**/*.aconfig"],
}
diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig
index c3fb150..1726eca 100644
--- a/aconfig/launcher.aconfig
+++ b/aconfig/launcher.aconfig
@@ -1,5 +1,5 @@
package: "com.android.launcher3"
-container: "system"
+container: "system_ext"
flag {
name: "enable_expanding_pause_work_button"
@@ -564,3 +564,23 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "restore_archived_shortcuts"
+ namespace: "launcher"
+ description: "Makes sure pre-archived pinned shortcuts also get restored"
+ bug: "375414891"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "restore_archived_app_icons_from_db"
+ namespace: "launcher"
+ description: "Restores pre-archived icons from db when available, mimicing promise icons"
+ bug: "391913214"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/aconfig/launcher_overview.aconfig b/aconfig/launcher_overview.aconfig
index b299edf..30b0d40 100644
--- a/aconfig/launcher_overview.aconfig
+++ b/aconfig/launcher_overview.aconfig
@@ -1,5 +1,5 @@
package: "com.android.launcher3"
-container: "system"
+container: "system_ext"
flag {
name: "enable_grid_only_overview"
@@ -71,4 +71,21 @@
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"
+}
+
+flag {
+ name: "enable_separate_external_display_tasks"
+ namespace: "launcher_overview"
+ description: "Enables separating external display tasks in Overview."
+ bug: "391311008"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/aconfig/launcher_search.aconfig b/aconfig/launcher_search.aconfig
index 72f654e..b98eee6 100644
--- a/aconfig/launcher_search.aconfig
+++ b/aconfig/launcher_search.aconfig
@@ -1,5 +1,5 @@
package: "com.android.launcher3"
-container: "system"
+container: "system_ext"
flag {
name: "enable_private_space"
diff --git a/go/quickstep/res/values-fa/strings.xml b/go/quickstep/res/values-fa/strings.xml
index 8453d4e..f0e4a57 100644
--- a/go/quickstep/res/values-fa/strings.xml
+++ b/go/quickstep/res/values-fa/strings.xml
@@ -5,7 +5,7 @@
<string name="action_listen" msgid="2370304050784689486">"گوش دادن"</string>
<string name="action_translate" msgid="8028378961867277746">"ترجمه"</string>
<string name="action_search" msgid="6269564710943755464">"لنز"</string>
- <string name="dialog_acknowledge" msgid="2804025517675853172">"متوجهام"</string>
+ <string name="dialog_acknowledge" msgid="2804025517675853172">"متوجهم"</string>
<string name="dialog_cancel" msgid="6464336969134856366">"لغو"</string>
<string name="dialog_settings" msgid="6564397136021186148">"تنظیمات"</string>
<string name="niu_actions_confirmation_title" msgid="3863451714863526143">"ترجمه نوشتار روی صفحهنمایش یا گوش دادن به آن"</string>
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/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index 201c5f6..5ca7143 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -119,6 +119,7 @@
android:autoRemoveFromRecents="true"
android:excludeFromRecents="true"
android:theme="@style/GestureTutorialActivity"
+ android:label="@string/gesture_tutorial_title"
android:exported="true"
android:configChanges="orientation">
<intent-filter>
diff --git a/quickstep/dagger/com/android/launcher3/dagger/LauncherAppModule.java b/quickstep/dagger/com/android/launcher3/dagger/AppModule.kt
similarity index 65%
copy from quickstep/dagger/com/android/launcher3/dagger/LauncherAppModule.java
copy to quickstep/dagger/com/android/launcher3/dagger/AppModule.kt
index 1711fc1..29586c4 100644
--- a/quickstep/dagger/com/android/launcher3/dagger/LauncherAppModule.java
+++ b/quickstep/dagger/com/android/launcher3/dagger/AppModule.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,12 @@
* 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;
-
-@Module(includes = QuickStepModule.class)
-public class LauncherAppModule {}
+/**
+ * Module containing bindings for the final derivative app, an implementation of this module should
+ * be included in the final app code.
+ */
+@Module abstract class AppModule {}
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/overview_add_desktop_button.xml b/quickstep/res/layout/overview_add_desktop_button.xml
index 2333dd1..e36cf72 100644
--- a/quickstep/res/layout/overview_add_desktop_button.xml
+++ b/quickstep/res/layout/overview_add_desktop_button.xml
@@ -18,7 +18,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apgk/res-auto"
android:id="@+id/add_desktop_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_width="@dimen/add_desktop_button_size"
+ android:layout_height="@dimen/add_desktop_button_size"
android:src="@drawable/ic_desktop_add"
android:padding="10dp" />
\ No newline at end of file
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..eba4ae6 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Programvoorstelle is geaktiveer"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Programvoorstelle is gedeaktiveer"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Voorspelde app: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Draai jou toestel"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Draai asseblief jou toestel om die tutoriaal oor gebaarnavigasie te voltooi"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Maak seker dat jy van die rand heel regs of heel links af swiep"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"vou <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> uit"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"vou <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> in"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Omkring en Soek"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Appikoon"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Apptitel"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Maak Toe-knoppie"</string>
</resources>
diff --git a/quickstep/res/values-am/strings.xml b/quickstep/res/values-am/strings.xml
index 3114fe0..019850d 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"የመተግበሪያ አስተያየት ጥቆማዎች ነቅቷል"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"የመተግበሪያ አስተያየቶች ቦዝነዋል"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"የተገመተው መተግበሪያ፦ <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"መሣሪያዎን ያሽከርክሩ"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"የእጅ ምልክት ዳሰሳ አጋዥ ሥልጠናን ለማጠናቀቅ እባክዎ መሣሪያዎን ያሽከርክሩ"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"ከቀኝ ጥግ ወይም ከግራ ጥግ ጠርዝ ጀምሮ ማንሸራተትዎን ያረጋግጡ"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>ን ዘርጋ"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>ን ሰብስብ"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"ለመፈለግ ክበብ"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"የመተግበሪያ አዶ"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"የመተግበሪያ ርዕስ"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"የዝጋ አዝራር"</string>
</resources>
diff --git a/quickstep/res/values-ar/strings.xml b/quickstep/res/values-ar/strings.xml
index 5bfdd06..a77335f 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"تم تفعيل ميزة \"التطبيقات المقترحة\"."</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"ميزة \"التطبيقات المقترحة\" غير مفعّلة."</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"التطبيق المتوقع: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"يُرجى تدوير الجهاز"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"يُرجى تدوير جهازك لإكمال الدليل التوجيهي للتنقُّل بالإيماءات."</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"تأكَّد من التمرير سريعًا من أقصى الحافة اليسرى أو اليمنى."</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"توسيع <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"تصغير <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"دائرة البحث"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"رمز التطبيق"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"عنوان التطبيق"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"زر الإغلاق"</string>
</resources>
diff --git a/quickstep/res/values-as/strings.xml b/quickstep/res/values-as/strings.xml
index bce8075..a7a66e9 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"এপৰ পৰামৰ্শসমূহ সক্ষম কৰা আছে"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"এপৰ পৰামৰ্শসমূহ অক্ষম কৰা আছে"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"পূৰ্বানুমান কৰা এপ্: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"আপোনাৰ ডিভাইচটো ঘূৰাওক"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"আঙুলিৰ স্পৰ্শৰ নিৰ্দেশেৰে কৰা নেভিগেশ্বনৰ টিউট’ৰিয়েল শেষ কৰিবলৈ অনুগ্ৰহ কৰি আপোনাৰ ডিভাইচটো ঘূৰাওক"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"আপুনি সোঁ অথবা বাওঁ কাষৰ একেবাৰে সীমাৰ পৰা ছোৱাইপ কৰাটো নিশ্চিত কৰক"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> বিস্তাৰ কৰক"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> সংকোচন কৰক"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"সন্ধান কৰিবৰ বাবে বৃত্ত"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"এপৰ আইকন"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"এপৰ শিৰোনাম"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"বন্ধ কৰা বুটাম"</string>
</resources>
diff --git a/quickstep/res/values-az/strings.xml b/quickstep/res/values-az/strings.xml
index 715c2c6..0e0181a 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Tətbiq təklifləri aktivdir"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Tətbiq təklifləri deaktivdir"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Proqnozlaşdırılan tətbiq: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Cihazı fırladın"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Jest naviqasiyası təlimatını tamamlamaq üçün cihazı fırladın"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Ən sağ və ya sol kənardan sürüşdürün"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"genişləndirin: <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"yığcamlaşdırın: <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Dairəyə alaraq axtarın"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Tətbiq ikonası"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Tətbiq başlığı"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Qapatma düyməsi"</string>
</resources>
diff --git a/quickstep/res/values-b+sr+Latn/strings.xml b/quickstep/res/values-b+sr+Latn/strings.xml
index 9089e9a..95767e2 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Predlozi aplikacija su omogućeni"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Predlozi aplikacija su onemogućeni"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predviđamo aplikaciju: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Rotirajte uređaj"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Rotirajte uređaj da biste dovršili vodič za navigaciju pomoću pokreta"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Obavezno prevucite od same desne ili leve ivice"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"proširite oblačić <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"skupite oblačić <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Pretraga zaokruživanjem"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Ikona aplikacije"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Naziv aplikacije"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Dugme Zatvori"</string>
</resources>
diff --git a/quickstep/res/values-be/strings.xml b/quickstep/res/values-be/strings.xml
index 68fe66c..0261c0f 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Прапановы праграм уключаны"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Прапановы праграм выключаны"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Праграма з падказкі: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Павярніце прыладу"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Каб пачаць азнаямленне з дапаможнікам па навігацыі жэстамі, павярніце прыладу"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Правядзіце пальцам справа налева ці злева направа ад самага краю экрана"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>: разгарнуць"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>: згарнуць"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Абвесці для пошуку"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Значок праграмы"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Назва праграмы"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Кнопка \"Закрыць\""</string>
</resources>
diff --git a/quickstep/res/values-bg/strings.xml b/quickstep/res/values-bg/strings.xml
index 046f564..660d816 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Предложенията за приложения са активирани"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Функцията „Предложения за приложения“ е деактивирана"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Предвидено приложение: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Завъртете устройството си"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Моля, завъртете устройството си, за да завършите урока за навигиране с жестове"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Трябва да плъзнете пръст от най-дясната или най-лявата част на екрана"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"разгъване на <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"свиване на <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Търсене с ограждане"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Икона на приложението"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Име на приложението"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Бутон за затваряне"</string>
</resources>
diff --git a/quickstep/res/values-bn/strings.xml b/quickstep/res/values-bn/strings.xml
index 956f66d..b5b0a5e 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"অ্যাপ সাজেশন চালু করা আছে"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"অ্যাপ সাজেশন বন্ধ করা আছে"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"আপনার প্রয়োজন হতে পারে এমন অ্যাপ: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"আপনার ডিভাইস ঘোরান"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"জেসচার নেভিগেশন টিউটোরিয়াল সম্পূর্ণ করতে আপনার ডিভাইসটি ঘোরান"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"স্ক্রিনের একেবারে ডান বা বাঁদিকের প্রান্ত থেকে সোয়াইপ করেছেন কিনা তা দেখে নিন"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> বড় করুন"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> আড়াল করুন"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"খোঁজার জন্য সার্কেল বানান"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"অ্যাপ আইকন"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"অ্যাপের শিরোনাম"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"বন্ধ করার বোতাম"</string>
</resources>
diff --git a/quickstep/res/values-bs/strings.xml b/quickstep/res/values-bs/strings.xml
index d61220c..69a5cdb 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Prijedlozi aplikacija su omogućeni"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Prijedlozi aplikacija su onemogućeni"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predviđena aplikacija: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Rotirajte uređaj"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Rotirajte uređaj da završite vodič za navigaciju pokretima"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Prevucite s krajnjeg desnog ili lijevog ruba"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"proširivanje oblačića <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"sužavanje oblačića <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Pretraživanje zaokruživanjem"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Ikona aplikacije"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Naslov aplikacije"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Dugme za zatvaranje"</string>
</resources>
diff --git a/quickstep/res/values-ca/strings.xml b/quickstep/res/values-ca/strings.xml
index 4c240c5..cc643a6 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Els suggeriments d\'aplicacions estan activats"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Els suggeriments d\'aplicacions estan desactivats"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predicció d\'aplicació: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Gira el dispositiu"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Gira el dispositiu per completar el tutorial de navegació amb gestos"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Assegura\'t de lliscar des de l\'extrem dret o esquerre de la pantalla."</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"desplega <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"replega <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Encercla per cercar"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Icona de l\'aplicació"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Títol de l\'aplicació"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Botó Tanca"</string>
</resources>
diff --git a/quickstep/res/values-cs/strings.xml b/quickstep/res/values-cs/strings.xml
index 0fbe82a..eb0e8c8 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Návrhy aplikací jsou povoleny"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Návrhy aplikací jsou zakázány"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Předpokládaná aplikace: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Otočte zařízení"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Pokud chcete dokončit výukový program navigace gesty, otočte zařízení"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Přejeďte prstem z úplného pravého nebo levého okraje obrazovky"</string>
@@ -135,7 +136,7 @@
<string name="taskbar_a11y_hidden_with_bubbles_title" msgid="7397395993149508087">"Panel a bubliny jsou skryty"</string>
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigační panel"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Vždy zobrazovat panel aplikací"</string>
- <string name="change_navigation_mode" msgid="9088393078736808968">"Změnit režim navigace"</string>
+ <string name="change_navigation_mode" msgid="9088393078736808968">"Změnit navigační režim"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Rozdělovač panelu aplikací"</string>
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Přetečení panelu aplikací"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Přesunout doleva nahoru"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"rozbalit <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"sbalit <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Zakroužkuj a hledej"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Ikona aplikace"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Název aplikace"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Tlačítko Zavřít"</string>
</resources>
diff --git a/quickstep/res/values-da/strings.xml b/quickstep/res/values-da/strings.xml
index 7b9aa92..a7b7d04 100644
--- a/quickstep/res/values-da/strings.xml
+++ b/quickstep/res/values-da/strings.xml
@@ -23,12 +23,11 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Frit format"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Computertilstand"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Flyt til ekstern skærm"</string>
- <string name="recent_task_desktop" msgid="8081113562549637334">"Computer"</string>
+ <string name="recent_task_desktop" msgid="8081113562549637334">"Computertilstand"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Ingen nye elementer"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Indstillinger for appforbrug"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Ryd alt"</string>
<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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Appforslag er aktiveret"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Appforslag er deaktiveret"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"App, du forventes at skulle bruge: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Rotér din enhed"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Rotér enheden for at gennemgå vejledningen i navigation med bevægelser"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Stryg fra kanten yderst til højre eller venstre"</string>
@@ -141,7 +142,7 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Flyt til toppen eller venstre side"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Flyt til bunden eller højre side"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{yderligere app}one{yderligere app}other{yderligere apps}}"</string>
- <string name="quick_switch_desktop" msgid="8393802056024499749">"Computer"</string>
+ <string name="quick_switch_desktop" msgid="8393802056024499749">"Computertilstand"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> og <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Boble"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Overløb"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"udvid <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"skjul <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Circle to Search"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Appikon"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Apptitel"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Knappen Luk"</string>
</resources>
diff --git a/quickstep/res/values-de/strings.xml b/quickstep/res/values-de/strings.xml
index d8f4ceb..dd7b467 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Funktion „App-Vorschläge“ aktiviert"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Funktion \"App-Vorschläge\" deaktiviert"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Vorgeschlagene App: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Gerät drehen"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Bitte drehe dein Gerät, um die Anleitung für die Bedienung über Gesten abzuschließen"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Wische vom äußersten rechten oder linken Displayrand"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"„<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>“ maximieren"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"„<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>“ minimieren"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Circle to Search"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"App-Symbol"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Titel der App"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Schaltfläche „Schließen“"</string>
</resources>
diff --git a/quickstep/res/values-el/strings.xml b/quickstep/res/values-el/strings.xml
index 43f2c23..058d7ba 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Οι προτεινόμενες εφαρμογές ενεργοποιήθηκαν"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Οι προτεινόμενες εφαρμογές είναι απενεργοποιημένες"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Εφαρμογή από πρόβλεψη: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Περιστρέψτε τη συσκευή σας"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Περιστρέψτε τη συσκευή σας για να ολοκληρώσετε τον οδηγό πλοήγησης με κινήσεις"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Φροντίστε να σύρετε από το άκρο της δεξιάς ή της αριστερής πλευράς."</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"ανάπτυξη <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"σύμπτυξη <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Κυκλώστε για αναζήτηση"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Εικονίδιο εφαρμογής"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Τίτλος εφαρμογής"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Κουμπί κλεισίματος"</string>
</resources>
diff --git a/quickstep/res/values-en-rAU/strings.xml b/quickstep/res/values-en-rAU/strings.xml
index e2314b3..506e36d 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"App suggestions enabled"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"App suggestions are disabled"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predicted app: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Rotate your device"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Please rotate your device to complete the gesture navigation tutorial"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Make sure you swipe from the far-right or far-left edge"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"expand <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"collapse <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Circle to Search"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"App icon"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"App title"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Close button"</string>
</resources>
diff --git a/quickstep/res/values-en-rCA/strings.xml b/quickstep/res/values-en-rCA/strings.xml
index de8b73a..f91f4ee 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"App suggestions enabled"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"App suggestions are disabled"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predicted app: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Rotate your device"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Please rotate your device to complete the gesture navigation tutorial"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Make sure you swipe from the far-right or far-left edge"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"expand <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"collapse <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Circle to Search"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"App icon"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"App title"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Close button"</string>
</resources>
diff --git a/quickstep/res/values-en-rGB/strings.xml b/quickstep/res/values-en-rGB/strings.xml
index e2314b3..506e36d 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"App suggestions enabled"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"App suggestions are disabled"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predicted app: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Rotate your device"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Please rotate your device to complete the gesture navigation tutorial"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Make sure you swipe from the far-right or far-left edge"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"expand <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"collapse <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Circle to Search"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"App icon"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"App title"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Close button"</string>
</resources>
diff --git a/quickstep/res/values-en-rIN/strings.xml b/quickstep/res/values-en-rIN/strings.xml
index e2314b3..506e36d 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"App suggestions enabled"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"App suggestions are disabled"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predicted app: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Rotate your device"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Please rotate your device to complete the gesture navigation tutorial"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Make sure you swipe from the far-right or far-left edge"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"expand <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"collapse <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Circle to Search"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"App icon"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"App title"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Close button"</string>
</resources>
diff --git a/quickstep/res/values-es-rUS/strings.xml b/quickstep/res/values-es-rUS/strings.xml
index cd8530a..dd969d7 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Sugerencias de apps habilitadas"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Las sugerencias de aplicaciones están inhabilitadas"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predicción de app: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Rota el dispositivo"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Rota el dispositivo para completar el instructivo de la navegación por gestos"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Asegúrate de deslizar desde el extremo derecho o izquierdo"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"expandir <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"contraer <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Busca con un círculo"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Ícono de la app"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Título de la app"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Botón de cerrar"</string>
</resources>
diff --git a/quickstep/res/values-es/strings.xml b/quickstep/res/values-es/strings.xml
index 662f60b..1da35e6 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Sugerencias de aplicaciones habilitadas"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Las sugerencias de aplicaciones están inhabilitadas"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Aplicación sugerida: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Gira el dispositivo"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Gira el dispositivo para completar el tutorial de navegación por gestos"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Asegúrate de deslizar desde el borde derecho o izquierdo de la pantalla"</string>
@@ -140,7 +141,7 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Barra de tareas ampliada"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mover arriba/a la izquierda"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mover abajo/a la derecha"</string>
- <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{aplicación más}other{aplicaciones más}}"</string>
+ <string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{app más}other{apps más}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Ordenador"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> y <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Burbuja"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"desplegar <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"contraer <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Rodea para buscar"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Icono de la aplicación"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Título de la aplicación"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Botón de cerrar"</string>
</resources>
diff --git a/quickstep/res/values-et/strings.xml b/quickstep/res/values-et/strings.xml
index 0e360ec..70acc52 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Rakenduste soovitused on lubatud"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Rakenduste soovitused on keelatud"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Ennustatud rakendus: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Pöörake seadet"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Pöörake seadet, et liigutustega navigeerimise õpetused lõpetada"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Pühkige kindlasti parem- või vasakpoolsest servast"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"Toiminguriba <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> laiendamine"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"Toiminguriba <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ahendamine"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Ring otsimiseks"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Rakenduse ikoon"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Rakenduse pealkiri"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Sulgemisnupp"</string>
</resources>
diff --git a/quickstep/res/values-eu/strings.xml b/quickstep/res/values-eu/strings.xml
index 4d59ed7..af5962a 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Gaituta daude aplikazioen iradokizunak"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Desgaituta daude aplikazioen iradokizunak"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Iragarritako aplikazioa: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Biratu gailua"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Keinu bidezko nabigazioaren tutoriala osatzeko, biratu gailua"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Ziurtatu hatza pantailaren eskuineko edo ezkerreko ertzetik hasten zarela pasatzen"</string>
@@ -55,7 +56,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 +74,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>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"zabaldu <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"tolestu <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Inguratu bilatzeko"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Aplikazioaren ikonoa"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Aplikazioaren izena"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Ixteko botoia"</string>
</resources>
diff --git a/quickstep/res/values-fa/strings.xml b/quickstep/res/values-fa/strings.xml
index b521e92..40469f5 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"«پیشنهاد برنامه» فعال است"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"«پیشنهاد برنامه» غیرفعال است"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"برنامه پیشبینیشده: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"دستگاهتان را بچرخانید"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"لطفاً برای تکمیل آموزش گامبهگام پیمایش اشارهای، دستگاهتان را بچرخانید"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"دقت کنید که از انتهای لبه سمت راست یا سمت چپ تند بکشید"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"ازهم باز کردن <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"جمع کردن <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"حلقه جستجو"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"نماد برنامه"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"عنوان برنامه"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"دکمه بستن"</string>
</resources>
diff --git a/quickstep/res/values-fi/strings.xml b/quickstep/res/values-fi/strings.xml
index 6c7ec0f..6907e49 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Sovellusehdotukset käytössä"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Sovellusehdotukset on poistettu käytöstä"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Ennakoitu sovellus: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Käännä laite"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Käännä laite, niin voit katsoa esittelyn eleillä navigoinnista"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Pyyhkäise aivan oikeasta tai vasemmasta reunasta"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"laajenna <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"tiivistä <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Circle to Search"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Sovelluskuvake"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Sovelluksen nimi"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Sulje-painike"</string>
</resources>
diff --git a/quickstep/res/values-fr-rCA/strings.xml b/quickstep/res/values-fr-rCA/strings.xml
index 447ac9c..d57479d 100644
--- a/quickstep/res/values-fr-rCA/strings.xml
+++ b/quickstep/res/values-fr-rCA/strings.xml
@@ -21,14 +21,13 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recent_task_option_pin" msgid="7929860679018978258">"Épingler"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Forme libre"</string>
- <string name="recent_task_option_desktop" msgid="8280879717125435668">"Ordinateur de bureau"</string>
+ <string name="recent_task_option_desktop" msgid="8280879717125435668">"Bureau"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Passer à un écran externe"</string>
- <string name="recent_task_desktop" msgid="8081113562549637334">"Ordinateur de bureau"</string>
+ <string name="recent_task_desktop" msgid="8081113562549637334">"Bureau"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Aucun élément récent"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Paramètres d\'utilisation de l\'appli"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Tout effacer"</string>
<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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Les suggestions d\'applis sont activées"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Les suggestions d\'applis sont désactivées"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Appli prédite : <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Faites pivoter votre appareil"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Veuillez faire pivoter votre appareil pour terminer le tutoriel de navigation par gestes"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Assurez-vous de balayer l\'écran à partir de l\'extrémité droite ou gauche"</string>
@@ -141,7 +142,7 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Déplacer vers le coin supérieur gauche de l\'écran"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Déplacer vers le coin inférieur droit de l\'écran"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{autre appli}one{autre appli}other{autres applis}}"</string>
- <string name="quick_switch_desktop" msgid="8393802056024499749">"Ordinateur de bureau"</string>
+ <string name="quick_switch_desktop" msgid="8393802056024499749">"Bureau"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> et <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Bulle"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Bulle à développer"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"Développer <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"Réduire <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Encercler et rechercher"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Icône de l\'appli"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Nom de l\'appli"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Bouton Fermer"</string>
</resources>
diff --git a/quickstep/res/values-fr/strings.xml b/quickstep/res/values-fr/strings.xml
index 212a4ca..b185e0c 100644
--- a/quickstep/res/values-fr/strings.xml
+++ b/quickstep/res/values-fr/strings.xml
@@ -21,14 +21,13 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recent_task_option_pin" msgid="7929860679018978258">"Épingler"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Format libre"</string>
- <string name="recent_task_option_desktop" msgid="8280879717125435668">"Ordinateur"</string>
+ <string name="recent_task_option_desktop" msgid="8280879717125435668">"Mode ordinateur"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Déplacer vers l\'écran externe"</string>
- <string name="recent_task_desktop" msgid="8081113562549637334">"Ordinateur"</string>
+ <string name="recent_task_desktop" msgid="8081113562549637334">"Mode ordinateur"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Aucun élément récent"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Paramètres de consommation de l\'application"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Tout effacer"</string>
<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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Suggestions d\'applications activées"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Les suggestions d\'applications sont désactivées"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Application prédite : <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Faire pivoter l\'appareil"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Veuillez faire pivoter votre appareil pour effectuer le tutoriel de navigation par gestes"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Veillez à bien balayer l\'écran depuis le bord gauche ou droit"</string>
@@ -141,7 +142,7 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Déplacer en haut ou à gauche"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Déplacer en bas ou à droite"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{autre application}one{autre application}other{autres applications}}"</string>
- <string name="quick_switch_desktop" msgid="8393802056024499749">"Ordinateur"</string>
+ <string name="quick_switch_desktop" msgid="8393802056024499749">"Mode ordinateur"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> et <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Bulle"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Dépassement"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"Développer <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"Réduire <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Entourer pour chercher"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Icône de l\'application"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Titre de l\'application"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Bouton \"Fermer\""</string>
</resources>
diff --git a/quickstep/res/values-gl/strings.xml b/quickstep/res/values-gl/strings.xml
index d685997..8990a5f 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"As suxestións de aplicacións están activadas"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"As suxestións de aplicacións están desactivadas"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Aplicación predita: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Xira o dispositivo"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Xira o dispositivo para completar o titorial de navegación con xestos"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Asegúrate de pasar o dedo desde o bordo dereito ou esquerdo"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"despregar <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"contraer <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Rodear para buscar"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Icona da aplicación"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Título da aplicación"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Botón Pechar"</string>
</resources>
diff --git a/quickstep/res/values-gu/strings.xml b/quickstep/res/values-gu/strings.xml
index f815524..cb8a35a 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"ઍપના સુઝાવો ચાલુ છે"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"ઍપના સુઝાવો બંધ છે"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"પૂર્વાનુમાનિત ઍપ: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"તમારા ડિવાઇસને ફેરવો"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"સંકેતથી નૅવિગેશન ટ્યૂટૉરિઅલ પૂર્ણ કરવા માટે કૃપા કરીને તમારા ડિવાઇસને ફેરવો"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"ખાતરી કરો કે તમે એકદમ દૂરની જમણી કે ડાબી કિનારીએથી સ્વાઇપ કરો છો"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> મોટો કરો"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> નાનો કરો"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"શોધવા માટે વર્તુળ દોરો"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"ઍપનું આઇકન"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"ઍપનું શીર્ષક"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"\'બંધ કરો\' બટન"</string>
</resources>
diff --git a/quickstep/res/values-hi/strings.xml b/quickstep/res/values-hi/strings.xml
index 49fe0cd..b7ca582 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"सुझाए गए ऐप्लिकेशन की सुविधा चालू है"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"सुझाए गए ऐप्लिकेशन की सुविधा बंद है"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"सुझाया गया ऐप्लिकेशन: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"अपना डिवाइस घुमाएं"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"जेस्चर वाले नेविगेशन से जुड़े ट्यूटोरियल को पूरा करने के लिए अपने डिवाइस को घुमाएं"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"स्क्रीन पर बिलकुल दाएं या बाएं किनारे से स्वाइप करें"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> को बड़ा करें"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> को छोटा करें"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"सर्कल बनाकर ढूंढें"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"ऐप्लिकेशन आइकॉन"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"ऐप्लिकेशन का नाम"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"\'बंद करें\' बटन"</string>
</resources>
diff --git a/quickstep/res/values-hr/strings.xml b/quickstep/res/values-hr/strings.xml
index 83dde25..cb7d9b9 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Predlaganje apl. omogućeno"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Predlaganje apl. onemogućeno"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predviđena aplikacija: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Zakrenite uređaj"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Zakrenite uređaj da biste dovršili vodič o navigaciji pokretima"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Prijeđite prstom od krajnjeg desnog ili krajnjeg lijevog ruba"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"proširite oblačić <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"sažmite oblačić <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Zaokružite i potražite"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Ikona aplikacije"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Naziv aplikacije"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Gumb Zatvori"</string>
</resources>
diff --git a/quickstep/res/values-hu/strings.xml b/quickstep/res/values-hu/strings.xml
index 95e153b..184fef0 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Alkalmazásjavaslatok engedélyezve"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Alkalmazásjavaslatok letiltva"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Várható alkalmazás: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Forgassa el eszközét"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Forgassa el eszközét a kézmozdulatokkal való navigáció útmutatójának befejezéséhez"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Csúsztasson a képernyő jobb vagy bal széléről."</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> kibontása"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> összecsukása"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Bekarikázással keresés"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Alkalmazásikon"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Alkalmazás neve"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Bezárás gomb"</string>
</resources>
diff --git a/quickstep/res/values-hy/strings.xml b/quickstep/res/values-hy/strings.xml
index 519d9d4..8da1a6d 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"«Առաջարկվող հավելվածներ» գործառույթը միացված է"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"«Առաջարկվող հավելվածներ» գործառույթն անջատված է"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Առաջարկվող հավելված՝ <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Պտտեք սարքը"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Պտտեք սարքը՝ ժեստերով նավիգացիայի ուղեցույցն ավարտելու համար"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Համոզվեք, որ մատը սահեցնում եք էկրանի աջ կամ ձախ եզրից"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>. ծավալել"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>. ծալել"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Շրջագծել որոնելու համար"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Հավելվածի պատկերակ"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Հավելվածի անվանում"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"«Փակել» կոճակ"</string>
</resources>
diff --git a/quickstep/res/values-in/strings.xml b/quickstep/res/values-in/strings.xml
index b8722c3..eb592db 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Saran aplikasi diaktifkan"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Saran aplikasi dinonaktifkan"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Aplikasi yang diprediksi: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Putar perangkat Anda"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Putar perangkat Anda untuk menyelesaikan tutorial navigasi gestur"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Pastikan Anda menggeser dari tepi ujung kanan atau ujung kiri"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"luaskan <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"ciutkan <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Lingkari untuk Menelusuri"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Ikon aplikasi"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Judul aplikasi"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Tombol tutup"</string>
</resources>
diff --git a/quickstep/res/values-is/strings.xml b/quickstep/res/values-is/strings.xml
index c1ab22b..3500001 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Kveikt á tillögum að forritum"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Slökkt er á tillögðum forritum"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Tillaga að forriti: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Snúðu tækinu"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Snúðu tækinu til að ljúka leiðsögn um bendingastjórnun"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Passaðu að strjúka frá jaðri hægri eða vinstri brúnar"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"stækka <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"minnka <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Circle to Search"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Forritstákn"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Titil forrits"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Hnappur til að loka"</string>
</resources>
diff --git a/quickstep/res/values-it/strings.xml b/quickstep/res/values-it/strings.xml
index cda024b..b8fa813 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"La funzionalità app suggerite è attiva"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"La funzionalità app suggerite è disattivata"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"App prevista: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Ruota il dispositivo"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Ruota il dispositivo per completare il tutorial relativo alla navigazione tramite gesti"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Assicurati di scorrere dal bordo all\'estrema destra o all\'estrema sinistra"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"espandi <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"comprimi <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Cerchia e Cerca"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Icona dell\'app"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Titolo dell\'app"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Pulsante Chiudi"</string>
</resources>
diff --git a/quickstep/res/values-iw/strings.xml b/quickstep/res/values-iw/strings.xml
index 664f8e8..c6fc845 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"התכונה \'הצעות לאפליקציות\' מופעלת"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"ההצעות לאפליקציות מושבתות"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"האפליקציות החזויות: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"צריך לסובב את המכשיר"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"צריך לסובב את המכשיר כדי להשלים את המדריך לניווט באמצעות תנועות"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"חשוב להחליק מהקצה השמאלי או הימני"</string>
@@ -88,7 +89,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 +97,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>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"הרחבה של <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"כיווץ של <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"מקיפים ומחפשים"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"סמל האפליקציה"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"שם האפליקציה"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"כפתור הסגירה"</string>
</resources>
diff --git a/quickstep/res/values-ja/strings.xml b/quickstep/res/values-ja/strings.xml
index 3df5c00..5fdcf4a 100644
--- a/quickstep/res/values-ja/strings.xml
+++ b/quickstep/res/values-ja/strings.xml
@@ -23,12 +23,11 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"フリーフォーム"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"デスクトップ"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"外部ディスプレイに移動する"</string>
- <string name="recent_task_desktop" msgid="8081113562549637334">"パソコン"</string>
+ <string name="recent_task_desktop" msgid="8081113562549637334">"デスクトップ"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"最近のアイテムはありません"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"アプリの使用状況の設定"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"すべてクリア"</string>
<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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"アプリの候補表示が有効です"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"アプリの候補は無効です"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"予測されたアプリ: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"デバイスを回転してください"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"ジェスチャー ナビゲーションのチュートリアルを終了するには、デバイスを回転してください"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"右端または左端からスワイプしてください"</string>
@@ -141,7 +142,7 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"上 / 左に移動"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"下 / 右に移動"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{個のその他のアプリ}other{個のその他のアプリ}}"</string>
- <string name="quick_switch_desktop" msgid="8393802056024499749">"パソコン"</string>
+ <string name="quick_switch_desktop" msgid="8393802056024499749">"デスクトップ"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> と <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"ふきだし"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"オーバーフロー"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>を開きます"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>を閉じます"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"かこって検索"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"アプリのアイコン"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"アプリのタイトル"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"閉じるボタン"</string>
</resources>
diff --git a/quickstep/res/values-ka/strings.xml b/quickstep/res/values-ka/strings.xml
index d0db915..01becd7 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"აპის შეთავაზებები ჩართულია"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"აპის შეთავაზებები გათიშულია"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"ნაწინასწარმეტყველები აპი: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"შეატრიალეთ მოწყობილობა"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"ჟესტებით ნავიგაციის სახელმძღვანელოს დასასრულებლად შეატრიალეთ თქვენი მოწყობილობა"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"გადაფურცლეთ უკიდურესი მარჯვენა ან მარცხენა ბოლოდან"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>-ის გაფართოება"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>-ის ჩაკეცვა"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"ძიება წრის მოხაზვით"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"აპის ხატულა"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"აპის სათაური"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"დახურვის ღილაკი"</string>
</resources>
diff --git a/quickstep/res/values-kk/strings.xml b/quickstep/res/values-kk/strings.xml
index 850ed46..3280908 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"\"Қолданба ұсыныстары\" функциясы қосылды."</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"\"Қолданба ұсыныстары\" функциясы өшірулі."</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Болжалды қолданба: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Құрылғыны бұрыңыз"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Қимылмен басқару нұсқаулығын аяқтау үшін құрылғыны бұрыңыз."</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Экранның оң немесе сол жиегінен сырғытыңыз."</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>: жаю"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>: жию"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Қоршау арқылы іздеу"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Қолданба белгішесі"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Қолданба атауы"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"\"Жабу\" түймесі"</string>
</resources>
diff --git a/quickstep/res/values-km/strings.xml b/quickstep/res/values-km/strings.xml
index 6da129d..d05ef16 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"បានបើកការណែនាំកម្មវិធី"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"បានបិទការណែនាំកម្មវិធី"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"កម្មវិធីដែលបានព្យាករ៖ <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"បង្វិលឧបករណ៍របស់អ្នក"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"សូមបង្វិលឧបករណ៍របស់អ្នក ដើម្បីបញ្ចប់មេរៀនអំពីការរុករកដោយប្រើចលនា"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"ត្រូវប្រាកដថាអ្នកអូសពីគែមខាងស្ដាំ ឬខាងឆ្វេង"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"ពង្រីក <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"បង្រួម <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"គូររង្វង់ដើម្បីស្វែងរក"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"រូបកម្មវិធី"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"ចំណងជើងកម្មវិធី"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"ប៊ូតុងបិទ"</string>
</resources>
diff --git a/quickstep/res/values-kn/strings.xml b/quickstep/res/values-kn/strings.xml
index aa38af2..3240b2a 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"ಆ್ಯಪ್ ಸಲಹೆಗಳನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"ಆ್ಯಪ್ ಸಲಹೆಗಳನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"ಶಿಫಾರಸು ಮಾಡಿದ ಆ್ಯಪ್: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"ನಿಮ್ಮ ಸಾಧನವನ್ನು ತಿರುಗಿಸಿ"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"ಗೆಸ್ಚರ್ ನ್ಯಾವಿಗೇಶನ್ ಟುಟೋರಿಯಲ್ ಅನ್ನು ಪೂರ್ಣಗೊಳಿಸಲು, ನಿಮ್ಮ ಸಾಧನವನ್ನು ತಿರುಗಿಸಿ"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"ನೀವು ಬಲಕೊನೆಯ ಅಂಚಿನಿಂದ ಅಥವಾ ಎಡಕೊನೆಯ ಅಂಚಿನಿಂದ ಸ್ವೈಪ್ ಮಾಡುತ್ತಿದ್ದೀರಿ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ಅನ್ನು ವಿಸ್ತೃತಗೊಳಿಸಿ"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ಅನ್ನು ಕುಗ್ಗಿಸಿ"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"ಹುಡುಕಲು ಒಂದು ಸರ್ಕಲ್ ರಚಿಸಿ"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"ಆ್ಯಪ್ ಐಕಾನ್"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"ಆ್ಯಪ್ ಶೀರ್ಷಿಕೆ"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"ಮುಚ್ಚುವ ಬಟನ್"</string>
</resources>
diff --git a/quickstep/res/values-ko/strings.xml b/quickstep/res/values-ko/strings.xml
index 29c9478..c8b8674 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"앱 제안이 사용 설정됨"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"앱 제안이 사용 중지됨"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"예상 앱: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"기기를 회전시키세요"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"동작 탐색 튜토리얼을 완료하려면 기기를 회전시키세요."</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"오른쪽 또는 왼쪽 가장자리 끝에서 스와이프하세요."</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> 펼치기"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> 접기"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"서클 투 서치"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"앱 아이콘"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"앱 제목"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"닫기 버튼"</string>
</resources>
diff --git a/quickstep/res/values-ky/strings.xml b/quickstep/res/values-ky/strings.xml
index b59cefa..6dafcd0 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Сунушталган колдонмолор функциясы иштетилди"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Сунушталган колдонмолор функциясы өчүрүлгөн"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Божомолдонгон колдонмо: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Түзмөгүңүздү буруңуз"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Жаңсап чабыттоо үйрөткүчүнүн аягына чыгуу үчүн түзмөктү буруңуз"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Экранды эң четинен оңдон солго же солдон оңго карай сүрүңүз"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> жайып көрсөтүү"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> жыйыштыруу"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Тегеректеп издөө"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Колдонмонун сүрөтчөсү"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Колдонмонун аталышы"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Жабуу баскычы"</string>
</resources>
diff --git a/quickstep/res/values-lo/strings.xml b/quickstep/res/values-lo/strings.xml
index 985f353..a13da69 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"ເປີດການນຳໃຊ້ການແນະນຳແອັບແລ້ວ"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"ປິດການນຳໃຊ້ການແນະນຳແອັບແລ້ວ"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"ແອັບທີ່ຄາດເດົາໄວ້: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"ໝຸນອຸປະກອນຂອງທ່ານ"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"ກະລຸນາໝຸນອຸປະກອນຂອງທ່ານເພື່ອເຮັດຕາມການສອນການນຳໃຊ້ກ່ຽວກັບການນຳທາງແບບທ່າທາງໃຫ້ສຳເລັດ"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"ກະລຸນາກວດສອບວ່າທ່ານປັດຈາກຂອບຂວາສຸດ ຫຼື ຊ້າຍສຸດ"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"ຂະຫຍາຍ <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"ຫຍໍ້ <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ລົງ"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"ແຕ້ມວົງມົນເພື່ອຊອກຫາ"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"ໄອຄອນແອັບ"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"ຊື່ແອັບ"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"ປຸ່ມປິດ"</string>
</resources>
diff --git a/quickstep/res/values-lt/strings.xml b/quickstep/res/values-lt/strings.xml
index e69c2dc..7334b0a 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Siūlomų programų funkcija įgalinta"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Siūlomų programų funkcija išjungta"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Numatoma programa: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Pasukite įrenginį"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Pasukite įrenginį, kad pereitumėte į naršymo gestais mokomąją medžiagą"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Turite perbraukti nuo dešiniojo ar kairiojo krašto"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"išskleisti „<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>“"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"sutraukti „<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>“"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Paieška apibrėžiant"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Programos piktograma"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Programos pavadinimas"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Mygtukas „Uždaryti“"</string>
</resources>
diff --git a/quickstep/res/values-lv/strings.xml b/quickstep/res/values-lv/strings.xml
index f9401ee..bbf45c3 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Ieteicamās lietotnes ir iespējotas"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Ieteicamās lietotnes ir atspējotas"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Prognozētā lietotne: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Pagrieziet ierīci"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Lūdzu, pagrieziet savu ierīci, lai pabeigtu žestu navigācijas apmācību."</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Jāvelk no pašas labās vai kreisās malas."</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"izvērst “<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>”"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"sakļaut “<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>”"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Apvilkt un meklēt"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Lietotnes ikona"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Lietotnes nosaukums"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Poga Aizvērt"</string>
</resources>
diff --git a/quickstep/res/values-mk/strings.xml b/quickstep/res/values-mk/strings.xml
index d9b6143..5340854 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Предлозите за апликации се овозможени"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Предлозите за апликации се оневозможени"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Предвидена апликација: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Ротирајте го уредот"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Ротирајте го уредот за да го завршите упатството за навигација со движење"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Повлечете од крајниот десен или крајниот лев раб"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"прошири <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"собери <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Пребарување со заокружување"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Икона за апликацијата"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Наслов на апликацијата"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Копче за затворање"</string>
</resources>
diff --git a/quickstep/res/values-ml/strings.xml b/quickstep/res/values-ml/strings.xml
index 9a2fa6e..d92ddbc 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"ആപ്പ് നിർദ്ദേശങ്ങൾ പ്രവർത്തനക്ഷമമാക്കി"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"ആപ്പ് നിർദ്ദേശങ്ങൾ പ്രവർത്തനരഹിതമാക്കി"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"പ്രവചിച്ച ആപ്പ്: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"നിങ്ങളുടെ ഉപകരണം റൊട്ടേറ്റ് ചെയ്യുക"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"ജെസ്ച്ചർ നാവിഗേഷൻ ട്യൂട്ടോറിയൽ പൂർത്തിയാക്കാൻ നിങ്ങളുടെ ഉപകരണം റൊട്ടേറ്റ് ചെയ്യുക"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"വലത്തേയറ്റത്തെയോ ഇടത്തേയറ്റത്തെയോ അരികിൽ നിന്നാണ് സ്വെെപ്പ് ചെയ്യുന്നതെന്ന് ഉറപ്പാക്കുക"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> വികസിപ്പിക്കുക"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ചുരുക്കുക"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"തിരയാൻ വട്ടം വരയ്ക്കൽ"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"ആപ്പ് ഐക്കൺ"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"ആപ്പിന്റെ പേര്"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"അടയ്ക്കുക ബട്ടൺ"</string>
</resources>
diff --git a/quickstep/res/values-mn/strings.xml b/quickstep/res/values-mn/strings.xml
index dda82a9..2b335a9 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Санал болгож буй аппуудыг идэвхжүүлсэн"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Санал болгож буй аппуудыг идэвхгүй болгосон"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Таамаглаж буй апп: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Төхөөрөмжөө эргүүлэх"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Зангааны навигацын практик хичээлийг дуусгахын тулд төхөөрөмжөө эргүүлнэ үү"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Та баруун зах эсвэл зүүн захын ирмэгээс шударна уу"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>-г дэлгэх"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>-г хураах"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Тойруулж зураад хай"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Aппын дүрс тэмдэг"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Аппын нэр"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Хаах товч"</string>
</resources>
diff --git a/quickstep/res/values-mr/strings.xml b/quickstep/res/values-mr/strings.xml
index 00c3be0..647e88d 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"अॅप सूचना सुरू केल्या आहेत"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"अॅप सूचना बंद केल्या आहेत"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"पूर्वानुमान केलेले अॅप: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"तुमचे डिव्हाइस फिरवा"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"कृपया जेश्चर नेव्हिगेशन ट्यूटोरियल पूर्ण करण्यासाठी तुमचे डिव्हाइस फिरवा"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"तुम्ही स्क्रीनच्या अगदी उजव्या किंवा अगदी डाव्या कडेपासून स्वाइप करत आहात खात्री करा"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> चा विस्तार करा"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> कोलॅप्स करा"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"शोधण्यासाठी वर्तुळ करा"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"अॅपचा आयकन"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"अॅपचे शीर्षक"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"बंद करा बटण"</string>
</resources>
diff --git a/quickstep/res/values-ms/strings.xml b/quickstep/res/values-ms/strings.xml
index 6d67b97..baffcbb 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Cadangan apl didayakan"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Cadangan apl dilumpuhkan"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Apl yang diramalkan: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Putarkan peranti anda"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Sila putarkan peranti anda untuk melengkapkan tutorial navigasi gerak isyarat"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Pastikan anda meleret dari hujung sebelah kanan atau hujung sebelah kiri"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"kembangkan <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"kuncupkan <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Bulatkan untuk Membuat Carian"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Ikon apl"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Tajuk apl"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Butang tutup"</string>
</resources>
diff --git a/quickstep/res/values-my/strings.xml b/quickstep/res/values-my/strings.xml
index a477c3b..f89920b 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"အက်ပ်အကြံပြုချက်များ ဖွင့်ထားသည်"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"အက်ပ်အကြံပြုချက်များကို ပိတ်ထားသည်"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"ကြိုတင်မှန်းဆထားသော အက်ပ်− <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"သင့်စက်ကို လှည့်ပါ"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"လက်ဟန်ဖြင့် လမ်းညွှန်ခြင်း ရှင်းလင်းပို့ချချက်အား အပြီးသတ်ရန် သင့်စက်ကို လှည့်ပါ"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"ညာ (သို့) ဘယ်ဘက်အစွန်ဆုံးမှ ပွတ်ဆွဲကြောင်း သေချာပါစေ"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ကို ပိုပြပါ"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ကို လျှော့ပြပါ"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"ရှာရန် ကွက်၍ဝိုင်းလိုက်ပါ"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"အက်ပ်သင်္ကေတ"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"အက်ပ်ခေါင်းစဉ်"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"အပိတ် ခလုတ်"</string>
</resources>
diff --git a/quickstep/res/values-nb/strings.xml b/quickstep/res/values-nb/strings.xml
index 763a6b7..3be8d68 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Appforslag er på"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Appforslag er slått av"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Foreslått app: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Roter enheten"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Roter enheten for å fullføre veiledningen for navigasjon med bevegelser"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Sørg for at du sveiper fra kanten helt til høyre eller venstre"</string>
@@ -141,7 +142,7 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Flytt til øverst/venstre"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Flytt til nederst/høyre"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{app til}other{apper til}}"</string>
- <string name="quick_switch_desktop" msgid="8393802056024499749">"Datamaskin"</string>
+ <string name="quick_switch_desktop" msgid="8393802056024499749">"Skrivebord"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> og <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Boble"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Overflyt"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"vis <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"skjul <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Circle to Search"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Appikon"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Apptittel"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Lukkeknapp"</string>
</resources>
diff --git a/quickstep/res/values-ne/strings.xml b/quickstep/res/values-ne/strings.xml
index c23ba58..4ed8bb9 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"सिफारिस गरिएका एप देखाउने सुविधा अन गरिएको छ"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"सिफारिस गरिएका एपहरू देखाउने सुविधा असक्षम पारिएको छ"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"पूर्वानुमान गरिएको एप: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"आफ्नो डिभाइस रोटेट गर्नुहोस्"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"जेस्चर नेभिगेसनको ट्युटोरियल पूरा गर्न कृपया आफ्नो डिभाइस रोटेट गर्नुहोस्"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"स्क्रिनको सबैभन्दा दायाँ किनारा वा सबैभन्दा बायाँ किनाराबाट स्वाइप गर्नुहोस्"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> एक्स्पान्ड गर्नुहोस्"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> कोल्याप्स गर्नुहोस्"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"खोज्न सर्कल बनाउनुहोस्"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"एप जनाउने आइकन"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"एपको शीर्षक"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"\"बन्द गर्नुहोस्\" बटन"</string>
</resources>
diff --git a/quickstep/res/values-nl/strings.xml b/quickstep/res/values-nl/strings.xml
index dd4a148..c1c1a22 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"App-suggesties staan aan"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"App-suggesties staan uit"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Voorspelde app: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Het apparaat draaien"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Draai het apparaat om de tutorial voor navigatie met gebaren af te ronden"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Swipe vanaf de rechter- of linkerrand"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> uitvouwen"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> samenvouwen"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Circle to Search"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Icoon van app"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Titel van app"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Knop Sluiten"</string>
</resources>
diff --git a/quickstep/res/values-or/strings.xml b/quickstep/res/values-or/strings.xml
index 0802a8b..789ba0a 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"ଆପ୍ ପରାମର୍ଶଗୁଡ଼ିକୁ ସକ୍ଷମ କରାଯାଇଛି"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"ଆପ୍ ପରାମର୍ଶଗୁଡ଼ିକୁ ଅକ୍ଷମ କରାଯାଇଛି"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"ପୂର୍ବାନୁମାନ କରାଯାଇଥିବା ଆପ୍: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"ଆପଣଙ୍କ ଡିଭାଇସକୁ ରୋଟେଟ କରନ୍ତୁ"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"ଜେଶ୍ଚର ନାଭିଗେସନ ଟ୍ୟୁଟୋରିଆଲ ସମ୍ପୂର୍ଣ୍ଣ କରିବାକୁ ଦୟାକରି ଆପଣଙ୍କ ଡିଭାଇସ ରୋଟେଟ କରନ୍ତୁ"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"ଆପଣ ସ୍କ୍ରିନର ଏକଦମ-ଡାହାଣ ବା ବାମ ଧାରରୁ ସ୍ୱାଇପ କରୁଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ।"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ବିସ୍ତାର କରନ୍ତୁ"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ସଙ୍କୁଚିତ କରନ୍ତୁ"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"ସର୍ଚ୍ଚ କରିବାକୁ ସର୍କଲ କରନ୍ତୁ"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"ଆପ ଆଇକନ"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"ଆପ ଟାଇଟେଲ"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"\"ବନ୍ଦ କରନ୍ତୁ\" ବଟନ"</string>
</resources>
diff --git a/quickstep/res/values-pa/strings.xml b/quickstep/res/values-pa/strings.xml
index 10efa76..2392890 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"ਐਪ ਸੁਝਾਵਾਂ ਨੂੰ ਚਾਲੂ ਕੀਤਾ ਗਿਆ"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"ਐਪ ਸੁਝਾਵਾਂ ਨੂੰ ਬੰਦ ਕੀਤਾ ਗਿਆ"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"ਪੂਰਵ ਅਨੁਮਾਨਿਤ ਐਪ: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"ਆਪਣੇ ਡੀਵਾਈਸ ਨੂੰ ਘੁੰਮਾਓ"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"ਕਿਰਪਾ ਕਰਕੇ ਇਸ਼ਾਰਾ ਨੈਵੀਗੇਸ਼ਨ ਟਿਊਟੋਰੀਅਲ ਨੂੰ ਪੂਰਾ ਕਰਨ ਲਈ ਆਪਣੇ ਡੀਵਾਈਸ ਨੂੰ ਘੁੰਮਾਓ"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"ਇਹ ਪੱਕਾ ਕਰੋ ਕਿ ਤੁਸੀਂ ਸੱਜੇ ਜਾਂ ਖੱਬੇ ਪਾਸੇ ਦੇ ਬਿਲਕੁਲ ਕਿਨਾਰੇ ਤੋਂ ਸਵਾਈਪ ਕਰਦੇ ਹੋ"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ਦਾ ਵਿਸਤਾਰ ਕਰੋ"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ਨੂੰ ਸਮੇਟੋ"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"ਖੋਜਣ ਲਈ ਚੱਕਰ ਬਣਾਓ"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"ਐਪ ਪ੍ਰਤੀਕ"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"ਐਪ ਸਿਰਲੇਖ"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"\'ਬੰਦ ਕਰੋ\' ਬਟਨ"</string>
</resources>
diff --git a/quickstep/res/values-pl/strings.xml b/quickstep/res/values-pl/strings.xml
index 46a4ae6..74aec66 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Włączono sugestie aplikacji"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Sugestie aplikacji są wyłączone"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Przewidywana aplikacja: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Obróć urządzenie"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Obróć urządzenie, aby ukończyć samouczek nawigacji przy użyciu gestów"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Pamiętaj, aby przesuwać palcem od samej krawędzi (prawej lub lewej)"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"rozwiń dymek: <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"zwiń dymek: <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Zaznacz, aby wyszukać"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Ikona aplikacji"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Tytuł aplikacji"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Przycisk Zamknij"</string>
</resources>
diff --git a/quickstep/res/values-pt-rPT/strings.xml b/quickstep/res/values-pt-rPT/strings.xml
index b3da02a..56d5514 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Sugestões de apps ativadas"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"As sugestões de apps estão desativadas"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"App prevista: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Rode o dispositivo"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Rode o seu dispositivo para concluir o tutorial de navegação por gestos"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Deslize a partir da extremidade mais à direita ou mais à esquerda"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"expandir <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"reduzir <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Circundar para Pesquisar"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Ícone da app"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Título da app"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Botão Fechar"</string>
</resources>
diff --git a/quickstep/res/values-pt/strings.xml b/quickstep/res/values-pt/strings.xml
index c7fface..bdcad13 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"O recurso \"sugestões de apps\" está ativado"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"O recurso \"sugestões de apps\" está desativado"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"App previsto: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Gire o dispositivo"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Gire o dispositivo para concluir o tutorial da navegação por gestos"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Deslize da borda direita ou esquerda"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"abrir <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"fechar <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Circule para pesquisar"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Ícone do app"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Título do app"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Botão \"Fechar\""</string>
</resources>
diff --git a/quickstep/res/values-ro/strings.xml b/quickstep/res/values-ro/strings.xml
index 2f610a9..6a05909 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Sugestiile de aplicații au fost activate"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Sugestiile de aplicații au fost dezactivate"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Aplicația estimată: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Rotește dispozitivul"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Rotește dispozitivul pentru a încheia tutorialul de navigare prin gesturi"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Glisează dinspre marginea dreaptă îndepărtată sau dinspre marginea stângă îndepărtată"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"extinde <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"restrânge <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Încercuiește și caută"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Pictograma aplicației"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Titlul aplicației"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Buton de închidere"</string>
</resources>
diff --git a/quickstep/res/values-ru/strings.xml b/quickstep/res/values-ru/strings.xml
index f359ab3..69f560d 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Функция \"Рекомендуемые приложения\" включена."</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Функция \"Рекомендуемые приложения\" отключена."</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Рекомендуемое приложение: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Поверните устройство"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Чтобы перейти к руководству по жестам, нужно повернуть устройство."</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Проведите справа налево или слева направо от самого края экрана."</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"Развернуто: <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"Свернуто: <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Обвести и найти"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Значок приложения"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Название приложения"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Кнопка \"Закрыть\""</string>
</resources>
diff --git a/quickstep/res/values-si/strings.xml b/quickstep/res/values-si/strings.xml
index 94d9086..2b1f147 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"යෙදුම් යෝජනා සබලිතයි"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"යෙදුම් යෝජනා අබල කර ඇත"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"පුරෝකථනය කළ යෙදුම: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"ඔබේ උපාංගය කරකවන්න"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"අභින සංචාලන නිබන්ධනය සම්පූර්ණ කිරීම සඳහා ඔබේ උපාංගය කරකවන්න"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"ඔබ ඈත දකුණු හෝ ඈත වම් දාරයේ සිට ස්වයිප් කරන බව සහතික කර ගන්න"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> දිග හරින්න"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> හකුළන්න"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"සෙවීමට කවයසෙවීමට කවය අදින්න"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"යෙදුම් නිරූපකය"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"යෙදුම් මාතෘකාව"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"වැසීමේ බොත්තම"</string>
</resources>
diff --git a/quickstep/res/values-sk/strings.xml b/quickstep/res/values-sk/strings.xml
index 4d302bc..c2e6b85 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Návrhy aplikácií zapnuté"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Návrhy aplikácií vypnuté"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predpovedaná aplikácia: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Otočte zariadenie"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Otočte zariadenie a dokončite tak návod, ako navigovať gestami"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Musíte potiahnuť úplne z pravého alebo ľavého okraja."</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"rozbaliť <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"zbaliť <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Vyhľadávanie krúžením"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Ikona aplikácie"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Názov aplikácie"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Tlačidlo Zavrieť"</string>
</resources>
diff --git a/quickstep/res/values-sl/strings.xml b/quickstep/res/values-sl/strings.xml
index 2b97af3..0922f24 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Predlogi aplikacij so omogočeni."</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Predlogi aplikacij so onemogočeni."</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predvidena aplikacija: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Zasukajte napravo"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Zasukajte napravo, če si želite ogledati vadnico za krmarjenje s potezami"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Pazite, da povlečete s skrajno desnega ali skrajno levega roba."</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"razširitev oblačka <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"strnitev oblačka <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Iskanje z obkroževanjem"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Ikona aplikacije"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Ime aplikacije"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Gumb za zapiranje"</string>
</resources>
diff --git a/quickstep/res/values-sq/strings.xml b/quickstep/res/values-sq/strings.xml
index e59d34f..50b9fa1 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Aplikacionet e sugjeruara janë aktivizuar"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Sugjerimet e aplikacioneve janë çaktivizuar"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Aplikacioni i parashikuar: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Rrotullo pajisjen"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Rrotullo pajisjen për të përfunduar udhëzuesin e navigimit me gjeste"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Sigurohu që të rrëshqasësh shpejt nga skaji më i djathtë ose më i majtë"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"zgjero <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"palos <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Qarko për të kërkuar"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Ikona e aplikacionit"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Titulli i aplikacionit"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Butoni i mbylljes"</string>
</resources>
diff --git a/quickstep/res/values-sr/strings.xml b/quickstep/res/values-sr/strings.xml
index 2952e5b..a20e927 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Предлози апликација су омогућени"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Предлози апликација су онемогућени"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Предвиђамо апликацију: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Ротирајте уређај"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Ротирајте уређај да бисте довршили водич за навигацију помоћу покрета"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Обавезно превуците од саме десне или леве ивице"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"проширите облачић <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"скупите облачић <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Претрага заокруживањем"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Икона апликације"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Назив апликације"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Дугме Затвори"</string>
</resources>
diff --git a/quickstep/res/values-sv/strings.xml b/quickstep/res/values-sv/strings.xml
index 2df4fe9..7c826cd 100644
--- a/quickstep/res/values-sv/strings.xml
+++ b/quickstep/res/values-sv/strings.xml
@@ -21,14 +21,13 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recent_task_option_pin" msgid="7929860679018978258">"Fäst"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Fritt format"</string>
- <string name="recent_task_option_desktop" msgid="8280879717125435668">"Dator"</string>
+ <string name="recent_task_option_desktop" msgid="8280879717125435668">"Skrivbordsläge"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Flytta till extern skärm"</string>
- <string name="recent_task_desktop" msgid="8081113562549637334">"Dator"</string>
+ <string name="recent_task_desktop" msgid="8081113562549637334">"Skrivbordsläge"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Listan är tom"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Inställningar för appanvändning"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Rensa alla"</string>
<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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Appförslag har aktiverats"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Appförslag har inaktiverats"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Appförslag: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Rotera enheten"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Rotera enheten för att slutföra guiden för navigering med rörelser"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Se till att du sveper ända från högerkanten eller vänsterkanten"</string>
@@ -141,7 +142,7 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Flytta högst upp/till vänster"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Flytta längst ned/till höger"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{app till}other{appar till}}"</string>
- <string name="quick_switch_desktop" msgid="8393802056024499749">"Dator"</string>
+ <string name="quick_switch_desktop" msgid="8393802056024499749">"Skrivbordsläge"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> och <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Bubbla"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Fler alternativ"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"utöka <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"komprimera <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Circle to Search"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Appikon"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Apptitel"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Knappen Stäng"</string>
</resources>
diff --git a/quickstep/res/values-sw/strings.xml b/quickstep/res/values-sw/strings.xml
index 6bff9a5..9181599 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Mapendekezo ya programu yamewashwa"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Umezima mapendekezo ya programu"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Programu iliyotabiriwa: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Zungusha kifaa chako"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Tafadhali zungusha kifaa chako ili ukamilishe mafunzo ya usogezaji kwa kutumia ishara"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Hakikisha unatelezesha kidole kutoka ukingo wa kulia au kushoto kabisa"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"panua <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"kunja <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Chora Mviringo ili Kutafuta"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Aikoni ya programu"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Kichwa cha programu"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Kitufe cha kufunga"</string>
</resources>
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..4e3a075 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"ஆப்ஸ் பரிந்துரைகள் இயக்கப்பட்டுள்ளன"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"ஆப்ஸ் பரிந்துரைகள் முடக்கப்பட்டுள்ளன"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"கணித்த ஆப்ஸ்: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"உங்கள் சாதனத்தைச் சுழற்றுங்கள்"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"சைகை வழிசெலுத்தல் பயிற்சியை நிறைவுசெய்ய உங்கள் சாதனத்தைச் சுழற்றுங்கள்"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"வலது அல்லது இடது ஓரத்தின் விளிம்பிலிருந்து ஸ்வைப் செய்வதை உறுதிசெய்துகொள்ளுங்கள்"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ஐ விரிவாக்கும்"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ஐச் சுருக்கும்"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"வட்டமிட்டுத் தேடல்"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"ஆப்ஸ் ஐகான்"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"ஆப்ஸ் தலைப்பு"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"மூடுவதற்கான பட்டன்"</string>
</resources>
diff --git a/quickstep/res/values-te/strings.xml b/quickstep/res/values-te/strings.xml
index 4ab0cd5..94fac1b 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"యాప్ సలహాలు ఎనేబుల్ చేయబడ్డాయి"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"యాప్ సూచనలు డిజేబుల్ చేయబడ్డాయి"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"సూచించబడిన యాప్: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"మీ పరికరాన్ని రొటేట్ చేయండి"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"సంజ్ఞ నావిగేషన్ ట్యుటోరియల్ను పూర్తి చేయడానికి దయచేసి మీ పరికరాన్ని రొటేట్ చేయండి"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"కుడి వైపు చిట్ట చివరి లేదా ఎడమ వైపు చిట్ట చివరి అంచు నుండి స్వైప్ చేస్తున్నారని నిర్ధారించుకోండి"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>ను విస్తరించండి"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>ను కుదించండి"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"సెర్చ్ చేయడానికి సర్కిల్ గీయండి"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"యాప్ చిహ్నం"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"యాప్ టైటిల్"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"\'మూసివేయండి\' బటన్"</string>
</resources>
diff --git a/quickstep/res/values-th/strings.xml b/quickstep/res/values-th/strings.xml
index e858a82..ee515b9 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"เปิดใช้แอปแนะนำแล้ว"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"ปิดใช้คำแนะนำเกี่ยวกับแอปอยู่"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"แอปที่คาดว่าจะใช้: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"หมุนอุปกรณ์ของคุณ"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"โปรดหมุนอุปกรณ์เพื่อทำตามบทแนะนำการนำทางด้วยท่าทางสัมผัสให้เสร็จสมบูรณ์"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"ปัดจากขอบด้านขวาสุดหรือซ้ายสุด"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"ขยาย <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"ยุบ <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"วงเพื่อค้นหา"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"ไอคอนแอป"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"ชื่อแอป"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"ปุ่มปิด"</string>
</resources>
diff --git a/quickstep/res/values-tl/strings.xml b/quickstep/res/values-tl/strings.xml
index 3126775..1bc4a3d 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Naka-enable ang mga iminumungkahing app"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Naka-disable ang mga iminumungkahing app"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Hinulaang app: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"I-rotate ang iyong device"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Paki-rotate ang iyong device para tapusin ang tutorial sa navigation gamit ang galaw"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Tiyaking magsa-swipe ka mula sa dulong kanan o dulong kaliwang gilid"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"i-expand ang <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"i-collapse ang <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Circle to Search"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Icon ng app"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Pamagat ng app"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Button na isara"</string>
</resources>
diff --git a/quickstep/res/values-tr/strings.xml b/quickstep/res/values-tr/strings.xml
index edff6d7..691e6c0 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Uygulama önerileri etkinleştirildi"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Uygulama önerileri devre dışı bırakıldı"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Tahmin edilen uygulama: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Cihazınızı döndürün"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Hareketle gezinme eğitimini tamamlamak için lütfen cihazınızı döndürün"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"En sağ veya en sol kenardan kaydırdığınızdan emin olun"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"genişlet: <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"daralt: <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Seçerek Arat"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Uygulama simgesi"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Uygulama başlığı"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Kapat düğmesi"</string>
</resources>
diff --git a/quickstep/res/values-uk/strings.xml b/quickstep/res/values-uk/strings.xml
index 2b87c22..1668b4f 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Рекомендовані додатки ввімкнено"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Рекомендовані додатки вимкнено"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Передбачений додаток: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Оберніть пристрій"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Обертайте пристрій, щоб ознайомитися з посібником із навігації за допомогою жестів"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Проведіть пальцем від самого краю екрана (правого або лівого)"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"розгорнути \"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>\""</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"згорнути \"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>\""</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Обвести й знайти"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Значок додатка"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Назва додатка"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Кнопка \"Закрити\""</string>
</resources>
diff --git a/quickstep/res/values-ur/strings.xml b/quickstep/res/values-ur/strings.xml
index 28f7872..5b780c3 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"ایپ کی تجاویز فعال ہیں"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"ایپ کی تجاویز غیر فعال ہیں"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"پیشن گوئی کردہ ایپ: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"اپنا آلہ گھمائیں"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"براہ کرم اشاروں والی نیویگیشن کا ٹیوٹوریل مکمل کرنے کے لیے اپنا آلہ گھمائیں"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"یقینی بنائیں کہ آپ دائیں یا بائیں کنارے سے دور سے سوائپ کریں"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> کو پھیلائیں"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> کو سکیڑیں"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"تلاش کرنے کیلئے دائرہ بنائیں"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"ایپ آئیکن"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"ایپ کا عنوان"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"\'بند کریں\' بٹن"</string>
</resources>
diff --git a/quickstep/res/values-uz/strings.xml b/quickstep/res/values-uz/strings.xml
index 4335d99..7de3648 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Ilova tavsiyalari yoqildi"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Endi ilova takliflari chiqmaydi"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Taklif etilgan ilova: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Qurilmangizni buring"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Ishorali navigatsiya darsligini tugatish uchun qurilmani buring"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Ekran chetidan boshlab oʻngdan yoki chapdan suring"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>ni yoyish"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>ni yigʻish"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Chizib qidirish"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Ilova belgisi"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Ilova nomi"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Yopish tugmasi"</string>
</resources>
diff --git a/quickstep/res/values-vi/strings.xml b/quickstep/res/values-vi/strings.xml
index 297b96a..dffcd6c 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Đã bật tính năng Ứng dụng đề xuất"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Tính năng Ứng dụng đề xuất bị tắt"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Ứng dụng dự đoán: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Xoay thiết bị của bạn"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Vui lòng xoay thiết bị của bạn để hoàn tất hướng dẫn thao tác bằng cử chỉ"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Hãy vuốt từ mép ngoài cùng bên phải hoặc ngoài cùng bên trái"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"mở rộng <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"thu gọn <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Khoanh tròn để tìm kiếm"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Biểu tượng ứng dụng"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Tên ứng dụng"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Nút đóng"</string>
</resources>
diff --git a/quickstep/res/values-zh-rCN/strings.xml b/quickstep/res/values-zh-rCN/strings.xml
index 0843c38..4d35d39 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"已启用应用建议"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"已停用应用建议"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"预测的应用:<xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"请旋转设备"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"请旋转设备,完成手势导航教程"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"确保从最右侧或最左侧边缘开始滑动"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"展开“<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>”"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"收起“<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>”"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"圈定即搜"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"应用图标"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"应用名称"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"“关闭”按钮"</string>
</resources>
diff --git a/quickstep/res/values-zh-rHK/strings.xml b/quickstep/res/values-zh-rHK/strings.xml
index ac6633c..fd11ba1 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"已啟用應用程式建議"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"已停用應用程式建議"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"預測應用程式:<xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"旋轉裝置方向"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"請旋轉裝置方向以完成手勢導覽教學課程"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"請確保從螢幕最右側或最左側邊緣滑動"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"打開<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"收埋<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"一圈即搜"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"應用程式圖示"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"應用程式名稱"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"關閉按鈕"</string>
</resources>
diff --git a/quickstep/res/values-zh-rTW/strings.xml b/quickstep/res/values-zh-rTW/strings.xml
index 605df7e..729f7fd 100644
--- a/quickstep/res/values-zh-rTW/strings.xml
+++ b/quickstep/res/values-zh-rTW/strings.xml
@@ -21,14 +21,13 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recent_task_option_pin" msgid="7929860679018978258">"固定"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"自由形式"</string>
- <string name="recent_task_option_desktop" msgid="8280879717125435668">"桌面"</string>
+ <string name="recent_task_option_desktop" msgid="8280879717125435668">"電腦模式"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"移至外接螢幕"</string>
- <string name="recent_task_desktop" msgid="8081113562549637334">"電腦"</string>
+ <string name="recent_task_desktop" msgid="8081113562549637334">"電腦模式"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"最近沒有任何項目"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"應用程式使用情況設定"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"全部清除"</string>
<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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"應用程式建議功能已啟用"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"應用程式建議功能已停用"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"預測的應用程式:<xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"旋轉裝置螢幕方向"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"如要完成手勢操作教學課程,請旋轉裝置螢幕方向"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"請務必從螢幕最右側或最左側滑動"</string>
@@ -141,7 +142,7 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"移到上方/左側"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"移到底部/右側"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{個其他應用程式}other{個其他應用程式}}"</string>
- <string name="quick_switch_desktop" msgid="8393802056024499749">"電腦"</string>
+ <string name="quick_switch_desktop" msgid="8393802056024499749">"電腦模式"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"「<xliff:g id="APP_NAME_1">%1$s</xliff:g>」和「<xliff:g id="APP_NAME_2">%2$s</xliff:g>」"</string>
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"泡泡"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"溢位"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"展開「<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>」"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"收合「<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>」"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"畫圈搜尋"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"應用程式圖示"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"應用程式標題"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"關閉按鈕"</string>
</resources>
diff --git a/quickstep/res/values-zu/strings.xml b/quickstep/res/values-zu/strings.xml
index b205fad..5209ac1 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>
@@ -47,6 +46,8 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Iziphakamiso zohlelo lokusebenza zinikwe amandla"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Iziphakamiso zohlelo lokusebenza zikhutshaziwe"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Uhlelo lokusebenza olubikezelwe: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
+ <skip />
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Zungezisa idivayisi yakho"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Sicela uzungezise idivayisi yakho ukuze uqedele okokufundisa kokufuna ngokuthinta"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Qinisekisa ukuthi uswayipha ukusuka onqenqemeni olukude ngakwesokudla noma olukude ngakwesokunxele"</string>
@@ -153,4 +154,7 @@
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"nweba <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"goqa <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="search_gesture_feature_title" msgid="1294044108313175306">"Khethela Ukusesha"</string>
+ <string name="header_app_icon_description" msgid="2184625881433608027">"Isithonjana se-app"</string>
+ <string name="header_default_app_title" msgid="8308052350689531566">"Isihloko se-app"</string>
+ <string name="header_close_icon_description" msgid="5400033616675911319">"Inkinobho yokuvala"</string>
</resources>
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index 1f33e08..a530325 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -34,7 +34,6 @@
<string name="launcher_restore_event_logger_class" translatable="false">com.android.quickstep.LauncherRestoreEventLoggerImpl</string>
<string name="taskbar_edu_tooltip_controller_class" translatable="false">com.android.launcher3.taskbar.TaskbarEduTooltipController</string>
<string name="nav_handle_long_press_handler_class" translatable="false"></string>
- <string name="contextual_search_state_manager_class" translatable="false"></string>
<!-- The number of thumbnails and icons to keep in the cache. The thumbnail cache size also
determines how many thumbnails will be fetched in the background. -->
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index b404bb5..b253343 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>
@@ -103,6 +106,9 @@
<dimen name="recents_clear_all_outline_radius">24dp</dimen>
<dimen name="recents_clear_all_outline_padding">2dp</dimen>
+ <!-- Recents add desktop button -->
+ <dimen name="add_desktop_button_size">56dp</dimen>
+
<!-- The speed in dp/s at which the user needs to be scrolling in recents such that we start
loading full resolution screenshots. -->
<dimen name="recents_fast_fling_velocity">600dp</dimen>
@@ -465,6 +471,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 +529,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..d2a7029 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>
@@ -101,6 +98,9 @@
<!-- content description for hotseat items -->
<string name="hotseat_prediction_content_description">Predicted app: <xliff:g id="title" example="Chrome">%1$s</xliff:g></string>
+ <!-- Title of the Gesture Navigation Tutorial page [CHAR LIMIT=NONE] -->
+ <string name="gesture_tutorial_title">Gesture Navigation Tutorial</string>
+
<!-- Title of prompt shown before the gesture navigation tutorial to users who need to rotate their screen. [CHAR LIMIT=100] -->
<string name="gesture_tutorial_rotation_prompt_title">Rotate your device</string>
<!-- Prompt shown before the gesture navigation tutorial to users who need to rotate their screen to begin. [CHAR LIMIT=100] -->
@@ -304,10 +304,6 @@
<string name="taskbar_a11y_shown_with_bubbles_left_title">Taskbar & bubbles left shown</string>
<!-- Accessibility title for the Taskbar window appearing together with bubble bar on right. [CHAR_LIMIT=30] -->
<string name="taskbar_a11y_shown_with_bubbles_right_title">Taskbar & bubbles right shown</string>
- <!-- Accessibility title for the Taskbar window being closed. [CHAR_LIMIT=30] -->
- <string name="taskbar_a11y_hidden_title">Taskbar hidden</string>
- <!-- Accessibility title for the Taskbar window being closed together with bubble bar. [CHAR_LIMIT=30] -->
- <string name="taskbar_a11y_hidden_with_bubbles_title">Taskbar & bubbles hidden</string>
<!-- Accessibility title for the Taskbar window on phones. [CHAR_LIMIT=NONE] -->
<string name="taskbar_phone_a11y_title">Navigation bar</string>
<!-- Text in popup dialog for user to switch between always showing Taskbar or not. [CHAR LIMIT=30] -->
@@ -360,4 +356,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/dagger/Modules.kt b/quickstep/src/com/android/launcher3/dagger/Modules.kt
new file mode 100644
index 0000000..52be413
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/dagger/Modules.kt
@@ -0,0 +1,54 @@
+/*
+ * 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.GestureExclusionManager
+import com.android.quickstep.util.SystemWindowManagerProxy
+import dagger.Binds
+import dagger.Module
+import dagger.Provides
+
+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
+}
+
+@Module
+object StaticObjectModule {
+
+ @Provides
+ @JvmStatic
+ fun provideGestureExclusionManager(): GestureExclusionManager = GestureExclusionManager.INSTANCE
+}
diff --git a/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt b/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt
index 87a82f0..2406fb6 100644
--- a/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt
+++ b/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt
@@ -22,6 +22,7 @@
import android.content.Context
import android.graphics.Rect
import android.os.IBinder
+import android.view.Choreographer
import android.view.SurfaceControl.Transaction
import android.view.WindowManager.TRANSIT_OPEN
import android.view.WindowManager.TRANSIT_TO_BACK
@@ -32,6 +33,8 @@
import android.window.TransitionInfo.Change
import androidx.core.animation.addListener
import com.android.app.animation.Interpolators
+import com.android.internal.jank.Cuj
+import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.policy.ScreenDecorationsUtils
import com.android.quickstep.RemoteRunnable
import com.android.wm.shell.shared.animation.MinimizeAnimator
@@ -49,8 +52,11 @@
private val context: Context,
private val mainExecutor: Executor,
private val launchType: AppLaunchType,
+ @Cuj.CujType private val cujType: Int,
) : RemoteTransitionStub() {
+ private val interactionJankMonitor = InteractionJankMonitor.getInstance()
+
enum class AppLaunchType(
val boundsAnimationParams: WindowAnimator.BoundsAnimationParams,
val alphaDurationMs: Long,
@@ -127,7 +133,10 @@
duration = launchType.alphaDurationMs
interpolator = Interpolators.LINEAR
addUpdateListener { animation ->
- transaction.setAlpha(change.leash, animation.animatedValue as Float).apply()
+ transaction
+ .setAlpha(change.leash, animation.animatedValue as Float)
+ .setFrameTimeline(Choreographer.getInstance().vsyncId)
+ .apply()
}
}
val clipRect = Rect(change.endAbsBounds).apply { offsetTo(0, 0) }
@@ -137,8 +146,14 @@
ScreenDecorationsUtils.getWindowCornerRadius(context),
)
return AnimatorSet().apply {
+ interactionJankMonitor.begin(change.leash, context, context.mainThreadHandler, cujType)
playTogether(boundsAnimator, alphaAnimator)
- addListener(onEnd = { animation -> onAnimFinish(animation) })
+ addListener(
+ onEnd = { animation ->
+ onAnimFinish(animation)
+ interactionJankMonitor.end(cujType)
+ }
+ )
}
}
diff --git a/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransitionManager.kt b/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransitionManager.kt
index 6e36305..6cf9b9e 100644
--- a/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransitionManager.kt
+++ b/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransitionManager.kt
@@ -23,6 +23,7 @@
import android.window.RemoteTransition
import android.window.TransitionFilter
import android.window.TransitionFilter.CONTAINER_ORDER_TOP
+import com.android.internal.jank.Cuj
import com.android.launcher3.desktop.DesktopAppLaunchTransition.AppLaunchType
import com.android.launcher3.util.Executors.MAIN_EXECUTOR
import com.android.quickstep.SystemUiProxy
@@ -45,8 +46,13 @@
}
remoteWindowLimitUnminimizeTransition =
RemoteTransition(
- DesktopAppLaunchTransition(context, MAIN_EXECUTOR, AppLaunchType.UNMINIMIZE),
- "DesktopWindowLimitUnminimize"
+ DesktopAppLaunchTransition(
+ context,
+ MAIN_EXECUTOR,
+ AppLaunchType.UNMINIMIZE,
+ Cuj.CUJ_DESKTOP_MODE_APP_LAUNCH_FROM_INTENT,
+ ),
+ "DesktopWindowLimitUnminimize",
)
systemUiProxy.registerRemoteTransition(
remoteWindowLimitUnminimizeTransition,
diff --git a/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt b/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt
index 8b064d3..010a5c8 100644
--- a/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt
+++ b/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt
@@ -64,11 +64,16 @@
}
/** Launch desktop tasks from recents view */
- fun moveToDesktop(taskContainer: TaskContainer, transitionSource: DesktopModeTransitionSource) {
+ fun moveToDesktop(
+ taskContainer: TaskContainer,
+ transitionSource: DesktopModeTransitionSource,
+ successCallback: Runnable,
+ ) {
systemUiProxy.moveToDesktop(
taskContainer.task.key.id,
transitionSource,
/* transition = */ null,
+ successCallback,
)
}
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java
index 56945ba..70868c5 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java
@@ -17,7 +17,6 @@
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
import static com.android.launcher3.model.PredictionHelper.getAppTargetFromItemInfo;
-import static com.android.launcher3.model.PredictionHelper.isTrackedForHotseatPrediction;
import static com.android.launcher3.model.PredictionHelper.wrapAppTargetWithItemLocation;
import android.app.prediction.AppTarget;
@@ -27,9 +26,12 @@
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
+import com.android.launcher3.model.PredictionHelper;
import com.android.launcher3.model.data.ItemInfo;
import java.util.ArrayList;
+import java.util.Objects;
+import java.util.stream.Collectors;
/**
* Model helper for app predictions in workspace
@@ -43,13 +45,18 @@
*/
public static Bundle convertDataModelToAppTargetBundle(Context context, BgDataModel dataModel) {
Bundle bundle = new Bundle();
- ArrayList<AppTargetEvent> events = new ArrayList<>();
- ArrayList<ItemInfo> workspaceItems = dataModel.getAllWorkspaceItems();
- for (ItemInfo item : workspaceItems) {
- AppTarget target = getAppTargetFromItemInfo(context, item);
- if (target != null && !isTrackedForHotseatPrediction(item)) continue;
- events.add(wrapAppTargetWithItemLocation(target, AppTargetEvent.ACTION_PIN, item));
- }
+ ArrayList<AppTargetEvent> events = dataModel.itemsIdMap
+ .stream()
+ .filter(PredictionHelper::isTrackedForHotseatPrediction)
+ .map(item -> {
+ AppTarget target = getAppTargetFromItemInfo(context, item);
+ return target != null
+ ? wrapAppTargetWithItemLocation(target, AppTargetEvent.ACTION_PIN, item)
+ : null;
+ })
+ .filter(Objects::nonNull)
+ .collect(Collectors.toCollection(ArrayList::new));
+
ArrayList<AppTarget> currentTargets = new ArrayList<>();
FixedContainerItems hotseatItems = dataModel.extraItems.get(CONTAINER_HOTSEAT_PREDICTION);
if (hotseatItems != null) {
diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
index 25e1813..40e8fc2 100644
--- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
+++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
@@ -462,7 +462,7 @@
private Bundle getBundleForWidgetsOnWorkspace(Context context, BgDataModel dataModel) {
Bundle bundle = new Bundle();
ArrayList<AppTargetEvent> widgetEvents =
- dataModel.getAllWorkspaceItems().stream()
+ dataModel.itemsIdMap.stream()
.filter(PredictionHelper::isTrackedForWidgetPrediction)
.map(item -> {
AppTarget target = getAppTargetFromItemInfo(context, item);
diff --git a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
index 9d9054e..40e1c10 100644
--- a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
+++ b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
@@ -18,6 +18,7 @@
import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
import static com.android.launcher3.Flags.enableTieredWidgetsByDefaultInPicker;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
+import static com.android.launcher3.model.ModelUtils.WIDGET_FILTER;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toMap;
@@ -69,9 +70,11 @@
@NonNull AllAppsList apps) {
Predicate<WidgetItem> predictedWidgetsFilter = enableTieredWidgetsByDefaultInPicker()
? dataModel.widgetsModel.getPredictedWidgetsFilter() : null;
- Set<ComponentKey> widgetsInWorkspace = dataModel.appWidgets.stream().map(
- widget -> new ComponentKey(widget.providerName, widget.user)).collect(
- Collectors.toSet());
+ Set<ComponentKey> widgetsInWorkspace = dataModel.itemsIdMap
+ .stream()
+ .filter(WIDGET_FILTER)
+ .map(item -> new ComponentKey(item.getTargetComponent(), item.user))
+ .collect(Collectors.toSet());
// Widgets (excluding shortcuts & already added widgets) that belong to apps eligible for
// being in predictions.
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt
index 5a8302c..0703a61 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt
+++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt
@@ -74,9 +74,9 @@
if (visibleTasksCount != field) {
val wasVisible = field > 0
val isVisible = visibleTasksCount > 0
- val wereDesktopTasksVisibleBefore = areDesktopTasksVisible()
+ val wereDesktopTasksVisibleBefore = areDesktopTasksVisibleAndNotInOverview()
field = visibleTasksCount
- val areDesktopTasksVisibleNow = areDesktopTasksVisible()
+ val areDesktopTasksVisibleNow = areDesktopTasksVisibleAndNotInOverview()
if (wereDesktopTasksVisibleBefore != areDesktopTasksVisibleNow) {
notifyDesktopVisibilityListeners(areDesktopTasksVisibleNow)
}
@@ -192,9 +192,9 @@
)
}
if (overviewStateEnabled != inOverviewState) {
- val wereDesktopTasksVisibleBefore = areDesktopTasksVisible()
+ val wereDesktopTasksVisibleBefore = areDesktopTasksVisibleAndNotInOverview()
inOverviewState = overviewStateEnabled
- val areDesktopTasksVisibleNow = areDesktopTasksVisible()
+ val areDesktopTasksVisibleNow = areDesktopTasksVisibleAndNotInOverview()
if (wereDesktopTasksVisibleBefore != areDesktopTasksVisibleNow) {
notifyDesktopVisibilityListeners(areDesktopTasksVisibleNow)
}
@@ -261,7 +261,7 @@
this.backgroundStateEnabled = backgroundStateEnabled
if (this.backgroundStateEnabled) {
markLauncherResumed()
- } else if (areDesktopTasksVisible() && !gestureInProgress) {
+ } else if (areDesktopTasksVisibleAndNotInOverview() && !gestureInProgress) {
// Switching out of background state. If desktop tasks are visible, pause launcher.
markLauncherPaused()
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java b/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java
index a833ccf..b33fd38 100644
--- a/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.ShortcutInfo;
+import android.os.UserHandle;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
@@ -60,9 +61,9 @@
}
@Override
- public void showAppBubble(Intent intent) {
+ public void showAppBubble(Intent intent, UserHandle user) {
if (intent == null || intent.getPackage() == null) return;
- SystemUiProxy.INSTANCE.get(this).showAppBubble(intent);
+ SystemUiProxy.INSTANCE.get(this).showAppBubble(intent, user);
}
/** Callback invoked when a drag is initiated within this context. */
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
index cd38e5e..3736e6d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
@@ -28,10 +28,12 @@
import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayDragLayer;
import com.android.launcher3.util.TouchController;
+import com.android.quickstep.RecentsFilterState;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.util.DesktopTask;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.util.LayoutUtils;
+import com.android.quickstep.util.SingleTask;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -153,6 +155,8 @@
// Skip the task reload if the list is not changed.
if (!mModel.isTaskListValid(mTaskListChangeId) || !taskIdsToExclude.equals(
mExcludedTaskIds)) {
+ final boolean shouldShowDesktopTasks = mControllers.taskbarDesktopModeController
+ .shouldShowDesktopTasksInTaskbar();
mExcludedTaskIds = taskIdsToExclude;
mTaskListChangeId = mModel.getTasks((tasks) -> {
processLoadedTasks(tasks, taskIdsToExclude);
@@ -162,7 +166,8 @@
currentFocusIndexOverride,
mHasDesktopTask,
mWasDesktopTaskFilteredOut);
- });
+ }, shouldShowDesktopTasks ? RecentsFilterState.EMPTY_FILTER
+ : RecentsFilterState.getEmptyDesktopTaskFilter());
}
mQuickSwitchViewController.updateLayoutForSurface(wasOpenedFromTaskbar,
@@ -188,8 +193,8 @@
mQuickSwitchViewController = new KeyboardQuickSwitchViewController(
mControllers, mOverlayContext, keyboardQuickSwitchView, mControllerCallbacks);
- final boolean onDesktop = mControllers.taskbarDesktopModeController
- .getAreDesktopTasksVisibleAndNotInOverview();
+ final boolean shouldShowDesktopTasks = mControllers.taskbarDesktopModeController
+ .shouldShowDesktopTasksInTaskbar();
if (mModel.isTaskListValid(mTaskListChangeId)
&& taskIdsToExclude.equals(mExcludedTaskIds)) {
@@ -201,7 +206,7 @@
/* updateTasks= */ false,
currentFocusedIndex == -1 && !mControllerCallbacks.isFirstTaskRunning()
? 0 : currentFocusedIndex,
- onDesktop,
+ shouldShowDesktopTasks,
mHasDesktopTask,
mWasDesktopTaskFilteredOut,
wasOpenedFromTaskbar);
@@ -219,11 +224,12 @@
/* updateTasks= */ true,
currentFocusedIndex == -1 && !mControllerCallbacks.isFirstTaskRunning()
? 0 : currentFocusedIndex,
- onDesktop,
+ shouldShowDesktopTasks,
mHasDesktopTask,
mWasDesktopTaskFilteredOut,
wasOpenedFromTaskbar);
- });
+ }, shouldShowDesktopTasks ? RecentsFilterState.EMPTY_FILTER
+ : RecentsFilterState.getEmptyDesktopTaskFilter());
}
private boolean shouldExcludeTask(GroupTask task, Set<Integer> taskIdsToExclude) {
@@ -233,7 +239,7 @@
private void processLoadedTasks(List<GroupTask> tasks, Set<Integer> taskIdsToExclude) {
mHasDesktopTask = false;
mWasDesktopTaskFilteredOut = false;
- if (mControllers.taskbarDesktopModeController.getAreDesktopTasksVisibleAndNotInOverview()) {
+ if (mControllers.taskbarDesktopModeController.shouldShowDesktopTasksInTaskbar()) {
processLoadedTasksOnDesktop(tasks, taskIdsToExclude);
} else {
processLoadedTasksOutsideDesktop(tasks, taskIdsToExclude);
@@ -269,8 +275,8 @@
DesktopTask desktopTask = findDesktopTask(tasks);
if (desktopTask != null) {
- mTasks = desktopTask.tasks.stream()
- .map(GroupTask::new)
+ mTasks = desktopTask.getTasks().stream()
+ .map(SingleTask::new)
.filter(task -> !shouldExcludeTask(task, taskIdsToExclude))
.collect(Collectors.toList());
// All other tasks, apart from the grouped desktop task, are hidden
@@ -349,15 +355,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 +414,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/KeyboardQuickSwitchViewController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
index cb811d6..8cb43d2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
@@ -38,7 +38,6 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.desktop.DesktopAppLaunchTransition;
-import com.android.launcher3.desktop.DesktopAppLaunchTransition.AppLaunchType;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayDragLayer;
import com.android.launcher3.util.DisplayController;
@@ -50,6 +49,7 @@
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.QuickStepContract;
+import com.android.wm.shell.shared.desktopmode.DesktopTaskToFrontReason;
import java.io.PrintWriter;
import java.util.List;
@@ -286,13 +286,19 @@
) {
// This app is being unminimized - use our own transition runner.
remoteTransition = new RemoteTransition(
- new DesktopAppLaunchTransition(context, MAIN_EXECUTOR, UNMINIMIZE),
+ new DesktopAppLaunchTransition(
+ context,
+ MAIN_EXECUTOR,
+ UNMINIMIZE,
+ Cuj.CUJ_DESKTOP_MODE_KEYBOARD_QUICK_SWITCH_APP_LAUNCH
+ ),
"DesktopKeyboardQuickSwitchUnminimize");
}
mControllers.taskbarActivityContext.handleGroupTaskLaunch(
task,
remoteTransition,
mOnDesktop,
+ DesktopTaskToFrontReason.ALT_TAB,
onStartCallback,
onFinishCallback);
return -1;
diff --git a/quickstep/src/com/android/launcher3/taskbar/ManageWindowsTaskbarShortcut.kt b/quickstep/src/com/android/launcher3/taskbar/ManageWindowsTaskbarShortcut.kt
index 75ce7c3..bfd93dd 100644
--- a/quickstep/src/com/android/launcher3/taskbar/ManageWindowsTaskbarShortcut.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/ManageWindowsTaskbarShortcut.kt
@@ -27,7 +27,6 @@
import com.android.launcher3.popup.SystemShortcut
import com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_MULTI_INSTANCE_MENU_OPEN
import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext
-import com.android.launcher3.util.Themes
import com.android.launcher3.util.TouchController
import com.android.launcher3.views.ActivityContext
import com.android.quickstep.RecentsModel
@@ -35,9 +34,8 @@
import com.android.quickstep.util.DesktopTask
import com.android.systemui.shared.recents.model.Task
import com.android.systemui.shared.recents.model.ThumbnailData
+import com.android.wm.shell.shared.desktopmode.DesktopTaskToFrontReason
import com.android.wm.shell.shared.multiinstance.ManageWindowsViewContainer
-import java.util.Collections
-import java.util.function.Predicate
/**
* A single menu item shortcut to execute displaying open instances of an app. Default interaction
@@ -72,7 +70,7 @@
val packageDesktopTasks =
(desktopTask?.tasks ?: emptyList()).filter(isTargetPackageTask)
val nonDesktopPackageTasks =
- tasks.filter { isTargetPackageTask(it.task1) }.map { it.task1 }
+ tasks.flatMap { it.tasks }.filter { isTargetPackageTask(it) }
// Add tasks from the fetched tasks, deduplicating by task ID
val packageTasks =
@@ -120,7 +118,12 @@
({ taskId: Int? ->
taskbarShortcutAllWindowsView.animateClose()
if (taskId != null) {
- SystemUiProxy.INSTANCE.get(target).showDesktopApp(taskId, null)
+ SystemUiProxy.INSTANCE.get(target)
+ .showDesktopApp(
+ taskId,
+ /* transition= */ null,
+ DesktopTaskToFrontReason.TASKBAR_MANAGE_WINDOW,
+ )
}
})
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index 1144ac5..289e720 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -42,7 +42,7 @@
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BACK_DISABLED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_BUTTON_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
@@ -129,7 +129,9 @@
private final Rect mTempRect = new Rect();
- private static final int FLAG_SWITCHER_SHOWING = 1 << 0;
+ /** Whether the IME Switcher button is visible. */
+ private static final int FLAG_IME_SWITCHER_BUTTON_VISIBLE = 1 << 0;
+ /** Whether the IME is visible. */
private static final int FLAG_IME_VISIBLE = 1 << 1;
private static final int FLAG_ROTATION_BUTTON_VISIBLE = 1 << 2;
private static final int FLAG_A11Y_VISIBLE = 1 << 3;
@@ -301,7 +303,7 @@
isThreeButtonNav ? mStartContextualContainer : mEndContextualContainer,
mControllers.navButtonController, R.id.ime_switcher);
mPropertyHolders.add(new StatePropertyHolder(mImeSwitcherButton,
- flags -> ((flags & FLAG_SWITCHER_SHOWING) != 0)
+ flags -> ((flags & FLAG_IME_SWITCHER_BUTTON_VISIBLE) != 0)
&& ((flags & FLAG_ROTATION_BUTTON_VISIBLE) == 0)));
}
@@ -507,8 +509,9 @@
private void parseSystemUiFlags(@SystemUiStateFlags long sysUiStateFlags) {
mSysuiStateFlags = sysUiStateFlags;
+ boolean isImeSwitcherButtonVisible =
+ (sysUiStateFlags & SYSUI_STATE_IME_SWITCHER_BUTTON_SHOWING) != 0;
boolean isImeVisible = (sysUiStateFlags & SYSUI_STATE_IME_SHOWING) != 0;
- boolean isImeSwitcherShowing = (sysUiStateFlags & SYSUI_STATE_IME_SWITCHER_SHOWING) != 0;
boolean a11yVisible = (sysUiStateFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
boolean isHomeDisabled = (sysUiStateFlags & SYSUI_STATE_HOME_DISABLED) != 0;
boolean isRecentsDisabled = (sysUiStateFlags & SYSUI_STATE_OVERVIEW_DISABLED) != 0;
@@ -522,9 +525,8 @@
boolean isKeyboardShortcutHelperShowing =
(sysUiStateFlags & SYSUI_STATE_SHORTCUT_HELPER_SHOWING) != 0;
- // TODO(b/202218289) we're getting IME as not visible on lockscreen from system
+ updateStateForFlag(FLAG_IME_SWITCHER_BUTTON_VISIBLE, isImeSwitcherButtonVisible);
updateStateForFlag(FLAG_IME_VISIBLE, isImeVisible);
- updateStateForFlag(FLAG_SWITCHER_SHOWING, isImeSwitcherShowing);
updateStateForFlag(FLAG_A11Y_VISIBLE, a11yVisible);
updateStateForFlag(FLAG_DISABLE_HOME, isHomeDisabled);
updateStateForFlag(FLAG_DISABLE_RECENTS, isRecentsDisabled);
@@ -1226,7 +1228,8 @@
private static String getStateString(int flags) {
StringJoiner str = new StringJoiner("|");
- appendFlag(str, flags, FLAG_SWITCHER_SHOWING, "FLAG_SWITCHER_SHOWING");
+ appendFlag(str, flags, FLAG_IME_SWITCHER_BUTTON_VISIBLE,
+ "FLAG_IME_SWITCHER_BUTTON_VISIBLE");
appendFlag(str, flags, FLAG_IME_VISIBLE, "FLAG_IME_VISIBLE");
appendFlag(str, flags, FLAG_ROTATION_BUTTON_VISIBLE, "FLAG_ROTATION_BUTTON_VISIBLE");
appendFlag(str, flags, FLAG_A11Y_VISIBLE, "FLAG_A11Y_VISIBLE");
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index ee9c6a1..e8a0c45 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -59,7 +59,6 @@
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
-import android.os.Bundle;
import android.os.IRemoteCallback;
import android.os.Process;
import android.os.Trace;
@@ -82,8 +81,10 @@
import androidx.core.graphics.Insets;
import androidx.core.view.WindowInsetsCompat;
+import com.android.internal.jank.Cuj;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.BubbleTextView.RunningAppState;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.LauncherSettings.Favorites;
@@ -140,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;
@@ -163,6 +165,7 @@
import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
import com.android.systemui.unfold.updates.RotationChangeProvider;
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
+import com.android.wm.shell.shared.desktopmode.DesktopTaskToFrontReason;
import java.io.PrintWriter;
import java.util.Collections;
@@ -194,6 +197,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;
/**
@@ -228,6 +232,7 @@
private DeviceProfile mPersistentTaskbarDeviceProfile;
private final LauncherPrefs mLauncherPrefs;
+ private final SystemUiProxy mSysUiProxy;
private TaskbarFeatureEvaluator mTaskbarFeatureEvaluator;
@@ -237,10 +242,11 @@
@Nullable Context navigationBarPanelContext, DeviceProfile launcherDp,
TaskbarNavButtonController buttonController,
ScopedUnfoldTransitionProgressProvider unfoldTransitionProgressProvider,
- boolean isPrimaryDisplay) {
+ boolean isPrimaryDisplay, SystemUiProxy sysUiProxy) {
super(windowContext);
mIsPrimaryDisplay = isPrimaryDisplay;
mNavigationBarPanelContext = navigationBarPanelContext;
+ mSysUiProxy = sysUiProxy;
applyDeviceProfile(launcherDp);
final Resources resources = getResources();
mTaskbarFeatureEvaluator = TaskbarFeatureEvaluator.getInstance(this);
@@ -269,8 +275,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);
@@ -362,7 +367,8 @@
new KeyboardQuickSwitchController(),
new TaskbarPinningController(this),
bubbleControllersOptional,
- new TaskbarDesktopModeController(DesktopVisibilityController.INSTANCE.get(this)));
+ new TaskbarDesktopModeController(this,
+ DesktopVisibilityController.INSTANCE.get(this)));
mLauncherPrefs = LauncherPrefs.get(this);
}
@@ -383,6 +389,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
@@ -880,32 +891,10 @@
return makeDefaultActivityOptions(SPLASH_SCREEN_STYLE_UNDEFINED);
}
- private ActivityOptionsWrapper getActivityLaunchDesktopOptions(ItemInfo info) {
- if (!DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS.isTrue()
- && !DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX.isTrue()) {
- return null;
- }
- if (!areDesktopTasksVisible()) {
- return null;
- }
- BubbleTextView.RunningAppState appState =
- mControllers.taskbarRecentAppsController.getDesktopItemState(info);
- AppLaunchType launchType = null;
- switch (appState) {
- case RUNNING:
- return null;
- case MINIMIZED:
- launchType = AppLaunchType.UNMINIMIZE;
- break;
- case NOT_RUNNING:
- launchType = AppLaunchType.LAUNCH;
- break;
- }
+ private ActivityOptionsWrapper getActivityLaunchDesktopOptions() {
ActivityOptions options = ActivityOptions.makeRemoteTransition(
- new RemoteTransition(
- new DesktopAppLaunchTransition(
- /* context= */ this, getMainExecutor(), launchType),
- "TaskbarDesktopLaunch"));
+ createDesktopAppLaunchRemoteTransition(
+ AppLaunchType.LAUNCH, Cuj.CUJ_DESKTOP_MODE_APP_LAUNCH_FROM_ICON));
return new ActivityOptionsWrapper(options, new RunnableList());
}
@@ -1005,6 +994,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) {
@@ -1018,11 +1009,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();
@@ -1298,7 +1296,9 @@
if (tag instanceof GroupTask groupTask) {
RemoteTransition remoteTransition =
(areDesktopTasksVisible() && canUnminimizeDesktopTask(groupTask.task1.key.id))
- ? createUnminimizeRemoteTransition() : null;
+ ? createDesktopAppLaunchRemoteTransition(AppLaunchType.UNMINIMIZE,
+ Cuj.CUJ_DESKTOP_MODE_APP_LAUNCH_FROM_ICON)
+ : null;
if (areDesktopTasksVisible() && mControllers.uiController.isInOverviewUi()) {
RunnableList runnableList = recents.launchRunningDesktopTaskView();
// Wrapping it in runnable so we post after DW is ready for the app
@@ -1306,10 +1306,12 @@
if (runnableList != null) {
runnableList.add(() -> UI_HELPER_EXECUTOR.execute(
() -> handleGroupTaskLaunch(groupTask, remoteTransition,
- areDesktopTasksVisible())));
+ areDesktopTasksVisible(),
+ DesktopTaskToFrontReason.TASKBAR_TAP)));
}
} else {
- handleGroupTaskLaunch(groupTask, remoteTransition, areDesktopTasksVisible());
+ handleGroupTaskLaunch(groupTask, remoteTransition, areDesktopTasksVisible(),
+ DesktopTaskToFrontReason.TASKBAR_TAP);
}
mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
} else if (tag instanceof FolderInfo) {
@@ -1329,14 +1331,17 @@
}
} else if (tag instanceof TaskItemInfo info) {
RemoteTransition remoteTransition = canUnminimizeDesktopTask(info.getTaskId())
- ? createUnminimizeRemoteTransition() : null;
+ ? createDesktopAppLaunchRemoteTransition(
+ AppLaunchType.UNMINIMIZE, Cuj.CUJ_DESKTOP_MODE_APP_LAUNCH_FROM_ICON)
+ : null;
TaskView taskView = null;
if (recents != null) {
taskView = recents.getTaskViewByTaskId(info.getTaskId());
}
- if (areDesktopTasksVisible() && taskView != null) {
+ if (areDesktopTasksVisible() && taskView != null
+ && mControllers.uiController.isInOverviewUi()) {
RunnableList runnableList = taskView.launchWithAnimation();
if (runnableList != null) {
runnableList.add(() ->
@@ -1345,12 +1350,14 @@
// task will show.
UI_HELPER_EXECUTOR.execute(() ->
SystemUiProxy.INSTANCE.get(this).showDesktopApp(
- info.getTaskId(), remoteTransition)));
+ info.getTaskId(), remoteTransition,
+ DesktopTaskToFrontReason.TASKBAR_TAP)));
}
} else {
UI_HELPER_EXECUTOR.execute(() ->
SystemUiProxy.INSTANCE.get(this).showDesktopApp(
- info.getTaskId(), remoteTransition));
+ info.getTaskId(), remoteTransition,
+ DesktopTaskToFrontReason.TASKBAR_TAP));
}
mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(
@@ -1443,8 +1450,9 @@
public void handleGroupTaskLaunch(
GroupTask task,
@Nullable RemoteTransition remoteTransition,
- boolean onDesktop) {
- handleGroupTaskLaunch(task, remoteTransition, onDesktop,
+ boolean onDesktop,
+ DesktopTaskToFrontReason toFrontReason) {
+ handleGroupTaskLaunch(task, remoteTransition, onDesktop, toFrontReason,
/* onStartCallback= */ null, /* onFinishCallback= */ null);
}
@@ -1463,6 +1471,7 @@
GroupTask task,
@Nullable RemoteTransition remoteTransition,
boolean onDesktop,
+ DesktopTaskToFrontReason toFrontReason,
@Nullable Runnable onStartCallback,
@Nullable Runnable onFinishCallback) {
if (task instanceof DesktopTask) {
@@ -1477,8 +1486,8 @@
if (onStartCallback != null) {
onStartCallback.run();
}
- SystemUiProxy.INSTANCE.get(this).showDesktopApp(
- task.task1.key.id, useRemoteTransition ? remoteTransition : null);
+ SystemUiProxy.INSTANCE.get(this).showDesktopApp(task.task1.key.id,
+ useRemoteTransition ? remoteTransition : null, toFrontReason);
if (onFinishCallback != null) {
onFinishCallback.run();
}
@@ -1503,16 +1512,22 @@
public boolean canUnminimizeDesktopTask(int taskId) {
BubbleTextView.RunningAppState runningAppState =
mControllers.taskbarRecentAppsController.getRunningAppState(taskId);
- return runningAppState == BubbleTextView.RunningAppState.MINIMIZED
+ return runningAppState == RunningAppState.MINIMIZED
&& (DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_ALTTAB_TRANSITIONS.isTrue()
|| DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_ALTTAB_TRANSITIONS_BUGFIX.isTrue()
);
}
- private RemoteTransition createUnminimizeRemoteTransition() {
+ private RemoteTransition createDesktopAppLaunchRemoteTransition(
+ AppLaunchType appLaunchType, @Cuj.CujType int cujType) {
return new RemoteTransition(
- new DesktopAppLaunchTransition(this, getMainExecutor(), AppLaunchType.UNMINIMIZE),
- "TaskbarDesktopUnminimize");
+ new DesktopAppLaunchTransition(
+ this,
+ getMainExecutor(),
+ appLaunchType,
+ cujType
+ ),
+ "TaskbarDesktopAppLaunch");
}
/**
@@ -1630,12 +1645,12 @@
intent.getComponent(), info.user, intent.getSourceBounds(), null);
return;
}
+ int displayId = getDisplay() == null ? DEFAULT_DISPLAY : getDisplay().getDisplayId();
// TODO(b/216683257): Use startActivityForResult for search results that require it.
if (taskInRecents != null) {
// Re launch instance from recents
ActivityOptionsWrapper opts = getActivityLaunchOptions(null, info);
- opts.options.setLaunchDisplayId(
- getDisplay() == null ? DEFAULT_DISPLAY : getDisplay().getDisplayId());
+ opts.options.setLaunchDisplayId(displayId);
if (ActivityManagerWrapper.getInstance()
.startActivityFromRecents(taskInRecents.key, opts.options)) {
mControllers.uiController.getRecentsView()
@@ -1643,12 +1658,12 @@
return;
}
}
- ActivityOptionsWrapper opts = null;
- if (areDesktopTasksVisible()) {
- opts = getActivityLaunchDesktopOptions(info);
+ if (areDesktopTasksVisible()
+ && DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX.isTrue()) {
+ launchDesktopApp(intent, info, displayId);
+ } else {
+ startActivity(intent, null);
}
- Bundle optionsBundle = opts == null ? null : opts.options.toBundle();
- startActivity(intent, optionsBundle);
} catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT)
.show();
@@ -1656,6 +1671,27 @@
}
}
+ private void launchDesktopApp(Intent intent, ItemInfo info, int displayId) {
+ TaskbarRecentAppsController.TaskState taskState =
+ mControllers.taskbarRecentAppsController.getDesktopItemState(info);
+ RunningAppState appState = taskState.getRunningAppState();
+ if (appState == RunningAppState.RUNNING || appState == RunningAppState.MINIMIZED) {
+ // We only need a custom animation (a RemoteTransition) if the task is minimized - if
+ // it's already visible it will just be brought forward.
+ RemoteTransition remoteTransition = (appState == RunningAppState.MINIMIZED)
+ ? createDesktopAppLaunchRemoteTransition(
+ AppLaunchType.UNMINIMIZE, Cuj.CUJ_DESKTOP_MODE_APP_LAUNCH_FROM_ICON)
+ : null;
+ UI_HELPER_EXECUTOR.execute(() ->
+ SystemUiProxy.INSTANCE.get(this).showDesktopApp(taskState.getTaskId(),
+ remoteTransition, DesktopTaskToFrontReason.TASKBAR_TAP));
+ return;
+ }
+ // There is no task associated with this launch - launch a new task through an intent
+ ActivityOptionsWrapper opts = getActivityLaunchDesktopOptions();
+ mSysUiProxy.startLaunchIntentTransition(intent, opts.options.toBundle(), displayId);
+ }
+
/** Expands a folder icon when it is clicked */
private void expandFolder(FolderIcon folderIcon) {
Folder folder = folderIcon.getFolder();
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/TaskbarDesktopModeController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarDesktopModeController.kt
index a7c7381..8806bc6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDesktopModeController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDesktopModeController.kt
@@ -16,13 +16,16 @@
package com.android.launcher3.taskbar
+import android.content.Context
import com.android.launcher3.statehandlers.DesktopVisibilityController
import com.android.launcher3.statehandlers.DesktopVisibilityController.TaskbarDesktopModeListener
import com.android.launcher3.taskbar.TaskbarBackgroundRenderer.Companion.MAX_ROUNDNESS
+import com.android.launcher3.util.DisplayController
/** Handles Taskbar in Desktop Windowing mode. */
class TaskbarDesktopModeController(
- private val desktopVisibilityController: DesktopVisibilityController
+ private val context: Context,
+ private val desktopVisibilityController: DesktopVisibilityController,
) : TaskbarDesktopModeListener {
private lateinit var taskbarControllers: TaskbarControllers
private lateinit var taskbarSharedState: TaskbarSharedState
@@ -45,6 +48,12 @@
taskbarControllers.taskbarCornerRoundness.animateToValue(cornerRadius).start()
}
+ fun shouldShowDesktopTasksInTaskbar(): Boolean {
+ return desktopVisibilityController.areDesktopTasksVisible() ||
+ DisplayController.showLockedTaskbarOnHome(context) &&
+ taskbarControllers.taskbarStashController.isOnHome
+ }
+
fun getTaskbarCornerRoundness(doesAnyTaskRequireTaskbarRounding: Boolean): Float {
return if (doesAnyTaskRequireTaskbarRounding) {
MAX_ROUNDNESS
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/TaskbarHoverToolTipController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarHoverToolTipController.java
index 3bff31f..b7000db 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarHoverToolTipController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarHoverToolTipController.java
@@ -108,7 +108,7 @@
revealHoverToolTip();
mActivity.setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS, true);
}
- return true;
+ return false;
}
private void revealHoverToolTip() {
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 3fa0e8e..b46b0dc 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -61,7 +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.statemanager.StatefulActivity;
import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarNavButtonCallbacks;
import com.android.launcher3.taskbar.unfold.NonDestroyableScopedUnfoldTransitionProgressProvider;
@@ -90,6 +89,8 @@
public class TaskbarManager {
private static final String TAG = "TaskbarManager";
private static final boolean DEBUG = false;
+ // TODO(b/382378283) remove all logs with this tag
+ public static final String NULL_TASKBAR_ROOT_LAYOUT_TAG = "b/382378283";
/**
* All the configurations which do not initiate taskbar recreation.
@@ -152,6 +153,20 @@
private class RecreationListener implements DisplayController.DisplayInfoChangeListener {
@Override
public void onDisplayInfoChanged(Context context, DisplayController.Info info, int flags) {
+
+ if ((flags & CHANGE_DENSITY) != 0) {
+ Log.d(NULL_TASKBAR_ROOT_LAYOUT_TAG, "Display density changed");
+ }
+ if ((flags & CHANGE_NAVIGATION_MODE) != 0) {
+ Log.d(NULL_TASKBAR_ROOT_LAYOUT_TAG, "Navigation mode changed");
+ }
+ if ((flags & CHANGE_DESKTOP_MODE) != 0) {
+ Log.d(NULL_TASKBAR_ROOT_LAYOUT_TAG, "Desktop mode changed");
+ }
+ if ((flags & CHANGE_TASKBAR_PINNING) != 0) {
+ Log.d(NULL_TASKBAR_ROOT_LAYOUT_TAG, "Taskbar pinning changed");
+ }
+
if ((flags & (CHANGE_DENSITY | CHANGE_NAVIGATION_MODE | CHANGE_DESKTOP_MODE
| CHANGE_TASKBAR_PINNING)) != 0) {
recreateTaskbar();
@@ -270,7 +285,6 @@
context,
navCallbacks,
SystemUiProxy.INSTANCE.get(mWindowContext),
- ContextualEduStatsManager.INSTANCE.get(mWindowContext),
new Handler(),
new ContextualSearchInvoker(mWindowContext));
}
@@ -347,13 +361,13 @@
private void destroyTaskbarForDisplay(int displayId) {
TaskbarActivityContext taskbar = getTaskbarForDisplay(displayId);
- debugWhyTaskbarNotDestroyed(
- "destroyTaskbarForDisplay: " + taskbar + " displayId=" + displayId);
+ debugWhyTaskbarNotDestroyed("destroyTaskbarForDisplay: " + taskbar, displayId);
if (taskbar != null) {
taskbar.onDestroy();
// remove all defaults that we store
removeTaskbarFromMap(displayId);
}
+ // make this display-specific
DeviceProfile dp = mUserUnlocked ?
LauncherAppState.getIDP(mWindowContext).getDeviceProfile(mWindowContext) : null;
if (dp == null || !isTaskbarEnabled(dp)) {
@@ -508,6 +522,7 @@
private void recreateTaskbarForDisplay(int displayId) {
Trace.beginSection("recreateTaskbar");
try {
+ // TODO: make this code display specific
DeviceProfile dp = mUserUnlocked ?
LauncherAppState.getIDP(mWindowContext).getDeviceProfile(mWindowContext) : null;
@@ -549,9 +564,14 @@
if (enableTaskbarNoRecreate()) {
addTaskbarRootViewToWindow(displayId);
FrameLayout taskbarRootLayout = getTaskbarRootLayoutForDisplay(displayId);
- taskbarRootLayout.removeAllViews();
- taskbarRootLayout.addView(taskbar.getDragLayer());
- taskbar.notifyUpdateLayoutParams();
+ if (taskbarRootLayout != null) {
+ taskbarRootLayout.removeAllViews();
+ taskbarRootLayout.addView(taskbar.getDragLayer());
+ taskbar.notifyUpdateLayoutParams();
+ } else {
+ Log.e(NULL_TASKBAR_ROOT_LAYOUT_TAG,
+ "taskbarRootLayout is null for displayId=" + displayId);
+ }
}
} finally {
Trace.endSection();
@@ -689,6 +709,20 @@
}
}
+ /**
+ * Signal from SysUI indicating that a non-mirroring display was just connected to the
+ * primary device.
+ */
+ public void onDisplayReady(int displayId) {
+ }
+
+ /**
+ * Signal from SysUI indicating that a previously connected non-mirroring display was just
+ * removed from the primary device.
+ */
+ public void onDisplayRemoved(int displayId) {
+ }
+
private void removeActivityCallbacksAndListeners() {
if (mActivity != null) {
mActivity.removeOnDeviceProfileChangeListener(mDebugActivityDeviceProfileChanged);
@@ -732,17 +766,25 @@
public void dumpLogs(String prefix, PrintWriter pw) {
pw.println(prefix + "TaskbarManager:");
- TaskbarActivityContext taskbar = getTaskbarForDisplay(getDefaultDisplayId());
- if (taskbar == null) {
- pw.println(prefix + "\tTaskbarActivityContext: null");
- } else {
- taskbar.dumpLogs(prefix + "\t", pw);
+ // iterate through taskbars and do the dump for each
+ for (int i = 0; i < mTaskbars.size(); i++) {
+ int displayId = mTaskbars.keyAt(i);
+ TaskbarActivityContext taskbar = mTaskbars.get(i);
+ pw.println(prefix + "\tTaskbar at display " + displayId + ":");
+ if (taskbar == null) {
+ pw.println(prefix + "\t\tTaskbarActivityContext: null");
+ } else {
+ taskbar.dumpLogs(prefix + "\t\t", pw);
+ }
}
+
}
private void addTaskbarRootViewToWindow(int displayId) {
TaskbarActivityContext taskbar = getTaskbarForDisplay(displayId);
if (!enableTaskbarNoRecreate() || taskbar == null) {
+ Log.d(NULL_TASKBAR_ROOT_LAYOUT_TAG,
+ "addTaskbarRootViewToWindow - taskbar null | displayId=" + displayId);
return;
}
@@ -750,6 +792,10 @@
mWindowManager.addView(getTaskbarRootLayoutForDisplay(displayId),
taskbar.getWindowLayoutParams());
mAddedRootLayouts.put(displayId, true);
+ } else {
+ Log.d(NULL_TASKBAR_ROOT_LAYOUT_TAG,
+ "addTaskbarRootViewToWindow - root layout already added | displayId="
+ + displayId);
}
}
@@ -795,7 +841,8 @@
private TaskbarActivityContext createTaskbarActivityContext(DeviceProfile dp, int displayId) {
TaskbarActivityContext newTaskbar = new TaskbarActivityContext(mWindowContext,
mNavigationBarPanelContext, dp, mDefaultNavButtonController,
- mUnfoldProgressProvider, isDefaultDisplay(displayId));
+ mUnfoldProgressProvider, isDefaultDisplay(displayId),
+ SystemUiProxy.INSTANCE.get(mWindowContext));
addTaskbarToMap(displayId, newTaskbar);
return newTaskbar;
@@ -841,6 +888,7 @@
}
};
addTaskbarRootLayoutToMap(displayId, newTaskbarRootLayout);
+ Log.d(NULL_TASKBAR_ROOT_LAYOUT_TAG, "created new root layout - displayId=" + displayId);
}
private boolean isDefaultDisplay(int displayId) {
@@ -854,7 +902,14 @@
* @return The taskbar root layout {@link FrameLayout} for a given display or {@code null}.
*/
private FrameLayout getTaskbarRootLayoutForDisplay(int displayId) {
- return mRootLayouts.get(displayId);
+ FrameLayout frameLayout = mRootLayouts.get(displayId);
+ if (frameLayout != null) {
+ return frameLayout;
+ } else {
+ Log.d(NULL_TASKBAR_ROOT_LAYOUT_TAG,
+ "getTaskbarRootLayoutForDisplay == null | displayId=" + displayId);
+ return null;
+ }
}
/**
@@ -867,6 +922,8 @@
if (!mRootLayouts.contains(displayId) && rootLayout != null) {
mRootLayouts.put(displayId, rootLayout);
}
+
+ Log.d(NULL_TASKBAR_ROOT_LAYOUT_TAG, "mRootLayouts.size()=" + mRootLayouts.size());
}
/**
@@ -879,6 +936,8 @@
mAddedRootLayouts.delete(displayId);
mRootLayouts.delete(displayId);
}
+
+ Log.d(NULL_TASKBAR_ROOT_LAYOUT_TAG, "mRootLayouts.size()=" + mRootLayouts.size());
}
private int getDefaultDisplayId() {
@@ -887,11 +946,17 @@
/** Temp logs for b/254119092. */
public void debugWhyTaskbarNotDestroyed(String debugReason) {
+ debugWhyTaskbarNotDestroyed(debugReason, getDefaultDisplayId());
+ }
+
+ /** Temp logs for b/254119092. */
+ public void debugWhyTaskbarNotDestroyed(String debugReason, int displayId) {
StringJoiner log = new StringJoiner("\n");
- log.add(debugReason);
+ log.add(debugReason + " displayId=" + displayId);
boolean activityTaskbarPresent = mActivity != null
&& mActivity.getDeviceProfile().isTaskbarPresent;
+ // TODO: make this display specific
boolean contextTaskbarPresent = mUserUnlocked && LauncherAppState.getIDP(mWindowContext)
.getDeviceProfile(mWindowContext).isTaskbarPresent;
if (activityTaskbarPresent == contextTaskbarPresent) {
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/TaskbarOverflowView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarOverflowView.java
index 0ed6669..017a12c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarOverflowView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarOverflowView.java
@@ -39,7 +39,6 @@
import androidx.core.graphics.ColorUtils;
import com.android.app.animation.Interpolators;
-import com.android.launcher3.R;
import com.android.launcher3.Reorderable;
import com.android.launcher3.Utilities;
import com.android.launcher3.icons.IconNormalizer;
@@ -243,7 +242,9 @@
private void init() {
mIsRtlLayout = Utilities.isRtl(getResources());
mItemBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mItemBackgroundColor = getContext().getColor(R.color.taskbar_background);
+ mItemBackgroundColor = getContext().getColor(Utilities.isDarkTheme(getContext())
+ ? com.android.internal.R.color.materialColorSurface
+ : com.android.internal.R.color.materialColorInverseOnSurface);
mLeaveBehindColor = Themes.getAttrColor(getContext(), android.R.attr.textColorTertiary);
setWillNotDraw(false);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
index e704691..feb9b33 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
@@ -56,6 +56,7 @@
import com.android.launcher3.views.ActivityContext;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.LogUtils;
+import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import java.io.PrintWriter;
@@ -219,7 +220,7 @@
.getAreDesktopTasksVisibleAndNotInOverview()) {
shortcuts.addAll(mControllers.uiController.getSplitMenuOptions().toList());
}
- if (com.android.wm.shell.Flags.enableBubbleAnything()) {
+ if (BubbleAnythingFlagHelper.enableCreateAnyBubble()) {
shortcuts.add(BUBBLE);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt
index a059b22..6047999 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt
@@ -19,15 +19,18 @@
import android.window.DesktopModeFlags
import androidx.annotation.VisibleForTesting
import com.android.launcher3.BubbleTextView.RunningAppState
+import com.android.launcher3.Flags
import com.android.launcher3.Flags.enableRecentsInTaskbar
import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.model.data.TaskItemInfo
import com.android.launcher3.model.data.WorkspaceItemInfo
import com.android.launcher3.taskbar.TaskbarControllers.LoggableTaskbarController
import com.android.launcher3.util.CancellableTask
+import com.android.quickstep.RecentsFilterState
import com.android.quickstep.RecentsModel
import com.android.quickstep.util.DesktopTask
import com.android.quickstep.util.GroupTask
+import com.android.quickstep.util.SingleTask
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import java.io.PrintWriter
@@ -74,30 +77,43 @@
private set
/**
+ * The task-state of an app, i.e. whether the app has a task and what state
+ * that task is in.
+ *
+ * @property taskId The ID of the task if one exists (i.e. if the state is
+ * RUNNING or MINIMIZED), null otherwise (NOT_RUNNING).
+ */
+ data class TaskState(val runningAppState: RunningAppState, val taskId: Int? = null)
+
+ /**
* Returns the state of the most active Desktop task represented by the given [ItemInfo].
*
* If there are several tasks represented by the same [ItemInfo] we return the most active one,
* i.e. we return [DesktopAppState.RUNNING] over [DesktopAppState.MINIMIZED], and
* [DesktopAppState.MINIMIZED] over [DesktopAppState.NOT_RUNNING].
*/
- fun getDesktopItemState(itemInfo: ItemInfo?): RunningAppState {
- val packageName = itemInfo?.getTargetPackage() ?: return RunningAppState.NOT_RUNNING
- return getDesktopAppState(packageName, itemInfo.user.identifier)
+ fun getDesktopItemState(itemInfo: ItemInfo?): TaskState {
+ val packageName =
+ itemInfo?.getTargetPackage() ?: return TaskState(RunningAppState.NOT_RUNNING)
+ return getDesktopTaskState(packageName, itemInfo.user.identifier)
}
- private fun getDesktopAppState(packageName: String, userId: Int): RunningAppState {
- val tasks = desktopTask?.tasks ?: return RunningAppState.NOT_RUNNING
+ private fun getDesktopTaskState(packageName: String, userId: Int): TaskState {
+ val tasks = desktopTask?.tasks ?: return TaskState(RunningAppState.NOT_RUNNING)
val appTasks =
tasks.filter { task ->
packageName == task.key.packageName && task.key.userId == userId
}
- if (appTasks.find { getRunningAppState(it.key.id) == RunningAppState.RUNNING } != null) {
- return RunningAppState.RUNNING
+ val runningTask = appTasks.find { getRunningAppState(it.key.id) == RunningAppState.RUNNING }
+ if (runningTask != null) {
+ return TaskState(RunningAppState.RUNNING, runningTask.key.id)
}
- if (appTasks.find { getRunningAppState(it.key.id) == RunningAppState.MINIMIZED } != null) {
- return RunningAppState.MINIMIZED
+ val minimizedTask =
+ appTasks.find { getRunningAppState(it.key.id) == RunningAppState.MINIMIZED }
+ if (minimizedTask != null) {
+ return TaskState(RunningAppState.MINIMIZED, taskId = minimizedTask.key.id)
}
- return RunningAppState.NOT_RUNNING
+ return TaskState(RunningAppState.NOT_RUNNING)
}
/** Get the [RunningAppState] for the given task. */
@@ -118,7 +134,7 @@
get() {
if (
!canShowRunningApps ||
- !controllers.taskbarDesktopModeController.areDesktopTasksVisible
+ !controllers.taskbarDesktopModeController.shouldShowDesktopTasksInTaskbar()
) {
return emptySet()
}
@@ -134,7 +150,7 @@
get() {
if (
!canShowRunningApps ||
- !controllers.taskbarDesktopModeController.areDesktopTasksVisible
+ !controllers.taskbarDesktopModeController.shouldShowDesktopTasksInTaskbar()
) {
return emptySet()
}
@@ -170,10 +186,10 @@
/** Called to update hotseatItems, in order to de-dupe them from Recent/Running tasks later. */
fun updateHotseatItemInfos(hotseatItems: Array<ItemInfo?>): Array<ItemInfo?> {
// Ignore predicted apps - we show running or recent apps instead.
- val areDesktopTasksVisible = controllers.taskbarDesktopModeController.areDesktopTasksVisible
+ val showDesktopTasks =
+ controllers.taskbarDesktopModeController.shouldShowDesktopTasksInTaskbar()
val removePredictions =
- (areDesktopTasksVisible && canShowRunningApps) ||
- (!areDesktopTasksVisible && canShowRecentApps)
+ (showDesktopTasks && canShowRunningApps) || (!showDesktopTasks && canShowRecentApps)
if (!removePredictions) {
shownHotseatItems = hotseatItems.filterNotNull()
onRecentsOrHotseatChanged()
@@ -185,7 +201,7 @@
.filter { itemInfo -> !itemInfo.isPredictedItem }
.toMutableList()
- if (areDesktopTasksVisible && canShowRunningApps) {
+ if (showDesktopTasks && canShowRunningApps) {
shownHotseatItems =
updateHotseatItemsFromRunningTasks(
getOrderedAndWrappedDesktopTasks(),
@@ -203,25 +219,30 @@
// Kind of hacky, we wrap each single task in the Desktop as a GroupTask.
val orderFromId = orderedRunningTaskIds.withIndex().associate { (index, id) -> id to index }
val sortedTasks = tasks.sortedWith(compareBy(nullsLast()) { orderFromId[it.key.id] })
- return sortedTasks.map { GroupTask(it) }
+ return sortedTasks.map { SingleTask(it) }
}
private fun reloadRecentTasksIfNeeded() {
if (!recentsModel.isTaskListValid(taskListChangeId)) {
taskListChangeId =
- recentsModel.getTasks { tasks ->
- allRecentTasks = tasks
- val oldRunningTaskdIds = runningTaskIds
- val oldMinimizedTaskIds = minimizedTaskIds
- desktopTask = allRecentTasks.filterIsInstance<DesktopTask>().firstOrNull()
- val runningTasksChanged = oldRunningTaskdIds != runningTaskIds
- val minimizedTasksChanged = oldMinimizedTaskIds != minimizedTaskIds
- if (
- onRecentsOrHotseatChanged() || runningTasksChanged || minimizedTasksChanged
- ) {
- controllers.taskbarViewController.commitRunningAppsToUI()
- }
- }
+ recentsModel.getTasks(
+ { tasks ->
+ allRecentTasks = tasks
+ val oldRunningTaskdIds = runningTaskIds
+ val oldMinimizedTaskIds = minimizedTaskIds
+ desktopTask = allRecentTasks.filterIsInstance<DesktopTask>().firstOrNull()
+ val runningTasksChanged = oldRunningTaskdIds != runningTaskIds
+ val minimizedTasksChanged = oldMinimizedTaskIds != minimizedTaskIds
+ if (
+ onRecentsOrHotseatChanged() ||
+ runningTasksChanged ||
+ minimizedTasksChanged
+ ) {
+ controllers.taskbarViewController.commitRunningAppsToUI()
+ }
+ },
+ RecentsFilterState.EMPTY_FILTER,
+ )
}
}
@@ -234,7 +255,7 @@
val oldShownTasks = shownTasks
orderedRunningTaskIds = updateOrderedRunningTaskIds()
shownTasks =
- if (controllers.taskbarDesktopModeController.areDesktopTasksVisible) {
+ if (controllers.taskbarDesktopModeController.shouldShowDesktopTasksInTaskbar()) {
computeShownRunningTasks()
} else {
computeShownRecentTasks()
@@ -265,8 +286,8 @@
}
private fun updateOrderedRunningTaskIds(): MutableList<Int> {
- val desktopTaskAsList = getOrderedAndWrappedDesktopTasks()
- val desktopTaskIds = desktopTaskAsList.map { it.task1.key.id }
+ val desktopTasksAsList = getOrderedAndWrappedDesktopTasks().flatMap { it.tasks }
+ val desktopTaskIds = desktopTasksAsList.map { it.key.id }
var newOrder =
orderedRunningTaskIds
.filter { it in desktopTaskIds } // Only keep the tasks that are still running
@@ -276,27 +297,60 @@
return newOrder
}
+ /**
+ * Computes the list of running tasks to be shown in the recent apps section of the taskbar in
+ * desktop mode, taking into account deduplication against hotseat items and existing tasks.
+ */
private fun computeShownRunningTasks(): List<GroupTask> {
if (!canShowRunningApps) {
return emptyList()
}
- val desktopTaskAsList = getOrderedAndWrappedDesktopTasks()
- val desktopTaskIds = desktopTaskAsList.map { it.task1.key.id }
- val shownTaskIds = shownTasks.map { it.task1.key.id }
- // TODO(b/315344726 Multi-instance support): only show one icon per package once we support
- // taskbar multi-instance menus
- val shownHotseatItemTaskIds =
- shownHotseatItems.mapNotNull { it as? TaskItemInfo }.map { it.taskId }
- // Remove any newly-missing Tasks, and actual group-tasks
+
+ val desktopTasks = getOrderedAndWrappedDesktopTasks()
+
val newShownTasks =
- shownTasks
- .filter { !it.supportsMultipleTasks() }
- .filter { it.task1.key.id in desktopTaskIds }
- .toMutableList()
- // Add any new Tasks, maintaining the order from previous shownTasks.
- newShownTasks.addAll(desktopTaskAsList.filter { it.task1.key.id !in shownTaskIds })
- // Remove any tasks already covered by Hotseat icons
- return newShownTasks.filter { it.task1.key.id !in shownHotseatItemTaskIds }
+ if (Flags.enableMultiInstanceMenuTaskbar()) {
+ val deduplicatedDesktopTasks =
+ desktopTasks.distinctBy { Pair(it.task1.key.packageName, it.task1.key.userId) }
+
+ shownTasks
+ .filter {
+ !it.supportsMultipleTasks() &&
+ it.task1.key.id in deduplicatedDesktopTasks.map { it.task1.key.id }
+ }
+ .toMutableList()
+ .apply {
+ addAll(
+ deduplicatedDesktopTasks.filter { currentTask ->
+ val currentTaskKey = currentTask.task1.key
+ currentTaskKey.id !in shownTasks.map { it.task1.key.id } &&
+ shownHotseatItems.none { hotseatItem ->
+ hotseatItem.targetPackage == currentTaskKey.packageName &&
+ hotseatItem.user.identifier == currentTaskKey.userId
+ }
+ }
+ )
+ }
+ } else {
+ val desktopTaskIds = desktopTasks.map { it.task1.key.id }
+ val shownHotseatItemTaskIds =
+ shownHotseatItems.mapNotNull { it as? TaskItemInfo }.map { it.taskId }
+
+ shownTasks
+ .filter { !it.supportsMultipleTasks() && it.task1.key.id in desktopTaskIds }
+ .toMutableList()
+ .apply {
+ addAll(
+ desktopTasks.filter { desktopTask ->
+ desktopTask.task1.key.id !in
+ shownTasks.map { shownTask -> shownTask.task1.key.id }
+ }
+ )
+ removeAll { it.task1.key.id in shownHotseatItemTaskIds }
+ }
+ }
+
+ return newShownTasks
}
private fun computeShownRecentTasks(): List<GroupTask> {
@@ -305,7 +359,6 @@
}
// Remove the current task.
val allRecentTasks = allRecentTasks.subList(0, allRecentTasks.size - 1)
- // TODO(b/315344726 Multi-instance support): dedupe Tasks of the same package too
var shownTasks = dedupeHotseatTasks(allRecentTasks, shownHotseatItems)
if (shownTasks.size > MAX_RECENT_TASKS) {
// Remove any tasks older than MAX_RECENT_TASKS.
@@ -318,10 +371,22 @@
groupTasks: List<GroupTask>,
shownHotseatItems: List<ItemInfo>,
): List<GroupTask> {
- val hotseatPackages = shownHotseatItems.map { item -> item.targetPackage }
- return groupTasks.filter { groupTask ->
- groupTask.hasMultipleTasks() ||
- !hotseatPackages.contains(groupTask.task1.key.packageName)
+ return if (Flags.enableMultiInstanceMenuTaskbar()) {
+ groupTasks.filter { groupTask ->
+ val taskKey = groupTask.task1.key
+ // Keep tasks that are group tasks or unique package name/user combinations
+ groupTask.hasMultipleTasks() ||
+ shownHotseatItems.none {
+ it.targetPackage == taskKey.packageName &&
+ it.user.identifier == taskKey.userId
+ }
+ }
+ } else {
+ val hotseatPackages = shownHotseatItems.map { it.targetPackage }
+ groupTasks.filter { groupTask ->
+ groupTask.hasMultipleTasks() ||
+ !hotseatPackages.contains(groupTask.task1.key.packageName)
+ }
}
}
@@ -338,11 +403,13 @@
itemInfo
} else {
val foundTask =
- groupTasks.find { task ->
- task.task1.key.packageName == itemInfo.targetPackage &&
- task.task1.key.userId == itemInfo.user.identifier
- } ?: return@map itemInfo
- TaskItemInfo(foundTask.task1.key.id, itemInfo as WorkspaceItemInfo)
+ groupTasks
+ .flatMap { it.tasks }
+ .find { task ->
+ task.key.packageName == itemInfo.targetPackage &&
+ task.key.userId == itemInfo.user.identifier
+ } ?: return@map itemInfo
+ TaskItemInfo(foundTask.key.id, itemInfo as WorkspaceItemInfo)
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 502c001..68114f1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -35,7 +35,7 @@
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DIALOG_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_BUTTON_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
@@ -259,8 +259,10 @@
private @Nullable AnimatorSet mAnimator;
private boolean mIsSystemGestureInProgress;
+ /** Whether the IME is visible. */
private boolean mIsImeShowing;
- private boolean mIsImeSwitcherShowing;
+ /** Whether the IME Switcher button is visible. */
+ private boolean mIsImeSwitcherButtonShowing;
private final Alarm mTimeoutAlarm = new Alarm();
private boolean mEnableBlockingTimeoutDuringTests = false;
@@ -269,6 +271,8 @@
private final long mTaskbarBackgroundDuration;
private boolean mUserIsNotGoingHome = false;
+ private final boolean mInAppStateAffectsDesktopTasksVisibilityInTaskbar;
+
// Evaluate whether the handle should be stashed
private final LongPredicate mIsStashedPredicate = flags -> {
boolean inApp = hasAnyFlag(flags, FLAGS_IN_APP);
@@ -293,8 +297,17 @@
mSystemUiProxy = SystemUiProxy.INSTANCE.get(activity);
mAccessibilityManager = mActivity.getSystemService(AccessibilityManager.class);
- mTaskbarBackgroundDuration =
- activity.getResources().getInteger(R.integer.taskbar_background_duration);
+ // Taskbar, via `TaskbarDesktopModeController`, depends on `TaskbarStashController` state to
+ // determine whether desktop tasks should be shown because taskbar is pinned on the home
+ // screen for freeform windowing displays. In this case, list of items shown in the taskbar
+ // needs to be updated when in-app state changes.
+ // TODO(b/390665752): Feature to "lock" pinned taskbar to home screen will be superseded by
+ // pinning, in other launcher states, at which point this variable can be removed.
+ mInAppStateAffectsDesktopTasksVisibilityInTaskbar =
+ DisplayController.showLockedTaskbarOnHome(mActivity);
+
+ mTaskbarBackgroundDuration = activity.getResources().getInteger(
+ R.integer.taskbar_background_duration);
if (mActivity.isPhoneMode()) {
mUnstashedHeight = mActivity.getResources().getDimensionPixelSize(
R.dimen.taskbar_phone_size);
@@ -1136,7 +1149,8 @@
SystemUiFlagUtils.isLocked(systemUiStateFlags));
mIsImeShowing = hasAnyFlag(systemUiStateFlags, SYSUI_STATE_IME_SHOWING);
- mIsImeSwitcherShowing = hasAnyFlag(systemUiStateFlags, SYSUI_STATE_IME_SWITCHER_SHOWING);
+ mIsImeSwitcherButtonShowing =
+ hasAnyFlag(systemUiStateFlags, SYSUI_STATE_IME_SWITCHER_BUTTON_SHOWING);
if (updateStateForFlag(FLAG_STASHED_IME, shouldStashForIme())) {
animDuration = TASKBAR_STASH_DURATION_FOR_IME;
startDelay = getTaskbarStashStartDelayForIme();
@@ -1188,7 +1202,7 @@
return false;
}
- return mIsImeShowing || mIsImeSwitcherShowing;
+ return mIsImeShowing || mIsImeSwitcherButtonShowing;
}
/**
@@ -1237,6 +1251,10 @@
if (hasAnyFlag(changedFlags, FLAG_IN_OVERVIEW | FLAG_IN_APP)) {
mControllers.runAfterInit(() -> mControllers.taskbarInsetsController
.onTaskbarOrBubblebarWindowHeightOrInsetsChanged());
+ if (mInAppStateAffectsDesktopTasksVisibilityInTaskbar) {
+ mControllers.runAfterInit(
+ () -> mControllers.taskbarViewController.commitRunningAppsToUI());
+ }
}
mActivity.applyForciblyShownFlagWhileTransientTaskbarUnstashed(!isStashedInApp());
}
@@ -1360,7 +1378,7 @@
pw.println(prefix + "\tmState=" + getStateString(mState));
pw.println(prefix + "\tmIsSystemGestureInProgress=" + mIsSystemGestureInProgress);
pw.println(prefix + "\tmIsImeShowing=" + mIsImeShowing);
- pw.println(prefix + "\tmIsImeSwitcherShowing=" + mIsImeSwitcherShowing);
+ pw.println(prefix + "\tmIsImeSwitcherButtonShowing=" + mIsImeSwitcherButtonShowing);
}
private static String getStateString(long flags) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index 6b9f5a9..457ba3d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -33,7 +33,6 @@
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.os.Bundle;
import android.util.ArraySet;
import android.util.AttributeSet;
import android.view.DisplayCutout;
@@ -41,7 +40,6 @@
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
-import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
import androidx.annotation.LayoutRes;
@@ -311,16 +309,6 @@
mShouldTryStartAlign = mActivityContext.shouldStartAlignTaskbar();
}
- @Override
- public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
- if (action == AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS) {
- announceTaskbarShown();
- } else if (action == AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS) {
- announceTaskbarHidden();
- }
- return super.performAccessibilityActionInternal(action, arguments);
- }
-
private void announceTaskbarShown() {
BubbleBarLocation bubbleBarLocation = mControllerCallbacks.getBubbleBarLocationIfVisible();
if (bubbleBarLocation == null) {
@@ -334,21 +322,12 @@
}
}
- private void announceTaskbarHidden() {
- BubbleBarLocation bubbleBarLocation = mControllerCallbacks.getBubbleBarLocationIfVisible();
- if (bubbleBarLocation == null) {
- announceForAccessibility(mContext.getString(R.string.taskbar_a11y_hidden_title));
- } else {
- announceForAccessibility(
- mContext.getString(R.string.taskbar_a11y_hidden_with_bubbles_title));
- }
- }
-
protected void announceAccessibilityChanges() {
- this.performAccessibilityAction(
- isVisibleToUser() ? AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS
- : AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
-
+ // Only announce taskbar window shown. Window disappearing is generally not announce.
+ // This also aligns with talkback guidelines and unnecessary announcement to users.
+ if (isVisibleToUser()) {
+ announceTaskbarShown();
+ }
ActivityContext.lookupContext(getContext()).getDragLayer()
.sendAccessibilityEvent(TYPE_WINDOW_CONTENT_CHANGED);
}
@@ -402,6 +381,16 @@
view.setTag(null);
}
+ /** Loop through all {@link FolderIcon} as child views and clear listeners to avoid leak. */
+ public void removeFolderIconListeners() {
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ if (getChildAt(i) instanceof FolderIcon fi) {
+ fi.removeListeners();
+ }
+ }
+ }
+
/** Inflates/binds the hotseat items and recent tasks to the view. */
protected void updateItems(ItemInfo[] hotseatItemInfos, List<GroupTask> recentTasks) {
// Filter out unsupported items.
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 60de066..0f05887 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();
@@ -324,6 +365,7 @@
if (enableTaskbarPinning()) {
mTaskbarView.removeOnLayoutChangeListener(mTaskbarViewLayoutChangeListener);
}
+ mTaskbarView.removeFolderIconListeners();
LauncherAppState.getInstance(mActivity).getModel().removeCallbacks(mModelCallbacks);
mActivity.removeOnDeviceProfileChangeListener(mDeviceProfileChangeListener);
}
@@ -450,16 +492,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 +603,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 +873,13 @@
if (mTaskbarView.updateMaxNumIcons()) {
commitRunningAppsToUI();
}
+ adjustTaskbarXForBubbleBar();
+ }
+
+ private void adjustTaskbarXForBubbleBar() {
+ if (mBubbleControllers != null && mActivity.isTransientTaskbar()) {
+ translateTaskbarXForBubbleBar(/* animate= */ true);
+ }
}
/**
@@ -841,7 +910,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 +1121,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 3dcf2b4..aff879e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
@@ -20,7 +20,7 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_BUTTON_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
@@ -34,6 +34,7 @@
import android.os.SystemProperties;
import android.util.ArrayMap;
import android.util.Log;
+import android.widget.Toast;
import com.android.launcher3.taskbar.TaskbarSharedState;
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController;
@@ -94,7 +95,7 @@
| SYSUI_STATE_IME_SHOWING
| SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED
| SYSUI_STATE_QUICK_SETTINGS_EXPANDED
- | SYSUI_STATE_IME_SWITCHER_SHOWING;
+ | SYSUI_STATE_IME_SWITCHER_BUTTON_SHOWING;
private static final long MASK_HIDE_HANDLE_VIEW = SYSUI_STATE_BOUNCER_SHOWING
| SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
@@ -342,10 +343,10 @@
mBubbleBarViewController.showOverflow(update.showOverflow);
}
- BubbleBarBubble bubbleToSelect = null;
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())) {
@@ -363,7 +364,7 @@
&& update.removedBubbles.isEmpty()
&& !mBubbles.isEmpty()) {
// A bubble was added from the overflow (& now it's empty / not showing)
- 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);
@@ -371,7 +372,8 @@
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, bubbleToSelect);
@@ -387,7 +389,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 {
@@ -588,6 +590,18 @@
});
}
+ @Override
+ public void onDragItemOverBubbleBarDragZone(BubbleBarLocation location) {
+ //TODO(b/388894910): add meaningful implementation
+ MAIN_EXECUTOR.execute(() ->
+ Toast.makeText(mContext, "onDragItemOver " + location, Toast.LENGTH_SHORT).show());
+ }
+
+ @Override
+ public void onItemDraggedOutsideBubbleBarDropZone() {
+
+ }
+
/** Notifies WMShell to show the expanded view. */
void showExpandedView() {
mSystemUiProxy.showExpandedView();
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
index aa6ad25..c001123 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
@@ -741,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() {
@@ -802,8 +799,8 @@
invalidate();
}
};
- mBubbleAnimator.animateNewAndRemoveOld(indexOfSelectedBubble, indexOfBubbleToRemove,
- listener);
+ mBubbleAnimator.animateNewAndRemoveOld(indexOfCurrentSelectedBubble,
+ indexOfNewlySelectedBubble, indexOfBubbleToRemove, addedIndex, listener);
}
@Override
@@ -1342,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 5685093..026f239 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;
}
/**
@@ -586,7 +588,7 @@
/** Returns maximum height of the bubble bar with the flyout view. */
public int getBubbleBarWithFlyoutMaximumHeight() {
- if (!isBubbleBarVisible() && !isAnimatingNewBubble()) return 0;
+ if (!hasBubbles() && !isAnimatingNewBubble()) return 0;
int bubbleBarTopOnHome = (int) (mBubbleStashController.getBubbleBarVerticalCenterForHome()
+ mBarView.getBubbleBarCollapsedHeight() / 2 + mBarView.getArrowHeight());
if (isAnimatingNewBubble()) {
@@ -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);
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 944e806..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.
@@ -70,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()
}
@@ -137,6 +143,7 @@
getBubbleTranslationXWhileAddingBubbleAtLimit(
bubbleIndex = bubbleIndex,
removedBubbleIndex = state.removedBubbleIndex,
+ addedBubbleIndex = state.addedBubbleIndex,
addedBubbleScale = animator.animatedFraction,
removedBubbleScale = 1 - animator.animatedFraction,
)
@@ -187,34 +194,25 @@
State.Idle -> 0f
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
+ 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 = 1f,
+ bubbleScale = scale,
) + iconSize * selectedBubbleScale / 2f
tx += (finalTx - tx) * animator.animatedFraction
}
@@ -266,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.
@@ -290,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
@@ -299,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
@@ -310,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
@@ -325,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
}
}
}
@@ -413,10 +461,17 @@
val removingLastRemainingBubble: Boolean,
) : State
- // TODO add index where bubble is being added, and index for newly selected bubble
/** 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/PredictedAppIcon.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index caac35e..a85e5e0 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -36,6 +36,7 @@
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.FloatProperty;
+import android.util.Log;
import android.util.Property;
import android.view.LayoutInflater;
import android.view.ViewGroup;
@@ -49,8 +50,8 @@
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.celllayout.DelegatedCellDrawing;
+import com.android.launcher3.graphics.IconShape;
import com.android.launcher3.icons.FastBitmapDrawable;
-import com.android.launcher3.icons.GraphicsUtils;
import com.android.launcher3.icons.IconNormalizer;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.data.ItemInfoWithIcon;
@@ -110,6 +111,8 @@
private boolean mForceHideRing = false;
private Animator mRingScaleAnim;
+ private int mWidth;
+
private static final FloatProperty<PredictedAppIcon> SLOT_MACHINE_TRANSLATION_Y =
new FloatProperty<PredictedAppIcon>("slotMachineTranslationY") {
@Override
@@ -139,7 +142,7 @@
int shadowSize = context.getResources().getDimensionPixelSize(
R.dimen.blur_size_thin_outline);
mShadowFilter = new BlurMaskFilter(shadowSize, BlurMaskFilter.Blur.OUTER);
- mShapePath = GraphicsUtils.getShapePath(context, mNormalizedIconSize);
+ mShapePath = IconShape.INSTANCE.get(context).getShapeOverridePath(mNormalizedIconSize);
}
@Override
@@ -300,7 +303,13 @@
}
private int getOutlineOffsetX() {
- return (getMeasuredWidth() - mNormalizedIconSize) / 2;
+ int measuredWidth = getMeasuredWidth();
+ if (mDisplay != DISPLAY_TASKBAR) {
+ Log.d("b/387844520", "getOutlineOffsetX: measured width = " + measuredWidth
+ + ", mNormalizedIconSize = " + mNormalizedIconSize
+ + ", last updated width = " + mWidth);
+ }
+ return (mWidth - mNormalizedIconSize) / 2;
}
private int getOutlineOffsetY() {
@@ -313,7 +322,11 @@
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
+ mWidth = w;
mSlotIconBound.offsetTo((w - getIconSize()) / 2, (h - getIconSize()) / 2);
+ if (mDisplay != DISPLAY_TASKBAR) {
+ Log.d("b/387844520", "calling updateRingPath from onSizeChanged");
+ }
updateRingPath();
}
@@ -325,6 +338,7 @@
private void updateRingPath() {
mRingPath.reset();
+ mTmpMatrix.reset();
mTmpMatrix.setTranslate(getOutlineOffsetX(), getOutlineOffsetY());
mRingPath.addPath(mShapePath, mTmpMatrix);
@@ -339,6 +353,7 @@
mTmpMatrix.preTranslate(-mNormalizedIconSize, -mNormalizedIconSize);
mRingPath.addPath(mShapePath, mTmpMatrix);
}
+ invalidate();
}
@Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 70c53fa..f672840 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -23,6 +23,7 @@
import static com.android.app.animation.Interpolators.EMPHASIZED;
import static com.android.internal.jank.Cuj.CUJ_LAUNCHER_LAUNCH_APP_PAIR_FROM_WORKSPACE;
+import static com.android.launcher3.Flags.enableExpressiveDismissTaskMotion;
import static com.android.launcher3.Flags.enableUnfoldStateAnimation;
import static com.android.launcher3.LauncherConstants.SavedInstanceKeys.PENDING_SPLIT_SELECT_INFO;
import static com.android.launcher3.LauncherConstants.SavedInstanceKeys.RUNTIME_STATE;
@@ -39,7 +40,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;
@@ -65,7 +65,6 @@
import static com.android.quickstep.util.AnimUtils.completeRunnableListCallback;
import static com.android.quickstep.util.SplitAnimationTimings.TABLET_HOME_TO_SPLIT;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
-import static com.android.wm.shell.Flags.enableBubbleAnything;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50;
import android.animation.Animator;
@@ -86,6 +85,7 @@
import android.os.IRemoteCallback;
import android.os.SystemProperties;
import android.os.Trace;
+import android.os.UserHandle;
import android.util.AttributeSet;
import android.view.Display;
import android.view.HapticFeedbackConstants;
@@ -150,7 +150,9 @@
import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.QuickSwitchTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController;
+import com.android.launcher3.uioverrides.touchcontrollers.TaskViewRecentsTouchContext;
import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController;
+import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchControllerDeprecated;
import com.android.launcher3.uioverrides.touchcontrollers.TransposedQuickSwitchTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.TwoButtonNavbarTouchController;
import com.android.launcher3.util.ActivityOptionsWrapper;
@@ -170,7 +172,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;
@@ -202,6 +203,7 @@
import com.android.systemui.unfold.dagger.UnfoldMain;
import com.android.systemui.unfold.progress.RemoteUnfoldTransitionReceiver;
import com.android.systemui.unfold.updates.RotationChangeProvider;
+import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
@@ -266,6 +268,25 @@
private final OverviewChangeListener mOverviewChangeListener = this::onOverviewTargetChanged;
+ private final TaskViewRecentsTouchContext mTaskViewRecentsTouchContext =
+ new TaskViewRecentsTouchContext() {
+ @Override
+ public boolean isRecentsInteractive() {
+ return isInState(OVERVIEW) || isInState(OVERVIEW_MODAL_TASK);
+ }
+
+ @Override
+ public boolean isRecentsModal() {
+ return isInState(OVERVIEW_MODAL_TASK);
+ }
+
+ @Override
+ public void onUserControlledAnimationCreated(
+ AnimatorPlaybackController animController) {
+ getStateManager().setCurrentUserControlledAnimation(animController);
+ }
+ };
+
public static QuickstepLauncher getLauncher(Context context) {
return fromContext(context);
}
@@ -282,7 +303,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 +310,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());
@@ -470,7 +490,7 @@
if (Flags.enablePrivateSpace()) {
shortcuts.add(UNINSTALL_APP);
}
- if (enableBubbleAnything()) {
+ if (BubbleAnythingFlagHelper.enableCreateAnyBubble()) {
shortcuts.add(BUBBLE_SHORTCUT);
}
return shortcuts.stream();
@@ -564,9 +584,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());
}
@@ -663,7 +687,11 @@
list.add(new StatusBarTouchController(this));
}
- list.add(new LauncherTaskViewController(this));
+ if (enableExpressiveDismissTaskMotion()) {
+ list.add(new TaskViewTouchController<>(this, mTaskViewRecentsTouchContext));
+ } else {
+ list.add(new TaskViewTouchControllerDeprecated<>(this, mTaskViewRecentsTouchContext));
+ }
return list.toArray(new TouchController[list.size()]);
}
@@ -721,11 +749,7 @@
splitSelectSource.alreadyRunningTaskId = taskWasFound
? foundTask.key.id
: INVALID_TASK_ID;
- if (enableSplitContextually()) {
- startSplitToHome(splitSelectSource);
- } else {
- recentsView.initiateSplitSelect(splitSelectSource);
- }
+ startSplitToHome(splitSelectSource);
}
);
}
@@ -815,15 +839,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()) {
@@ -1422,7 +1444,7 @@
@Override
public boolean handleIncorrectSplitTargetSelection() {
- if (!enableSplitContextually() || !mSplitSelectStateController.isSplitSelectActive()) {
+ if (!mSplitSelectStateController.isSplitSelectActive()) {
return false;
}
mSplitSelectStateController.getSplitInstructionsView().goBoing();
@@ -1436,9 +1458,9 @@
}
@Override
- public void showAppBubble(Intent intent) {
+ public void showAppBubble(Intent intent, UserHandle user) {
if (intent == null || intent.getPackage() == null) return;
- SystemUiProxy.INSTANCE.get(this).showAppBubble(intent);
+ SystemUiProxy.INSTANCE.get(this).showAppBubble(intent, user);
}
/** Sets the location of the bubble bar */
@@ -1446,29 +1468,6 @@
mBubbleBarLocation = bubbleBarLocation;
}
- private static final class LauncherTaskViewController extends
- TaskViewTouchController<QuickstepLauncher> {
-
- LauncherTaskViewController(QuickstepLauncher activity) {
- super(activity);
- }
-
- @Override
- protected boolean isRecentsInteractive() {
- return mContainer.isInState(OVERVIEW) || mContainer.isInState(OVERVIEW_MODAL_TASK);
- }
-
- @Override
- protected boolean isRecentsModal() {
- return mContainer.isInState(OVERVIEW_MODAL_TASK);
- }
-
- @Override
- protected void onUserControlledAnimationCreated(AnimatorPlaybackController animController) {
- mContainer.getStateManager().setCurrentUserControlledAnimation(animController);
- }
- }
-
@Override
public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
super.dump(prefix, fd, writer, args);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.kt b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.kt
index f196548..c8f46a9 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.kt
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.kt
@@ -26,7 +26,6 @@
import com.android.launcher3.anim.AnimatorListeners.forSuccessCallback
import com.android.launcher3.anim.PendingAnimation
import com.android.launcher3.anim.PropertySetter
-import com.android.launcher3.config.FeatureFlags.enableSplitContextually
import com.android.launcher3.logging.StatsLogManager.LauncherEvent
import com.android.launcher3.statemanager.StateManager.StateHandler
import com.android.launcher3.states.StateAnimationConfig
@@ -53,7 +52,6 @@
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
-import com.android.wm.shell.Flags.enableSplitContextual
/**
* State handler for handling UI changes for [com.android.quickstep.views.LauncherRecentsView]. In
@@ -129,23 +127,6 @@
config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR),
)
- val exitingOverview = !enableSplitContextually() && !toState.isRecentsViewVisible
- if (recentsView.isSplitSelectionActive && exitingOverview) {
- builder.add(
- recentsView.splitSelectController.splitAnimationController
- .createPlaceholderDismissAnim(
- launcher,
- LauncherEvent.LAUNCHER_SPLIT_SELECTION_EXIT_HOME,
- builder.duration,
- )
- )
- builder.setViewAlpha(
- recentsView.splitInstructionsView,
- 0f,
- config.getInterpolator(ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE, LINEAR),
- )
- }
-
builder.setFloat(
recentsView,
CONTENT_ALPHA,
@@ -227,9 +208,7 @@
builder: PendingAnimation,
animate: Boolean,
) {
- val goingToOverviewFromWorkspaceContextual =
- enableSplitContextual() &&
- toState == LauncherState.OVERVIEW &&
+ val goingToOverviewFromWorkspaceContextual = toState == LauncherState.OVERVIEW &&
launcher.isSplitSelectionActive
if (
toState != LauncherState.OVERVIEW_SPLIT_SELECT &&
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/launcher3/uioverrides/touchcontrollers/TaskViewRecentsTouchContext.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewRecentsTouchContext.java
new file mode 100644
index 0000000..e8d31c1
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewRecentsTouchContext.java
@@ -0,0 +1,32 @@
+/*
+ * 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.touchcontrollers;
+
+import com.android.launcher3.anim.AnimatorPlaybackController;
+
+/** Interface providing context about the RecentsView state to a {@link TaskViewTouchController}. */
+public interface TaskViewRecentsTouchContext {
+ /** Returns whether Recents is interactive for touch. */
+ boolean isRecentsInteractive();
+
+ /** Returns if Recents is showing a single task in a modal way. */
+ boolean isRecentsModal();
+
+ /** Runs when a user controlled animation is created. */
+ default void onUserControlledAnimationCreated(AnimatorPlaybackController animController) {
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.kt b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.kt
new file mode 100644
index 0000000..c996f34
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.kt
@@ -0,0 +1,394 @@
+/*
+ * 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.touchcontrollers
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.content.Context
+import android.graphics.Rect
+import android.os.VibrationEffect
+import android.view.MotionEvent
+import android.view.animation.Interpolator
+import com.android.app.animation.Interpolators
+import com.android.launcher3.AbstractFloatingView
+import com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS
+import com.android.launcher3.LauncherAnimUtils.blockedFlingDurationFactor
+import com.android.launcher3.R
+import com.android.launcher3.Utilities
+import com.android.launcher3.anim.AnimatorPlaybackController
+import com.android.launcher3.anim.PendingAnimation
+import com.android.launcher3.touch.BaseSwipeDetector
+import com.android.launcher3.touch.SingleAxisSwipeDetector
+import com.android.launcher3.util.DisplayController
+import com.android.launcher3.util.FlingBlockCheck
+import com.android.launcher3.util.TouchController
+import com.android.launcher3.util.VibratorWrapper
+import com.android.quickstep.util.VibrationConstants
+import com.android.quickstep.views.RecentsView
+import com.android.quickstep.views.RecentsViewContainer
+import com.android.quickstep.views.TaskView
+import kotlin.math.abs
+
+/** Touch controller for handling task view card swipes */
+class TaskViewTouchController<CONTAINER>(
+ private val container: CONTAINER,
+ private val taskViewRecentsTouchContext: TaskViewRecentsTouchContext,
+) : AnimatorListenerAdapter(), TouchController, SingleAxisSwipeDetector.Listener where
+CONTAINER : Context,
+CONTAINER : RecentsViewContainer {
+ private val recentsView: RecentsView<*, *> = container.getOverviewPanel()
+ private val detector: SingleAxisSwipeDetector =
+ SingleAxisSwipeDetector(
+ container as Context,
+ this,
+ recentsView.pagedOrientationHandler.upDownSwipeDirection,
+ )
+ private val tempRect = Rect()
+ private val isRtl = Utilities.isRtl(container.resources)
+ private val flingBlockCheck = FlingBlockCheck()
+
+ private var currentAnimation: AnimatorPlaybackController? = null
+ private var currentAnimationIsGoingUp = false
+ private var allowGoingUp = false
+ private var allowGoingDown = false
+ private var noIntercept = false
+ private var displacementShift = 0f
+ private var progressMultiplier = 0f
+ private var endDisplacement = 0f
+ private var draggingEnabled = true
+ private var overrideVelocity: Float? = null
+ private var taskBeingDragged: TaskView? = null
+ private var isDismissHapticRunning = false
+
+ private fun canInterceptTouch(ev: MotionEvent): Boolean {
+ val currentAnimation = currentAnimation
+ return when {
+ (ev.edgeFlags and Utilities.EDGE_NAV_BAR) != 0 -> {
+ // Don't intercept swipes on the nav bar, as user might be trying to go home
+ // during a task dismiss animation.
+ currentAnimation?.animationPlayer?.end()
+ false
+ }
+ currentAnimation != null -> {
+ currentAnimation.forceFinishIfCloseToEnd()
+ true
+ }
+ AbstractFloatingView.getTopOpenViewWithType(
+ container,
+ AbstractFloatingView.TYPE_TOUCH_CONTROLLER_NO_INTERCEPT,
+ ) != null -> false
+ else -> taskViewRecentsTouchContext.isRecentsInteractive
+ }
+ }
+
+ override fun onAnimationCancel(animation: Animator) {
+ if (animation === currentAnimation?.target) {
+ clearState()
+ }
+ }
+
+ override fun onControllerInterceptTouchEvent(ev: MotionEvent): Boolean {
+ if (
+ (ev.action == MotionEvent.ACTION_UP || ev.action == MotionEvent.ACTION_CANCEL) &&
+ currentAnimation == null
+ ) {
+ clearState()
+ }
+ if (ev.action == MotionEvent.ACTION_DOWN) {
+ // Disable swiping up and down if the task overlay is modal.
+ if (taskViewRecentsTouchContext.isRecentsModal) {
+ noIntercept = true
+ return false
+ }
+ noIntercept = !canInterceptTouch(ev)
+ if (noIntercept) {
+ return false
+ }
+ // Now figure out which direction scroll events the controller will start
+ // calling the callbacks.
+ var directionsToDetectScroll = 0
+ var ignoreSlopWhenSettling = false
+ if (currentAnimation != null) {
+ directionsToDetectScroll = SingleAxisSwipeDetector.DIRECTION_BOTH
+ ignoreSlopWhenSettling = true
+ } else {
+ taskBeingDragged = null
+ recentsView.taskViews.forEach { taskView ->
+ if (
+ recentsView.isTaskViewVisible(taskView) &&
+ container.dragLayer.isEventOverView(taskView, ev)
+ ) {
+ taskBeingDragged = taskView
+ val upDirection = recentsView.pagedOrientationHandler.getUpDirection(isRtl)
+
+ // The task can be dragged up to dismiss it
+ allowGoingUp = true
+
+ // The task can be dragged down to open it if:
+ // - It's the current page
+ // - We support gestures to enter overview
+ // - It's the focused task if in grid view
+ // - The task is snapped
+ allowGoingDown =
+ taskView === recentsView.currentPageTaskView &&
+ DisplayController.getNavigationMode(container).hasGestures &&
+ (!recentsView.showAsGrid() || taskView.isLargeTile) &&
+ recentsView.isTaskInExpectedScrollPosition(taskView)
+
+ directionsToDetectScroll =
+ if (allowGoingDown) SingleAxisSwipeDetector.DIRECTION_BOTH
+ else upDirection
+ return@forEach
+ }
+ }
+ if (taskBeingDragged == null) {
+ noIntercept = true
+ return false
+ }
+ }
+ detector.setDetectableScrollConditions(directionsToDetectScroll, ignoreSlopWhenSettling)
+ }
+ if (noIntercept) {
+ return false
+ }
+ onControllerTouchEvent(ev)
+ return detector.isDraggingOrSettling
+ }
+
+ override fun onControllerTouchEvent(ev: MotionEvent): Boolean = detector.onTouchEvent(ev)
+
+ private fun reInitAnimationController(goingUp: Boolean) {
+ if (currentAnimation != null && currentAnimationIsGoingUp == goingUp) {
+ // No need to init
+ return
+ }
+ if ((goingUp && !allowGoingUp) || (!goingUp && !allowGoingDown)) {
+ // Trying to re-init in an unsupported direction.
+ return
+ }
+ val taskBeingDragged = taskBeingDragged ?: return
+ currentAnimation?.setPlayFraction(0f)
+ currentAnimation?.target?.removeListener(this)
+ currentAnimation?.dispatchOnCancel()
+
+ val orientationHandler = recentsView.pagedOrientationHandler
+ currentAnimationIsGoingUp = goingUp
+ val dl = container.dragLayer
+ val secondaryLayerDimension = orientationHandler.getSecondaryDimension(dl)
+ val maxDuration = 2L * secondaryLayerDimension
+ val verticalFactor = orientationHandler.getTaskDragDisplacementFactor(isRtl)
+ val secondaryTaskDimension = orientationHandler.getSecondaryDimension(taskBeingDragged)
+ // The interpolator controlling the most prominent visual movement. We use this to determine
+ // whether we passed SUCCESS_TRANSITION_PROGRESS.
+ val currentInterpolator: Interpolator
+ val pa: PendingAnimation
+ if (goingUp) {
+ currentInterpolator = Interpolators.LINEAR
+ pa = PendingAnimation(maxDuration)
+ recentsView.createTaskDismissAnimation(
+ pa,
+ taskBeingDragged,
+ true, /* animateTaskView */
+ true, /* removeTask */
+ maxDuration,
+ false, /* dismissingForSplitSelection*/
+ )
+
+ endDisplacement = -secondaryTaskDimension.toFloat()
+ } else {
+ currentInterpolator = Interpolators.ZOOM_IN
+ pa =
+ recentsView.createTaskLaunchAnimation(
+ taskBeingDragged,
+ maxDuration,
+ currentInterpolator,
+ )
+
+ // Since the thumbnail is what is filling the screen, based the end displacement on it.
+ taskBeingDragged.getThumbnailBounds(tempRect, /* relativeToDragLayer= */ true)
+ endDisplacement = (secondaryLayerDimension - tempRect.bottom).toFloat()
+ }
+ endDisplacement *= verticalFactor.toFloat()
+ currentAnimation =
+ pa.createPlaybackController().apply {
+ // Setting this interpolator doesn't affect the visual motion, but is used to
+ // determine whether we successfully reached the target state in onDragEnd().
+ target.interpolator = currentInterpolator
+ taskViewRecentsTouchContext.onUserControlledAnimationCreated(this)
+ target.addListener(this@TaskViewTouchController)
+ dispatchOnStart()
+ }
+ progressMultiplier = 1 / endDisplacement
+ }
+
+ override fun onDragStart(start: Boolean, startDisplacement: Float) {
+ if (!draggingEnabled) return
+ val currentAnimation = currentAnimation
+
+ val orientationHandler = recentsView.pagedOrientationHandler
+ if (currentAnimation == null) {
+ reInitAnimationController(orientationHandler.isGoingUp(startDisplacement, isRtl))
+ displacementShift = 0f
+ } else {
+ displacementShift = currentAnimation.progressFraction / progressMultiplier
+ currentAnimation.pause()
+ }
+ flingBlockCheck.unblockFling()
+ overrideVelocity = null
+ }
+
+ override fun onDrag(displacement: Float): Boolean {
+ if (!draggingEnabled) return true
+ val taskBeingDragged = taskBeingDragged ?: return true
+ val currentAnimation = currentAnimation ?: return true
+
+ val orientationHandler = recentsView.pagedOrientationHandler
+ val totalDisplacement = displacement + displacementShift
+ val isGoingUp =
+ if (totalDisplacement == 0f) currentAnimationIsGoingUp
+ else orientationHandler.isGoingUp(totalDisplacement, isRtl)
+ if (isGoingUp != currentAnimationIsGoingUp) {
+ reInitAnimationController(isGoingUp)
+ flingBlockCheck.blockFling()
+ } else {
+ flingBlockCheck.onEvent()
+ }
+
+ if (isGoingUp) {
+ if (currentAnimation.progressFraction < ANIMATION_PROGRESS_FRACTION_MIDPOINT) {
+ // Halve the value when dismissing, as we are animating the drag across the full
+ // length for only the first half of the progress
+ currentAnimation.setPlayFraction(
+ Utilities.boundToRange(totalDisplacement * progressMultiplier / 2, 0f, 1f)
+ )
+ } else {
+ // Set mOverrideVelocity to control task dismiss velocity in onDragEnd
+ var velocityDimenId = R.dimen.default_task_dismiss_drag_velocity
+ if (recentsView.showAsGrid()) {
+ velocityDimenId =
+ if (taskBeingDragged.isLargeTile) {
+ R.dimen.default_task_dismiss_drag_velocity_grid_focus_task
+ } else {
+ R.dimen.default_task_dismiss_drag_velocity_grid
+ }
+ }
+ overrideVelocity = -taskBeingDragged.resources.getDimension(velocityDimenId)
+
+ // Once halfway through task dismissal interpolation, switch from reversible
+ // dragging-task animation to playing the remaining task translation animations,
+ // while this is in progress disable dragging.
+ draggingEnabled = false
+ }
+ } else {
+ currentAnimation.setPlayFraction(
+ Utilities.boundToRange(totalDisplacement * progressMultiplier, 0f, 1f)
+ )
+ }
+
+ return true
+ }
+
+ override fun onDragEnd(velocity: Float) {
+ val taskBeingDragged = taskBeingDragged ?: return
+ val currentAnimation = currentAnimation ?: return
+
+ // Limit velocity, as very large scalar values make animations play too quickly
+ val maxTaskDismissDragVelocity =
+ taskBeingDragged.resources.getDimension(R.dimen.max_task_dismiss_drag_velocity)
+ val endVelocity =
+ Utilities.boundToRange(
+ overrideVelocity ?: velocity,
+ -maxTaskDismissDragVelocity,
+ maxTaskDismissDragVelocity,
+ )
+ overrideVelocity = null
+
+ var fling = draggingEnabled && detector.isFling(endVelocity)
+ val goingToEnd: Boolean
+ val blockedFling = fling && flingBlockCheck.isBlocked
+ if (blockedFling) {
+ fling = false
+ }
+ val orientationHandler = recentsView.pagedOrientationHandler
+ val goingUp = orientationHandler.isGoingUp(endVelocity, isRtl)
+ val progress = currentAnimation.progressFraction
+ val interpolatedProgress = currentAnimation.interpolatedProgress
+ goingToEnd =
+ if (fling) {
+ goingUp == currentAnimationIsGoingUp
+ } else {
+ interpolatedProgress > SUCCESS_TRANSITION_PROGRESS
+ }
+ var animationDuration =
+ BaseSwipeDetector.calculateDuration(
+ endVelocity,
+ if (goingToEnd) (1 - progress) else progress,
+ )
+ if (blockedFling && !goingToEnd) {
+ animationDuration *= blockedFlingDurationFactor(endVelocity).toLong()
+ }
+ // Due to very high or low velocity dismissals, animation durations can be inconsistently
+ // long or short. Bound the duration for animation of task translations for a more
+ // standardized feel.
+ animationDuration =
+ Utilities.boundToRange(
+ animationDuration,
+ MIN_TASK_DISMISS_ANIMATION_DURATION,
+ MAX_TASK_DISMISS_ANIMATION_DURATION,
+ )
+
+ currentAnimation.setEndAction { this.clearState() }
+ currentAnimation.startWithVelocity(
+ container,
+ goingToEnd,
+ abs(endVelocity.toDouble()).toFloat(),
+ endDisplacement,
+ animationDuration,
+ )
+ if (goingUp && goingToEnd && !isDismissHapticRunning) {
+ VibratorWrapper.INSTANCE.get(container)
+ .vibrate(
+ TASK_DISMISS_VIBRATION_PRIMITIVE,
+ TASK_DISMISS_VIBRATION_PRIMITIVE_SCALE,
+ TASK_DISMISS_VIBRATION_FALLBACK,
+ )
+ isDismissHapticRunning = true
+ }
+
+ draggingEnabled = true
+ }
+
+ private fun clearState() {
+ detector.finishedScrolling()
+ detector.setDetectableScrollConditions(0, false)
+ draggingEnabled = true
+ taskBeingDragged = null
+ currentAnimation = null
+ isDismissHapticRunning = false
+ }
+
+ companion object {
+ private const val ANIMATION_PROGRESS_FRACTION_MIDPOINT = 0.5f
+ private const val MIN_TASK_DISMISS_ANIMATION_DURATION: Long = 300
+ private const val MAX_TASK_DISMISS_ANIMATION_DURATION: Long = 600
+
+ private const val TASK_DISMISS_VIBRATION_PRIMITIVE: Int =
+ VibrationEffect.Composition.PRIMITIVE_TICK
+ private const val TASK_DISMISS_VIBRATION_PRIMITIVE_SCALE: Float = 1f
+ private val TASK_DISMISS_VIBRATION_FALLBACK: VibrationEffect =
+ VibrationConstants.EFFECT_TEXTURE_TICK
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchControllerDeprecated.java
similarity index 94%
rename from quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
rename to quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchControllerDeprecated.java
index d622987..b1a36c7 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchControllerDeprecated.java
@@ -49,10 +49,13 @@
/**
* Touch controller for handling task view card swipes
+ *
+ * @deprecated This class will be replaced by the new {@link TaskViewTouchController}.
*/
-public abstract class TaskViewTouchController<CONTAINER extends Context & RecentsViewContainer>
- extends AnimatorListenerAdapter implements TouchController,
- SingleAxisSwipeDetector.Listener {
+@Deprecated
+public class TaskViewTouchControllerDeprecated<
+ CONTAINER extends Context & RecentsViewContainer> extends AnimatorListenerAdapter
+ implements TouchController, SingleAxisSwipeDetector.Listener {
private static final float ANIMATION_PROGRESS_FRACTION_MIDPOINT = 0.5f;
private static final long MIN_TASK_DISMISS_ANIMATION_DURATION = 300;
@@ -65,6 +68,7 @@
VibrationConstants.EFFECT_TEXTURE_TICK;
protected final CONTAINER mContainer;
+ private final TaskViewRecentsTouchContext mTaskViewRecentsTouchContext;
private final SingleAxisSwipeDetector mDetector;
private final RecentsView<?, ?> mRecentsView;
private final Rect mTempRect = new Rect();
@@ -88,8 +92,10 @@
private boolean mIsDismissHapticRunning = false;
- public TaskViewTouchController(CONTAINER container) {
+ public TaskViewTouchControllerDeprecated(CONTAINER container,
+ TaskViewRecentsTouchContext taskViewRecentsTouchContext) {
mContainer = container;
+ mTaskViewRecentsTouchContext = taskViewRecentsTouchContext;
mRecentsView = container.getOverviewPanel();
mIsRtl = Utilities.isRtl(container.getResources());
SingleAxisSwipeDetector.Direction dir =
@@ -117,15 +123,7 @@
mContainer, TYPE_TOUCH_CONTROLLER_NO_INTERCEPT) != null) {
return false;
}
- return isRecentsInteractive();
- }
-
- protected abstract boolean isRecentsInteractive();
-
- /** Is recents view showing a single task in a modal way. */
- protected abstract boolean isRecentsModal();
-
- protected void onUserControlledAnimationCreated(AnimatorPlaybackController animController) {
+ return mTaskViewRecentsTouchContext.isRecentsInteractive();
}
@Override
@@ -161,7 +159,7 @@
if (mRecentsView.isTaskViewVisible(taskView) && mContainer.getDragLayer()
.isEventOverView(taskView, ev)) {
// Disable swiping up and down if the task overlay is modal.
- if (isRecentsModal()) {
+ if (mTaskViewRecentsTouchContext.isRecentsModal()) {
mTaskBeingDragged = null;
break;
}
@@ -259,7 +257,7 @@
// Setting this interpolator doesn't affect the visual motion, but is used to determine
// whether we successfully reached the target state in onDragEnd().
mCurrentAnimation.getTarget().setInterpolator(currentInterpolator);
- onUserControlledAnimationCreated(mCurrentAnimation);
+ mTaskViewRecentsTouchContext.onUserControlledAnimationCreated(mCurrentAnimation);
mCurrentAnimation.getTarget().addListener(this);
mCurrentAnimation.dispatchOnStart();
mProgressMultiplier = 1 / mEndDisplacement;
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 03394ef..8b76ce9 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
@@ -1823,9 +1826,7 @@
final Rect hotseatKeepClearArea = getKeepClearAreaForHotseat();
final Rect destinationBounds = SystemUiProxy.INSTANCE.get(mContext)
- .startSwipePipToHome(taskInfo.topActivity,
- taskInfo.topActivityInfo,
- runningTaskTarget.taskInfo.pictureInPictureParams,
+ .startSwipePipToHome(taskInfo,
homeRotation,
hotseatKeepClearArea);
if (destinationBounds == null) {
@@ -1980,7 +1981,7 @@
}
// Make sure recents is in its final state
maybeUpdateRecentsAttachedState(false);
- mContainerInterface.onSwipeUpToHomeComplete(mDeviceState);
+ mContainerInterface.onSwipeUpToHomeComplete();
}
});
if (mRecentsAnimationTargets != null) {
@@ -2684,9 +2685,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/BaseContainerInterface.java b/quickstep/src/com/android/quickstep/BaseContainerInterface.java
index e2ebaa5..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.
@@ -252,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);
@@ -277,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();
@@ -382,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);
@@ -402,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..914855b 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,8 @@
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.DesktopModeCompatPolicy
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource
@@ -31,7 +34,7 @@
class DesktopSystemShortcut(
container: RecentsViewContainer,
private val taskContainer: TaskContainer,
- abstractFloatingViewHelper: AbstractFloatingViewHelper
+ abstractFloatingViewHelper: AbstractFloatingViewHelper,
) :
SystemShortcut<RecentsViewContainer>(
R.drawable.ic_desktop,
@@ -39,15 +42,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,18 +69,33 @@
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
- else
- listOf(
- DesktopSystemShortcut(
- container,
- taskContainer,
- abstractFloatingViewHelper
+ val context = container.asContext()
+ val taskKey = taskContainer.task.key
+ val desktopModeCompatPolicy = DesktopModeCompatPolicy(context)
+ return when {
+ !DesktopModeStatus.canEnterDesktopMode(context) -> null
+
+ desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
+ taskKey.baseActivity?.packageName,
+ taskKey.numActivities,
+ taskKey.isTopActivityNoDisplay,
+ taskKey.isActivityStackTransparent,
+ ) -> null
+
+ !taskContainer.task.isDockable -> null
+
+ else -> {
+ listOf(
+ DesktopSystemShortcut(
+ container,
+ taskContainer,
+ abstractFloatingViewHelper,
+ )
)
- )
+ }
+ }
}
override fun showForGroupedTask() = true
diff --git a/quickstep/src/com/android/quickstep/ExternalDisplaySystemShortcut.kt b/quickstep/src/com/android/quickstep/ExternalDisplaySystemShortcut.kt
index 46c4f36..f97cf9c 100644
--- a/quickstep/src/com/android/quickstep/ExternalDisplaySystemShortcut.kt
+++ b/quickstep/src/com/android/quickstep/ExternalDisplaySystemShortcut.kt
@@ -25,6 +25,7 @@
import com.android.quickstep.views.RecentsViewContainer
import com.android.quickstep.views.TaskContainer
import com.android.window.flags.Flags
+import com.android.wm.shell.shared.desktopmode.DesktopModeCompatPolicy
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
/** A menu item that allows the user to move the current app into external display. */
@@ -66,18 +67,31 @@
container: RecentsViewContainer,
taskContainer: TaskContainer,
): List<ExternalDisplaySystemShortcut>? {
- return if (
- DesktopModeStatus.canEnterDesktopMode(container.asContext()) &&
- Flags.moveToExternalDisplayShortcut()
- )
- listOf(
- ExternalDisplaySystemShortcut(
- container,
- abstractFloatingViewHelper,
- taskContainer,
+ val context = container.asContext()
+ val taskKey = taskContainer.task.key
+ val desktopModeCompatPolicy = DesktopModeCompatPolicy(context)
+ return when {
+ !DesktopModeStatus.canEnterDesktopMode(context) -> null
+
+ !Flags.moveToExternalDisplayShortcut() -> null
+
+ desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
+ taskKey.baseActivity?.packageName,
+ taskKey.numActivities,
+ taskKey.isTopActivityNoDisplay,
+ taskKey.isActivityStackTransparent,
+ ) -> null
+
+ else -> {
+ listOf(
+ ExternalDisplaySystemShortcut(
+ container,
+ abstractFloatingViewHelper,
+ taskContainer,
+ )
)
- )
- else null
+ }
+ }
}
override fun showForGroupedTask() = true
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..e1d4536 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -28,11 +28,11 @@
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET_HOME;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET_NEW_TASK;
-import android.app.TaskInfo;
import android.content.Intent;
import android.os.SystemClock;
import android.view.MotionEvent;
import android.view.RemoteAnimationTarget;
+import android.window.TransitionInfo;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -46,7 +46,6 @@
import com.android.quickstep.util.ActiveGestureProtoLogProxy;
import com.android.quickstep.views.RecentsViewContainer;
import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.wm.shell.shared.GroupedTaskInfo;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -333,13 +332,7 @@
return new int[]{INVALID_TASK_ID, INVALID_TASK_ID};
} else {
if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
- if (mRunningTask.getVisibleTasks().isEmpty()) {
- return new int[0];
- }
- GroupedTaskInfo topRunningTask = mRunningTask.getVisibleTasks().getFirst();
- List<TaskInfo> groupedTasks = topRunningTask.getTaskInfoList();
- return groupedTasks.stream().mapToInt(
- groupedTask -> groupedTask.taskId).toArray();
+ return mRunningTask.topGroupedTaskIds();
} else {
int cachedTasksSize = mRunningTask.mAllCachedTasks.size();
int count = Math.min(cachedTasksSize, getMultipleTasks ? 2 : 1);
@@ -491,7 +484,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/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/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index 7af0618..c60d3e8 100644
--- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -67,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);
}
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
index 089706f..6ad9a2c 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
@@ -23,11 +23,14 @@
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
import com.android.internal.jank.Cuj
+import com.android.launcher3.Flags.enableFallbackOverviewInWindow
import com.android.launcher3.Flags.enableLargeDesktopWindowingTile
+import com.android.launcher3.Flags.enableLauncherOverviewInWindow
import com.android.launcher3.Flags.enableOverviewCommandHelperTimeout
import com.android.launcher3.PagedView
import com.android.launcher3.logger.LauncherAtom
@@ -343,9 +346,12 @@
return false
}
- val activity = containerInterface.getCreatedContainer()
- if (activity != null) {
- InteractionJankMonitorWrapper.begin(activity.rootView, Cuj.CUJ_LAUNCHER_QUICK_SWITCH)
+ val recentsInWindowFlagSet =
+ enableFallbackOverviewInWindow() || enableLauncherOverviewInWindow()
+ if (!recentsInWindowFlagSet) {
+ containerInterface.getCreatedContainer()?.rootView?.let { view ->
+ InteractionJankMonitorWrapper.begin(view, Cuj.CUJ_LAUNCHER_QUICK_SWITCH)
+ }
}
val gestureState =
@@ -369,8 +375,15 @@
override fun onRecentsAnimationStart(
controller: RecentsAnimationController,
targets: RecentsAnimationTargets,
+ transitionInfo: TransitionInfo,
) {
Log.d(TAG, "recents animation started: $command")
+ if (recentsInWindowFlagSet) {
+ containerInterface.getCreatedContainer()?.rootView?.let { view ->
+ InteractionJankMonitorWrapper.begin(view, Cuj.CUJ_LAUNCHER_QUICK_SWITCH)
+ }
+ }
+
updateRecentsViewFocus(command)
logShowOverviewFrom(command.type)
containerInterface.runOnInitBackgroundStateUI {
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 620e2b7..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();
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index 08d43c0..01ced75 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -21,6 +21,7 @@
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.util.SplitScreenUtils.convertShellSplitBoundsToLauncher;
import static com.android.wm.shell.shared.GroupedTaskInfo.TYPE_FREEFORM;
+import static com.android.wm.shell.shared.GroupedTaskInfo.TYPE_SPLIT;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.KeyguardManager;
@@ -38,7 +39,10 @@
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.quickstep.util.DesktopTask;
import com.android.quickstep.util.GroupTask;
+import com.android.quickstep.util.SingleTask;
+import com.android.quickstep.util.SplitTask;
import com.android.systemui.shared.recents.model.Task;
+import com.android.wm.shell.Flags;
import com.android.wm.shell.recents.IRecentTasksListener;
import com.android.wm.shell.shared.GroupedTaskInfo;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
@@ -48,6 +52,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -116,7 +121,8 @@
@Override
public void onTaskMovedToFront(GroupedTaskInfo taskToFront) {
mMainThreadExecutor.execute(() -> {
- topTaskTracker.handleTaskMovedToFront(taskToFront.getTaskInfo1());
+ topTaskTracker.handleTaskMovedToFront(
+ taskToFront.getBaseGroupedTask().getTaskInfo1());
});
}
@@ -340,50 +346,76 @@
int numVisibleTasks = 0;
for (GroupedTaskInfo rawTask : rawTasks) {
- if (rawTask.getType() == TYPE_FREEFORM) {
+ if (rawTask.isBaseType(TYPE_FREEFORM)) {
// TYPE_FREEFORM tasks is only created when desktop mode can be entered,
// leftover TYPE_FREEFORM tasks created when flag was on should be ignored.
if (DesktopModeStatus.canEnterDesktopMode(mContext)) {
- GroupTask desktopTask = createDesktopTask(rawTask);
+ GroupTask desktopTask = createDesktopTask(rawTask.getBaseGroupedTask());
if (desktopTask != null) {
allTasks.add(desktopTask);
}
}
continue;
}
- TaskInfo taskInfo1 = rawTask.getTaskInfo1();
- TaskInfo taskInfo2 = rawTask.getTaskInfo2();
- Task.TaskKey task1Key = new Task.TaskKey(taskInfo1);
- Task task1 = loadKeysOnly
- ? new Task(task1Key)
- : Task.from(task1Key, taskInfo1,
- tmpLockedUsers.get(task1Key.userId) /* isLocked */);
- Task task2 = null;
- if (taskInfo2 != null) {
- // Is split task
- Task.TaskKey task2Key = new Task.TaskKey(taskInfo2);
- task2 = loadKeysOnly
- ? new Task(task2Key)
- : Task.from(task2Key, taskInfo2,
- tmpLockedUsers.get(task2Key.userId) /* isLocked */);
+
+ if (Flags.enableShellTopTaskTracking()) {
+ final TaskInfo taskInfo1 = rawTask.getBaseGroupedTask().getTaskInfo1();
+ final Task.TaskKey task1Key = new Task.TaskKey(taskInfo1);
+ final Task task1 = Task.from(task1Key, taskInfo1,
+ tmpLockedUsers.get(task1Key.userId) /* isLocked */);
+
+ if (rawTask.isBaseType(TYPE_SPLIT)) {
+ final TaskInfo taskInfo2 = rawTask.getBaseGroupedTask().getTaskInfo2();
+ final Task.TaskKey task2Key = new Task.TaskKey(taskInfo2);
+ final Task task2 = Task.from(task2Key, taskInfo2,
+ tmpLockedUsers.get(task2Key.userId) /* isLocked */);
+ final SplitConfigurationOptions.SplitBounds launcherSplitBounds =
+ convertShellSplitBoundsToLauncher(
+ rawTask.getBaseGroupedTask().getSplitBounds());
+ allTasks.add(new SplitTask(task1, task2, launcherSplitBounds));
+ } else {
+ allTasks.add(new SingleTask(task1));
+ }
} else {
- // Is fullscreen task
- if (numVisibleTasks > 0) {
- boolean isExcluded = (taskInfo1.baseIntent.getFlags()
- & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0;
- if (taskInfo1.isTopActivityTransparent && isExcluded) {
- // If there are already visible tasks, then ignore the excluded tasks and
- // don't add them to the returned list
- continue;
+ TaskInfo taskInfo1 = rawTask.getTaskInfo1();
+ TaskInfo taskInfo2 = rawTask.getTaskInfo2();
+ Task.TaskKey task1Key = new Task.TaskKey(taskInfo1);
+ Task task1 = loadKeysOnly
+ ? new Task(task1Key)
+ : Task.from(task1Key, taskInfo1,
+ tmpLockedUsers.get(task1Key.userId) /* isLocked */);
+ Task task2 = null;
+ if (taskInfo2 != null) {
+ // Is split task
+ Task.TaskKey task2Key = new Task.TaskKey(taskInfo2);
+ task2 = loadKeysOnly
+ ? new Task(task2Key)
+ : Task.from(task2Key, taskInfo2,
+ tmpLockedUsers.get(task2Key.userId) /* isLocked */);
+ } else {
+ // Is fullscreen task
+ if (numVisibleTasks > 0) {
+ boolean isExcluded = (taskInfo1.baseIntent.getFlags()
+ & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0;
+ if (taskInfo1.isTopActivityTransparent && isExcluded) {
+ // If there are already visible tasks, then ignore the excluded tasks
+ // and don't add them to the returned list
+ continue;
+ }
}
}
+ if (taskInfo1.isVisible) {
+ numVisibleTasks++;
+ }
+ if (task2 != null) {
+ Objects.requireNonNull(rawTask.getSplitBounds());
+ final SplitConfigurationOptions.SplitBounds launcherSplitBounds =
+ convertShellSplitBoundsToLauncher(rawTask.getSplitBounds());
+ allTasks.add(new SplitTask(task1, task2, launcherSplitBounds));
+ } else {
+ allTasks.add(new SingleTask(task1));
+ }
}
- if (taskInfo1.isVisible) {
- numVisibleTasks++;
- }
- final SplitConfigurationOptions.SplitBounds launcherSplitBounds =
- convertShellSplitBoundsToLauncher(rawTask.getSplitBounds());
- allTasks.add(new GroupTask(task1, task2, launcherSplitBounds));
}
return allTasks;
@@ -392,10 +424,6 @@
private @Nullable DesktopTask createDesktopTask(GroupedTaskInfo recentTaskInfo) {
ArrayList<Task> tasks = new ArrayList<>(recentTaskInfo.getTaskInfoList().size());
int[] minimizedTaskIds = recentTaskInfo.getMinimizedTaskIds();
- if (minimizedTaskIds.length == recentTaskInfo.getTaskInfoList().size()) {
- // All Tasks are minimized -> don't create a DesktopTask
- return null;
- }
for (TaskInfo taskInfo : recentTaskInfo.getTaskInfoList()) {
Task.TaskKey key = new Task.TaskKey(taskInfo);
Task task = Task.from(key, taskInfo, false);
@@ -409,27 +437,17 @@
return new DesktopTask(tasks);
}
- private ArrayList<GroupTask> copyOf(ArrayList<GroupTask> tasks) {
- ArrayList<GroupTask> newTasks = new ArrayList<>();
- for (int i = 0; i < tasks.size(); i++) {
- newTasks.add(tasks.get(i).copy());
- }
- return newTasks;
- }
-
public void dump(String prefix, PrintWriter writer) {
writer.println(prefix + "RecentTasksList:");
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();
@@ -441,14 +459,7 @@
}
writer.println(prefix + " rawTasks=[");
for (GroupedTaskInfo task : rawTasks) {
- TaskInfo taskInfo1 = task.getTaskInfo1();
- TaskInfo taskInfo2 = task.getTaskInfo2();
- ComponentName cn1 = taskInfo1.topActivity;
- ComponentName cn2 = taskInfo2 != null ? taskInfo2.topActivity : null;
- writer.println(prefix + " t1: (id=" + taskInfo1.taskId
- + "; package=" + (cn1 != null ? cn1.getPackageName() + ")" : "no package)")
- + " t2: (id=" + (taskInfo2 != null ? taskInfo2.taskId : "-1")
- + "; package=" + (cn2 != null ? cn2.getPackageName() + ")" : "no package)"));
+ writer.println(prefix + task);
}
writer.println(prefix + " ]");
}
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index b34b502..5e8ea37 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -363,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);
@@ -460,6 +452,10 @@
@Override
protected void onDestroy() {
+ RecentsView recentsView = getOverviewPanel();
+ if (recentsView != null) {
+ recentsView.destroy();
+ }
super.onDestroy();
ACTIVITY_TRACKER.onContextDestroyed(this);
mActivityLaunchAnimationRunner = null;
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..0c89a80 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -64,6 +64,11 @@
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import com.android.launcher3.dagger.ApplicationContext;
+import com.android.launcher3.dagger.LauncherAppComponent;
+import com.android.launcher3.dagger.LauncherAppSingleton;
+import com.android.launcher3.util.DaggerSingletonObject;
+import com.android.launcher3.util.DaggerSingletonTracker;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
import com.android.launcher3.util.DisplayController.Info;
@@ -83,21 +88,24 @@
import com.android.systemui.shared.system.TaskStackChangeListeners;
import java.io.PrintWriter;
-import java.util.ArrayList;
+
+import javax.inject.Inject;
/**
* Manages the state of the system during a swipe up gesture.
*/
+@LauncherAppSingleton
public class RecentsAnimationDeviceState implements DisplayInfoChangeListener, ExclusionListener {
- private static final String TAG = "RecentsAnimationDeviceState";
-
static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
// TODO: Move to quickstep contract
private static final float QUICKSTEP_TOUCH_SLOP_RATIO_TWO_BUTTON = 3f;
private static final float QUICKSTEP_TOUCH_SLOP_RATIO_GESTURAL = 1.414f;
+ public static DaggerSingletonObject<RecentsAnimationDeviceState> INSTANCE =
+ new DaggerSingletonObject<>(LauncherAppComponent::getRecentsAnimationDeviceState);
+
private final Context mContext;
private final DisplayController mDisplayController;
@@ -106,12 +114,11 @@
private final RotationTouchHelper mRotationTouchHelper;
private final TaskStackChangeListener mPipListener;
+ private final DaggerSingletonTracker mLifeCycle;
// Cache for better performance since it doesn't change at runtime.
private final boolean mCanImeRenderGesturalNavButtons =
InputMethodService.canImeRenderGesturalNavButtons();
- private final ArrayList<Runnable> mOnDestroyActions = new ArrayList<>();
-
private @SystemUiStateFlags long mSystemUiStateFlags = QuickStepContract.SYSUI_STATE_AWAKE;
private NavigationMode mMode = THREE_BUTTONS;
private NavBarPosition mNavBarPosition;
@@ -130,55 +137,39 @@
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);
- }
-
@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
- */
+ @Inject
RecentsAnimationDeviceState(
- Context context, boolean isInstanceForTouches,
- GestureExclusionManager exclusionManager) {
+ @ApplicationContext Context context,
+ GestureExclusionManager exclusionManager,
+ DisplayController displayController,
+ ContextualSearchStateManager contextualSearchStateManager,
+ RotationTouchHelper rotationTouchHelper,
+ SettingsCache settingsCache,
+ DaggerSingletonTracker lifeCycle) {
mContext = context;
- mDisplayController = DisplayController.INSTANCE.get(context);
+ mDisplayController = displayController;
mExclusionManager = exclusionManager;
- mContextualSearchStateManager = ContextualSearchStateManager.INSTANCE.get(context);
+ mContextualSearchStateManager = contextualSearchStateManager;
+ mRotationTouchHelper = rotationTouchHelper;
+ mLifeCycle = lifeCycle;
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());
+ mLifeCycle.addCloseable(this::unregisterExclusionListener);
// Register for display changes changes
mDisplayController.addChangeListener(this);
onDisplayInfoChanged(context, mDisplayController.getInfo(), CHANGE_ALL);
- runOnDestroy(() -> mDisplayController.removeChangeListener(this));
+ mLifeCycle.addCloseable(() -> mDisplayController.removeChangeListener(this));
- SettingsCache settingsCache = SettingsCache.INSTANCE.get(mContext);
if (mIsOneHandedModeSupported) {
Uri oneHandedUri = Settings.Secure.getUriFor(ONE_HANDED_ENABLED);
SettingsCache.OnChangeListener onChangeListener =
enabled -> mIsOneHandedModeEnabled = enabled;
settingsCache.register(oneHandedUri, onChangeListener);
mIsOneHandedModeEnabled = settingsCache.getValue(oneHandedUri);
- runOnDestroy(() -> settingsCache.unregister(oneHandedUri, onChangeListener));
+ mLifeCycle.addCloseable(() -> settingsCache.unregister(oneHandedUri, onChangeListener));
} else {
mIsOneHandedModeEnabled = false;
}
@@ -189,14 +180,16 @@
enabled -> mIsSwipeToNotificationEnabled = enabled;
settingsCache.register(swipeBottomNotificationUri, onChangeListener);
mIsSwipeToNotificationEnabled = settingsCache.getValue(swipeBottomNotificationUri);
- runOnDestroy(() -> settingsCache.unregister(swipeBottomNotificationUri, onChangeListener));
+ mLifeCycle.addCloseable(
+ () -> settingsCache.unregister(swipeBottomNotificationUri, onChangeListener));
Uri setupCompleteUri = Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE);
mIsUserSetupComplete = settingsCache.getValue(setupCompleteUri, 0);
if (!mIsUserSetupComplete) {
SettingsCache.OnChangeListener userSetupChangeListener = e -> mIsUserSetupComplete = e;
settingsCache.register(setupCompleteUri, userSetupChangeListener);
- runOnDestroy(() -> settingsCache.unregister(setupCompleteUri, userSetupChangeListener));
+ mLifeCycle.addCloseable(
+ () -> settingsCache.unregister(setupCompleteUri, userSetupChangeListener));
}
try {
@@ -217,23 +210,10 @@
}
};
TaskStackChangeListeners.getInstance().registerTaskStackListener(mPipListener);
- runOnDestroy(() ->
+ mLifeCycle.addCloseable(() ->
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mPipListener));
}
- private void runOnDestroy(Runnable action) {
- mOnDestroyActions.add(action);
- }
-
- /**
- * Cleans up all the registered listeners and receivers.
- */
- public void destroy() {
- for (Runnable r : mOnDestroyActions) {
- r.run();
- }
- }
-
/**
* Adds a listener for the nav mode change, guaranteed to be called after the device state's
* mode has changed.
@@ -246,7 +226,7 @@
};
mDisplayController.addChangeListener(listener);
callback.run();
- runOnDestroy(() -> mDisplayController.removeChangeListener(listener));
+ mLifeCycle.addCloseable(() -> mDisplayController.removeChangeListener(listener));
}
@Override
@@ -603,10 +583,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..c4b0f25 100644
--- a/quickstep/src/com/android/quickstep/RecentsFilterState.java
+++ b/quickstep/src/com/android/quickstep/RecentsFilterState.java
@@ -19,6 +19,8 @@
import androidx.annotation.Nullable;
import com.android.quickstep.util.GroupTask;
+import com.android.quickstep.views.TaskViewType;
+import com.android.systemui.shared.recents.model.Task;
import java.util.HashMap;
import java.util.List;
@@ -37,7 +39,7 @@
public static final int MIN_FILTERING_TASK_COUNT = 2;
// default filter that returns true for any input
- public static final Predicate<GroupTask> DEFAULT_FILTER = (groupTask -> true);
+ public static final Predicate<GroupTask> EMPTY_FILTER = (groupTask -> true);
// the package name to filter recent tasks by
@Nullable
@@ -115,16 +117,37 @@
* Returns a predicate for filtering out GroupTasks by package name.
*
* @param packageName package name to filter GroupTasks by
- * if null, Predicate always returns true.
+ * if null, Predicate filters out desktop tasks with no non-minimized tasks.
*/
public static Predicate<GroupTask> getFilter(@Nullable String packageName) {
if (packageName == null) {
- return DEFAULT_FILTER;
+ return getEmptyDesktopTaskFilter();
}
- return (groupTask) -> (groupTask.task2 != null
- && groupTask.task2.key.getPackageName().equals(packageName))
- || groupTask.task1.key.getPackageName().equals(packageName);
+ return (groupTask) -> (groupTask.containsPackage(packageName)
+ && !isDestopTaskWithMinimizedTasksOnly(groupTask));
+ }
+
+ /**
+ * Returns a predicate that filters out desk tasks that contain no non-minimized desktop tasks.
+ */
+ public static Predicate<GroupTask> getEmptyDesktopTaskFilter() {
+ return (groupTask -> !isDestopTaskWithMinimizedTasksOnly(groupTask));
+ }
+
+ /**
+ * Whether the provided task is a desktop task with no non-minimized tasks - returns true if the
+ * desktop task has no tasks at all.
+ *
+ * @param groupTask The group task to check.
+ */
+ static boolean isDestopTaskWithMinimizedTasksOnly(GroupTask groupTask) {
+ if (groupTask.taskViewType != TaskViewType.DESKTOP) {
+ return false;
+ }
+ return groupTask.getTasks().stream()
+ .filter(task -> !task.isMinimized)
+ .toList().isEmpty();
}
/**
@@ -136,17 +159,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..dc5d59f 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() {
@@ -146,7 +154,7 @@
/**
* Fetches the list of recent tasks. Tasks are ordered by recency, with the latest active tasks
- * at the end of the list.
+ * at the end of the list. Filters out desktop tasks that contain no non-minimized tasks.
*
* @param callback The callback to receive the task plan once its complete or null. This is
* always called on the UI thread.
@@ -155,7 +163,7 @@
@Override
public int getTasks(@Nullable Consumer<List<GroupTask>> callback) {
return mTaskList.getTasks(false /* loadKeysOnly */, callback,
- RecentsFilterState.DEFAULT_FILTER);
+ RecentsFilterState.getEmptyDesktopTaskFilter());
}
/**
@@ -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 89337e5..ef63b9b 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;
@@ -141,7 +142,9 @@
if (mSplitBounds == null) {
SplitBounds shellSplitBounds = targets.extras.getParcelable(KEY_EXTRA_SPLIT_BOUNDS,
SplitBounds.class);
- mSplitBounds = convertShellSplitBoundsToLauncher(shellSplitBounds);
+ if (shellSplitBounds != null) {
+ mSplitBounds = convertShellSplitBoundsToLauncher(shellSplitBounds);
+ }
}
boolean containsSplitTargets = mSplitBounds != null;
@@ -213,7 +216,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++) {
@@ -222,6 +226,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..a614327 100644
--- a/quickstep/src/com/android/quickstep/RotationTouchHelper.java
+++ b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
@@ -33,13 +33,16 @@
import android.view.MotionEvent;
import android.view.OrientationEventListener;
+import com.android.launcher3.dagger.ApplicationContext;
+import com.android.launcher3.dagger.LauncherAppComponent;
+import com.android.launcher3.dagger.LauncherAppSingleton;
import com.android.launcher3.testing.shared.TestProtocol;
+import com.android.launcher3.util.DaggerSingletonObject;
+import com.android.launcher3.util.DaggerSingletonTracker;
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.quickstep.util.RecentsOrientedState;
import com.android.systemui.shared.Flags;
import com.android.systemui.shared.system.QuickStepContract;
@@ -47,26 +50,27 @@
import com.android.systemui.shared.system.TaskStackChangeListeners;
import java.io.PrintWriter;
-import java.util.ArrayList;
+
+import javax.inject.Inject;
/**
* Helper class for transforming touch events
*/
-public class RotationTouchHelper implements DisplayInfoChangeListener, SafeCloseable {
+@LauncherAppSingleton
+public class RotationTouchHelper implements DisplayInfoChangeListener {
- public static final MainThreadInitializedObject<RotationTouchHelper> INSTANCE =
- new MainThreadInitializedObject<>(RotationTouchHelper::new);
+ public static final DaggerSingletonObject<RotationTouchHelper> INSTANCE =
+ new DaggerSingletonObject<>(LauncherAppComponent::getRotationTouchHelper);
- private OrientationTouchTransformer mOrientationTouchTransformer;
- private DisplayController mDisplayController;
- private int mDisplayId;
+ private final OrientationTouchTransformer mOrientationTouchTransformer;
+ private final DisplayController mDisplayController;
+ private final SystemUiProxy mSystemUiProxy;
+ 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 +97,7 @@
}
};
- private Runnable mExitOverviewRunnable = new Runnable() {
+ private final Runnable mExitOverviewRunnable = new Runnable() {
@Override
public void run() {
mInOverview = false;
@@ -107,7 +111,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 +124,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,35 +134,24 @@
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) {
+ @Inject
+ RotationTouchHelper(@ApplicationContext Context context,
+ DisplayController displayController,
+ SystemUiProxy systemUiProxy,
+ DaggerSingletonTracker lifeCycle) {
mContext = context;
- if (mNeedsInit) {
- init();
- }
- }
-
- public void init() {
- if (!mNeedsInit) {
- return;
- }
- mDisplayController = DisplayController.INSTANCE.get(mContext);
- Resources resources = mContext.getResources();
+ mDisplayController = displayController;
+ mSystemUiProxy = systemUiProxy;
mDisplayId = DEFAULT_DISPLAY;
+ Resources resources = mContext.getResources();
mOrientationTouchTransformer = new OrientationTouchTransformer(resources, mMode,
() -> QuickStepContract.getWindowCornerRadius(mContext));
// 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 +172,13 @@
}
}
};
- 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();
+ lifeCycle.addCloseable(() -> {
+ mDisplayController.removeChangeListener(this);
+ mOrientationListener.disable();
+ TaskStackChangeListeners.getInstance()
+ .unregisterTaskStackListener(mFrozenTaskListener);
+ });
}
public boolean isTaskListFrozen() {
@@ -264,10 +229,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 +261,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 +324,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);
@@ -390,8 +351,7 @@
}
private void notifySysuiOfCurrentRotation(int rotation) {
- UI_HELPER_EXECUTOR.execute(() -> SystemUiProxy.INSTANCE.get(mContext)
- .notifyPrioritizedRotation(rotation));
+ UI_HELPER_EXECUTOR.execute(() -> mSystemUiProxy.notifyPrioritizedRotation(rotation));
}
/**
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..87953c7 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.kt
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.kt
@@ -19,11 +19,9 @@
import android.app.ActivityManager.RunningTaskInfo
import android.app.ActivityOptions
import android.app.PendingIntent
-import android.app.PictureInPictureParams
import android.content.ComponentName
import android.content.Context
import android.content.Intent
-import android.content.pm.ActivityInfo
import android.content.pm.ShortcutInfo
import android.graphics.Point
import android.graphics.Rect
@@ -45,6 +43,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 +61,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
@@ -81,6 +81,7 @@
import com.android.wm.shell.common.pip.IPipAnimationListener
import com.android.wm.shell.desktopmode.IDesktopMode
import com.android.wm.shell.desktopmode.IDesktopTaskListener
+import com.android.wm.shell.desktopmode.IMoveToDesktopCallback
import com.android.wm.shell.draganddrop.IDragAndDrop
import com.android.wm.shell.onehanded.IOneHanded
import com.android.wm.shell.recents.IRecentTasks
@@ -93,6 +94,7 @@
import com.android.wm.shell.shared.bubbles.BubbleBarLocation.UpdateSource
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource
+import com.android.wm.shell.shared.desktopmode.DesktopTaskToFrontReason
import com.android.wm.shell.shared.split.SplitBounds
import com.android.wm.shell.shared.split.SplitScreenConstants.PersistentSnapPosition
import com.android.wm.shell.splitscreen.ISplitScreen
@@ -168,7 +170,7 @@
* different process). It is bare-bones, so it's expected that the component and options will be
* provided via fill-in intent.
*/
- private val recentsPendingIntent =
+ private val recentsPendingIntent by lazy {
PendingIntent.getActivity(
context,
0,
@@ -182,6 +184,7 @@
)
.toBundle(),
)
+ }
val unfoldTransitionProvider: ProxyUnfoldTransitionProvider? =
if ((Flags.enableUnfoldStateAnimation() && ResourceUnfoldTransitionConfig().isEnabled))
@@ -215,9 +218,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) =
@@ -497,20 +500,12 @@
/** @return Destination bounds of auto-pip animation, `null` if the animation is not ready. */
fun startSwipePipToHome(
- componentName: ComponentName?,
- activityInfo: ActivityInfo?,
- pictureInPictureParams: PictureInPictureParams?,
+ taskInfo: RunningTaskInfo,
launcherRotation: Int,
hotseatKeepClearArea: Rect?,
): Rect? {
executeWithErrorLog({ "Failed call startSwipePipToHome" }) {
- return pip?.startSwipePipToHome(
- componentName,
- activityInfo,
- pictureInPictureParams,
- launcherRotation,
- hotseatKeepClearArea,
- )
+ return pip?.startSwipePipToHome(taskInfo, launcherRotation, hotseatKeepClearArea)
}
return null
}
@@ -671,8 +666,10 @@
*
* @param intent the intent used to create the bubble.
*/
- fun showAppBubble(intent: Intent?) =
- executeWithErrorLog({ "Failed call showAppBubble" }) { bubbles?.showAppBubble(intent) }
+ fun showAppBubble(intent: Intent?, user: UserHandle) =
+ executeWithErrorLog({ "Failed call showAppBubble" }) {
+ bubbles?.showAppBubble(intent, user)
+ }
/** Tells SysUI to show the expanded view. */
fun showExpandedView() =
@@ -836,6 +833,15 @@
splitScreen?.startIntent(intent, userId, fillInIntent, position, options, instanceId)
}
+ /**
+ * Call the desktop mode interface to start a TRANSIT_OPEN transition when launching an intent
+ * from the taskbar so that it can be handled in desktop mode.
+ */
+ fun startLaunchIntentTransition(intent: Intent, options: Bundle, displayId: Int) =
+ executeWithErrorLog({ "Failed call startLaunchIntentTransition" }) {
+ desktopMode?.startLaunchIntentTransition(intent, options, displayId)
+ }
+
//
// One handed
//
@@ -1067,6 +1073,19 @@
//
// Desktop Mode
//
+ /** Calls shell to create a new desk (if possible) on the display whose ID is `displayId`. */
+ fun createDesktop(displayId: Int) =
+ executeWithErrorLog({ "Failed call createDesk" }) { desktopMode?.createDesk(displayId) }
+
+ /**
+ * Calls shell to activate the desk whose ID is `deskId` on whatever display it exists on. This
+ * will bring all tasks on this desk to the front.
+ */
+ fun activateDesktop(deskId: Int, transition: RemoteTransition?) =
+ executeWithErrorLog({ "Failed call activateDesk" }) {
+ desktopMode?.activateDesk(deskId, transition)
+ }
+
/** Call shell to show all apps active on the desktop */
fun showDesktopApps(displayId: Int, transition: RemoteTransition?) =
executeWithErrorLog({ "Failed call showDesktopApps" }) {
@@ -1074,19 +1093,15 @@
}
/** If task with the given id is on the desktop, bring it to front */
- fun showDesktopApp(taskId: Int, transition: RemoteTransition?) =
+ fun showDesktopApp(
+ taskId: Int,
+ transition: RemoteTransition?,
+ toFrontReason: DesktopTaskToFrontReason,
+ ) =
executeWithErrorLog({ "Failed call showDesktopApp" }) {
- desktopMode?.showDesktopApp(taskId, transition)
+ desktopMode?.showDesktopApp(taskId, transition, toFrontReason)
}
- /** Call shell to get number of visible freeform tasks */
- fun getVisibleDesktopTaskCount(displayId: Int): Int {
- executeWithErrorLog({ "Failed call getVisibleDesktopTaskCount" }) {
- return desktopMode?.getVisibleTaskCount(displayId) ?: 0
- }
- return 0
- }
-
/** Set a listener on shell to get updates about desktop task state */
fun setDesktopTaskListener(listener: IDesktopTaskListener?) {
desktopTaskListener = listener
@@ -1106,9 +1121,19 @@
taskId: Int,
transitionSource: DesktopModeTransitionSource?,
transition: RemoteTransition?,
+ successCallback: Runnable,
) =
executeWithErrorLog({ "Failed call moveToDesktop" }) {
- desktopMode?.moveToDesktop(taskId, transitionSource, transition)
+ desktopMode?.moveToDesktop(
+ taskId,
+ transitionSource,
+ transition,
+ object : IMoveToDesktopCallback.Stub() {
+ override fun onTaskMovedToDesktop() {
+ successCallback.run()
+ }
+ },
+ )
}
/** Call shell to remove the desktop that is on given `displayId` */
@@ -1174,6 +1199,7 @@
homeContentInsets: Rect?,
minimizedHomeBounds: Rect?,
extras: Bundle?,
+ transitionInfo: TransitionInfo?,
) =
listener.onAnimationStart(
RecentsAnimationControllerCompat(controller),
@@ -1186,6 +1212,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/TaskThumbnailCache.kt b/quickstep/src/com/android/quickstep/TaskThumbnailCache.kt
index 7b56213..1d880ab 100644
--- a/quickstep/src/com/android/quickstep/TaskThumbnailCache.kt
+++ b/quickstep/src/com/android/quickstep/TaskThumbnailCache.kt
@@ -119,7 +119,7 @@
highResLoadingState.isEnabled
) {
val newCachedThumbnail = cache.getAndInvalidateIfModified(task.key)
- if (newCachedThumbnail.thumbnail != null && !newCachedThumbnail.reducedResolution) {
+ if (newCachedThumbnail?.thumbnail != null && !newCachedThumbnail.reducedResolution) {
return newCachedThumbnail
}
}
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..b3d9da3 100644
--- a/quickstep/src/com/android/quickstep/TopTaskTracker.java
+++ b/quickstep/src/com/android/quickstep/TopTaskTracker.java
@@ -24,12 +24,12 @@
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_A;
+import static com.android.wm.shell.Flags.enableShellTopTaskTracking;
import static com.android.wm.shell.Flags.enableFlexibleSplit;
import static com.android.wm.shell.shared.GroupedTaskInfo.TYPE_SPLIT;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.TaskInfo;
-import android.content.Context;
import android.util.ArrayMap;
import android.util.Log;
@@ -37,13 +37,15 @@
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.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,20 +62,20 @@
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;
- private final Context mContext;
-
// Only used when Flags.enableShellTopTaskTracking() is disabled
// Ordered list with first item being the most recent task.
private final LinkedList<TaskInfo> mOrderedTaskList = new LinkedList<>();
@@ -82,39 +84,33 @@
private int mPinnedTaskId = INVALID_TASK_ID;
// Only used when Flags.enableShellTopTaskTracking() is enabled
- // Mapping of display id to running tasks. Running tasks are ordered from top most to
- // bottom most.
- private ArrayMap<Integer, ArrayList<GroupedTaskInfo>> mVisibleTasks = new ArrayMap<>();
+ // Mapping of display id to visible tasks. Visible tasks are ordered from top most to bottom
+ // most.
+ private ArrayMap<Integer, GroupedTaskInfo> mVisibleTasks = new ArrayMap<>();
- private TopTaskTracker(Context context) {
- mContext = context;
-
- if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
- // Just prepopulate a list for the default display tasks so we don't need to add null
- // checks everywhere
- mVisibleTasks.put(DEFAULT_DISPLAY, new ArrayList<>());
- } else {
+ @Inject
+ public TopTaskTracker(DaggerSingletonTracker tracker, SystemUiProxy systemUiProxy) {
+ if (!enableShellTopTaskTracking()) {
mMainStagePosition.stageType = SplitConfigurationOptions.STAGE_TYPE_MAIN;
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 (enableShellTopTaskTracking()) {
+ return;
+ }
+
+ TaskStackChangeListeners.getInstance().unregisterTaskStackListener(this);
+ systemUiProxy.unregisterSplitScreenListener(this);
+ });
}
@Override
public void onTaskRemoved(int taskId) {
- if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ if (enableShellTopTaskTracking()) {
return;
}
@@ -127,7 +123,7 @@
}
void handleTaskMovedToFront(TaskInfo taskInfo) {
- if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ if (enableShellTopTaskTracking()) {
return;
}
@@ -181,32 +177,25 @@
* Called when the set of visible tasks have changed.
*/
public void onVisibleTasksChanged(GroupedTaskInfo[] visibleTasks) {
- if (!com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ if (!enableShellTopTaskTracking()) {
return;
}
- // TODO(346588978): Per-display info, just have everything in order by display
-
// Clear existing tasks for each display
- mVisibleTasks.forEach((displayId, visibleTasksOnDisplay) -> visibleTasksOnDisplay.clear());
+ mVisibleTasks.clear();
// Update the visible tasks on each display
- for (int i = 0; i < visibleTasks.length; i++) {
- final int displayId = visibleTasks[i].getTaskInfo1().getDisplayId();
- final ArrayList<GroupedTaskInfo> displayTasks;
- if (mVisibleTasks.containsKey(displayId)) {
- displayTasks = mVisibleTasks.get(displayId);
- } else {
- displayTasks = new ArrayList<>();
- mVisibleTasks.put(displayId, displayTasks);
- }
- displayTasks.add(visibleTasks[i]);
+ Log.d(TAG, "onVisibleTasksChanged:");
+ for (GroupedTaskInfo groupedTask : visibleTasks) {
+ Log.d(TAG, "\t" + groupedTask);
+ final int displayId = groupedTask.getBaseGroupedTask().getTaskInfo1().getDisplayId();
+ mVisibleTasks.put(displayId, groupedTask);
}
}
@Override
public void onStagePositionChanged(@StageType int stage, @StagePosition int position) {
- if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ if (enableShellTopTaskTracking()) {
return;
}
@@ -218,7 +207,7 @@
}
public void onTaskChanged(RunningTaskInfo taskInfo) {
- if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ if (enableShellTopTaskTracking()) {
return;
}
@@ -232,7 +221,7 @@
@Override
public void onTaskStageChanged(int taskId, @StageType int stage, boolean visible) {
- if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ if (enableShellTopTaskTracking()) {
return;
}
@@ -256,7 +245,7 @@
@Override
public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
- if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ if (enableShellTopTaskTracking()) {
return;
}
@@ -265,7 +254,7 @@
@Override
public void onActivityUnpinned() {
- if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ if (enableShellTopTaskTracking()) {
return;
}
@@ -273,16 +262,17 @@
}
/**
- * @return index 0 will be task in left/top position, index 1 in right/bottom position.
- * Will return empty array if device is not in staged split
+ * Return the running split task ids. Index 0 will be task in left/top position, index 1 in
+ * right/bottom position, or and empty array if device is not in splitscreen.
*/
public int[] getRunningSplitTaskIds() {
- if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
- // TODO(346588978): This assumes default display for now
- final ArrayList<GroupedTaskInfo> visibleTasks = mVisibleTasks.get(DEFAULT_DISPLAY);
- final GroupedTaskInfo splitTaskInfo = visibleTasks.stream()
- .filter(taskInfo -> taskInfo.getType() == TYPE_SPLIT)
- .findFirst().orElse(null);
+ if (enableShellTopTaskTracking()) {
+ // TODO(346588978): This assumes default display as splitscreen is only currently there
+ final GroupedTaskInfo visibleTasks = mVisibleTasks.get(DEFAULT_DISPLAY);
+ final GroupedTaskInfo splitTaskInfo =
+ visibleTasks != null && visibleTasks.isBaseType(TYPE_SPLIT)
+ ? visibleTasks.getBaseGroupedTask()
+ : null;
if (splitTaskInfo != null && splitTaskInfo.getSplitBounds() != null) {
return new int[] {
splitTaskInfo.getSplitBounds().leftTopTaskId,
@@ -311,24 +301,13 @@
* Dumps the list of tasks in top task tracker.
*/
public void dump(PrintWriter pw) {
- if (!com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ if (!enableShellTopTaskTracking()) {
return;
}
- // TODO(346588978): This assumes default display for now
- final ArrayList<GroupedTaskInfo> displayTasks = mVisibleTasks.get(DEFAULT_DISPLAY);
pw.println("TopTaskTracker:");
- pw.println(" tasks: [");
- for (GroupedTaskInfo taskInfo : displayTasks) {
- final TaskInfo info = taskInfo.getTaskInfo1();
- final boolean isExcluded = (info.baseIntent.getFlags()
- & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0;
- pw.println(" " + info.taskId + ": excluded=" + isExcluded
- + " visibleRequested=" + info.isVisibleRequested
- + " visible=" + info.isVisible
- + " " + info.baseIntent.getComponent());
- }
- pw.println(" ]");
+ mVisibleTasks.forEach((displayId, tasks) ->
+ pw.println(" visibleTasks(" + displayId + "): " + tasks));
}
/**
@@ -337,13 +316,12 @@
@NonNull
@UiThread
public CachedTaskInfo getCachedTopTask(boolean filterOnlyVisibleRecents) {
- if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ if (enableShellTopTaskTracking()) {
// TODO(346588978): Currently ignore filterOnlyVisibleRecents, but perhaps make this an
// explicit filter For things to ignore (ie. PIP/Bubbles/Assistant/etc/so that this is
// explicit)
- // TODO(346588978): This assumes default display for now (as does all of Launcher)
- final ArrayList<GroupedTaskInfo> displayTasks = mVisibleTasks.get(DEFAULT_DISPLAY);
- return new CachedTaskInfo(new ArrayList<>(displayTasks));
+ // TODO(346588978): This assumes default display as gesture nav is only supported there
+ return new CachedTaskInfo(mVisibleTasks.get(DEFAULT_DISPLAY));
} else {
if (filterOnlyVisibleRecents) {
// Since we only know about the top most task, any filtering may not be applied on
@@ -368,6 +346,11 @@
}
}
+ private static boolean isHomeTask(TaskInfo task) {
+ return task != null && task.configuration.windowConfiguration
+ .getActivityType() == ACTIVITY_TYPE_HOME;
+ }
+
private static boolean isRecentsTask(TaskInfo task) {
return task != null && task.configuration.windowConfiguration
.getActivityType() == ACTIVITY_TYPE_RECENTS;
@@ -378,7 +361,6 @@
* during the lifecycle of the task.
*/
public static class CachedTaskInfo {
-
// Only used when enableShellTopTaskTracking() is disabled
@Nullable
private final TaskInfo mTopTask;
@@ -387,40 +369,48 @@
// Only used when enableShellTopTaskTracking() is enabled
@Nullable
- private final GroupedTaskInfo mTopGroupedTask;
- @Nullable
- private final ArrayList<GroupedTaskInfo> mVisibleTasks;
+ private final GroupedTaskInfo mVisibleTasks;
// Only used when enableShellTopTaskTracking() is enabled
- CachedTaskInfo(@NonNull ArrayList<GroupedTaskInfo> visibleTasks) {
+ CachedTaskInfo(@Nullable GroupedTaskInfo visibleTasks) {
mAllCachedTasks = null;
mTopTask = null;
mVisibleTasks = visibleTasks;
- mTopGroupedTask = !mVisibleTasks.isEmpty() ? mVisibleTasks.getFirst() : null;
}
// Only used when enableShellTopTaskTracking() is disabled
CachedTaskInfo(@NonNull List<TaskInfo> allCachedTasks) {
mVisibleTasks = null;
- mTopGroupedTask = null;
mAllCachedTasks = allCachedTasks;
mTopTask = allCachedTasks.isEmpty() ? null : allCachedTasks.get(0);
}
/**
- * @return The list of visible tasks
+ * Returns the "base" task that is used the as the representative running task of the set
+ * of tasks initially provided.
+ *
+ * Not for general use, as in other windowing modes (ie. split/desktop) the caller should
+ * not make assumptions about there being a single base task.
+ * TODO(346588978): Try to remove all usage of this if possible
*/
- public ArrayList<GroupedTaskInfo> getVisibleTasks() {
- return mVisibleTasks;
+ @Nullable
+ private TaskInfo getLegacyBaseTask() {
+ if (enableShellTopTaskTracking()) {
+ return mVisibleTasks != null
+ ? mVisibleTasks.getBaseGroupedTask().getTaskInfo1()
+ : null;
+ } else {
+ return mTopTask;
+ }
}
/**
- * @return The top task id
+ * Returns the top task id.
*/
public int getTaskId() {
- if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ if (enableShellTopTaskTracking()) {
// Callers should use topGroupedTaskContainsTask() instead
return INVALID_TASK_ID;
} else {
@@ -429,29 +419,58 @@
}
/**
- * @return Whether the top grouped task contains the given {@param taskId} if
- * Flags.enableShellTopTaskTracking() is true, otherwise it checks the top
- * task as reported from TaskStackListener.
+ * Returns the top grouped task ids if Flags.enableShellTopTaskTracking() is true, otherwise
+ * an empty array.
+ */
+ public int[] topGroupedTaskIds() {
+ if (enableShellTopTaskTracking()) {
+ if (mVisibleTasks == null) {
+ return new int[0];
+ }
+ List<TaskInfo> groupedTasks = mVisibleTasks.getTaskInfoList();
+ return groupedTasks.stream().mapToInt(
+ groupedTask -> groupedTask.taskId).toArray();
+ } else {
+ // Not used
+ return new int[0];
+ }
+ }
+
+ /**
+ * Returns whether the top grouped task contains the given {@param taskId} if
+ * Flags.enableShellTopTaskTracking() is true, otherwise it checks the top task as reported
+ * from TaskStackListener.
*/
public boolean topGroupedTaskContainsTask(int taskId) {
- if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
- return mTopGroupedTask != null && mTopGroupedTask.containsTask(taskId);
+ if (enableShellTopTaskTracking()) {
+ return mVisibleTasks != null && mVisibleTasks.containsTask(taskId);
} else {
return mTopTask != null && mTopTask.taskId == taskId;
}
}
/**
- * Returns true if the root of the task chooser activity
+ * Returns true if this represents the task chooser activity
*/
public boolean isRootChooseActivity() {
- if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
- // TODO(346588978): Update this to not make an assumption on a specific task info
- return mTopGroupedTask != null && ACTION_CHOOSER.equals(
- mTopGroupedTask.getTaskInfo1().baseIntent.getAction());
- } else {
- return mTopTask != null && ACTION_CHOOSER.equals(mTopTask.baseIntent.getAction());
- }
+ final TaskInfo baseTask = getLegacyBaseTask();
+ return baseTask != null && ACTION_CHOOSER.equals(baseTask.baseIntent.getAction());
+ }
+
+ /**
+ * Returns true if this represents the HOME activity type task
+ */
+ public boolean isHomeTask() {
+ final TaskInfo baseTask = getLegacyBaseTask();
+ return baseTask != null && TopTaskTracker.isHomeTask(baseTask);
+ }
+
+ /**
+ * Returns true if this represents the RECENTS activity type task
+ */
+ public boolean isRecentsTask() {
+ final TaskInfo baseTask = getLegacyBaseTask();
+ return baseTask != null && TopTaskTracker.isRecentsTask(baseTask);
}
/**
@@ -459,7 +478,7 @@
* is another running task that is not excluded from recents, returns that underlying task.
*/
public @Nullable CachedTaskInfo getVisibleNonExcludedTask() {
- if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ if (enableShellTopTaskTracking()) {
// Callers should not need this when the full set of visible tasks are provided
return null;
}
@@ -479,49 +498,16 @@
}
/**
- * Returns true if this represents the HOME activity type task
- */
- public boolean isHomeTask() {
- if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
- // TODO(346588978): Update this to not make an assumption on a specific task info
- return mTopGroupedTask != null
- && mTopGroupedTask.getTaskInfo1().getActivityType() == ACTIVITY_TYPE_HOME;
- } else {
- return mTopTask != null && mTopTask.configuration.windowConfiguration
- .getActivityType() == ACTIVITY_TYPE_HOME;
- }
- }
-
- /**
- * Returns true if this represents the RECENTS activity type task
- */
- public boolean isRecentsTask() {
- if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
- // TODO(346588978): Update this to not make an assumption on a specific task info
- return mTopGroupedTask != null
- && TopTaskTracker.isRecentsTask(mTopGroupedTask.getTaskInfo1());
- } else {
- return TopTaskTracker.isRecentsTask(mTopTask);
- }
- }
-
- /**
* Returns {@link Task} array which can be used as a placeholder until the true object
* is loaded by the model
*/
public Task[] getPlaceholderTasks() {
- if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
- // TODO(346588978): Update this to return more than a single task once the callers
- // are refactored
- if (mVisibleTasks.isEmpty()) {
- return new Task[0];
- }
- final TaskInfo info = mVisibleTasks.getFirst().getTaskInfo1();
- return new Task[]{Task.from(new TaskKey(info), info, false)};
- } else {
- return mTopTask == null ? new Task[0]
- : new Task[]{Task.from(new TaskKey(mTopTask), mTopTask, false)};
- }
+ final TaskInfo baseTask = getLegacyBaseTask();
+ // TODO(346588978): Update this to return more than a single task once the callers
+ // are refactored
+ return baseTask == null
+ ? new Task[0]
+ : new Task[]{Task.from(new TaskKey(baseTask), baseTask, false)};
}
/**
@@ -529,13 +515,12 @@
* placeholder until the true object is loaded by the model
*/
public Task[] getSplitPlaceholderTasks(int[] taskIds) {
- if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
- if (mVisibleTasks.isEmpty()
- || mVisibleTasks.getFirst().getType() != TYPE_SPLIT) {
+ if (enableShellTopTaskTracking()) {
+ if (mVisibleTasks == null || !mVisibleTasks.isBaseType(TYPE_SPLIT)) {
return new Task[0];
}
- GroupedTaskInfo splitTask = mVisibleTasks.getFirst();
+ GroupedTaskInfo splitTask = mVisibleTasks.getBaseGroupedTask();
Task[] result = new Task[taskIds.length];
for (int i = 0; i < taskIds.length; i++) {
TaskInfo info = splitTask.getTaskById(taskIds[i]);
@@ -566,22 +551,11 @@
@Nullable
public String getPackageName() {
- if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
- // TODO(346588978): Update this to not make an assumption on a specific task info
- if (mTopGroupedTask == null) {
- return null;
- }
- final TaskInfo info = mTopGroupedTask.getTaskInfo1();
- if (info.baseActivity == null) {
- return null;
- }
- return info.baseActivity.getPackageName();
- } else {
- if (mTopTask == null || mTopTask.baseActivity == null) {
- return null;
- }
- return mTopTask.baseActivity.getPackageName();
+ final TaskInfo baseTask = getLegacyBaseTask();
+ if (baseTask == null || baseTask.baseActivity == null) {
+ return null;
}
+ return baseTask.baseActivity.getPackageName();
}
}
}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index aea02af..15f320d 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -47,9 +47,10 @@
import android.graphics.Region;
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.Log;
import android.view.Choreographer;
import android.view.InputDevice;
@@ -68,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;
@@ -96,6 +96,7 @@
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;
@@ -183,7 +184,7 @@
recentTasks, launcherUnlockAnimationController, backAnimation, desktopMode,
unfoldTransition, dragAndDrop);
tis.initInputMonitor("TISBinder#onInitialize()");
- tis.preloadOverview(true /* fromInit */);
+ ActivityPreloadUtil.preloadOverviewForTIS(tis, true /* fromInit */);
}));
}
@@ -300,6 +301,22 @@
@BinderThread
@Override
+ public void onDisplayReady(int displayId) {
+ MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(
+ tis -> executeForTaskbarManager(
+ taskbarManager -> taskbarManager.onDisplayReady(displayId))));
+ }
+
+ @BinderThread
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(
+ tis -> executeForTaskbarManager(
+ taskbarManager -> taskbarManager.onDisplayRemoved(displayId))));
+ }
+
+ @BinderThread
+ @Override
public void updateWallpaperVisibility(int displayId, boolean visible) {
MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(
tis -> executeForTaskbarManager(
@@ -350,16 +367,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 ->
@@ -396,6 +403,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();
@@ -552,8 +573,8 @@
// 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);
mTrackpadsConnected = new ActiveTrackpadList(this, () -> {
@@ -715,7 +736,6 @@
mOverviewComponentObserver.removeOverviewChangeListener(mOverviewChangeListener);
}
disposeEventHandlers("TouchInteractionService onDestroy()");
- mDeviceState.destroy();
SystemUiProxy.INSTANCE.get(this).clearProxy();
mAllAppsActionManager.onDestroy();
@@ -1036,47 +1056,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()) {
@@ -1104,7 +1083,7 @@
return;
}
- preloadOverview(false /* fromInit */);
+ ActivityPreloadUtil.preloadOverviewForTIS(this, false /* fromInit */);
}
private static boolean isTablet(Configuration config) {
@@ -1158,21 +1137,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 549c15b..fe25f32 100644
--- a/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java
+++ b/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java
@@ -21,9 +21,13 @@
import com.android.launcher3.model.WellbeingModel;
import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.quickstep.OverviewComponentObserver;
+import com.android.quickstep.RecentsAnimationDeviceState;
+import com.android.quickstep.RotationTouchHelper;
import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.TopTaskTracker;
import com.android.quickstep.fallback.window.RecentsDisplayModel;
import com.android.quickstep.util.AsyncClockEventDelegate;
+import com.android.quickstep.util.ContextualSearchStateManager;
/**
* Launcher Quickstep base component for Dagger injection.
@@ -46,4 +50,12 @@
OverviewComponentObserver getOverviewComponentObserver();
DesktopVisibilityController getDesktopVisibilityController();
+
+ TopTaskTracker getTopTaskTracker();
+
+ RotationTouchHelper getRotationTouchHelper();
+
+ ContextualSearchStateManager getContextualSearchStateManager();
+
+ RecentsAnimationDeviceState getRecentsAnimationDeviceState();
}
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index b76e39a..fff7e9b 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -33,7 +33,6 @@
import com.android.launcher3.Flags;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.desktop.DesktopRecentsTransitionController;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.statehandlers.DesktopVisibilityController;
@@ -45,9 +44,9 @@
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.SingleTask;
import com.android.quickstep.util.SplitSelectStateController;
import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.views.OverviewActionsView;
@@ -114,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);
}
/**
@@ -212,7 +210,7 @@
if (!found) {
ArrayList<GroupTask> newList = new ArrayList<>(taskGroups.size() + 1);
newList.addAll(taskGroups);
- newList.add(new GroupTask(mHomeTask, null, null));
+ newList.add(new SingleTask(mHomeTask));
taskGroups = newList;
}
}
@@ -286,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/RecentsDragLayer.java b/quickstep/src/com/android/quickstep/fallback/RecentsDragLayer.java
index a2884b6..7e5afc3 100644
--- a/quickstep/src/com/android/quickstep/fallback/RecentsDragLayer.java
+++ b/quickstep/src/com/android/quickstep/fallback/RecentsDragLayer.java
@@ -15,9 +15,15 @@
*/
package com.android.quickstep.fallback;
+import static com.android.launcher3.Flags.enableExpressiveDismissTaskMotion;
+
import android.content.Context;
import android.util.AttributeSet;
+import com.android.launcher3.statemanager.StatefulContainer;
+import com.android.launcher3.uioverrides.touchcontrollers.TaskViewRecentsTouchContext;
+import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController;
+import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchControllerDeprecated;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.views.RecentsViewContainer;
@@ -25,16 +31,33 @@
/**
* Drag layer for fallback recents activity
*/
-public class RecentsDragLayer<T extends Context & RecentsViewContainer> extends BaseDragLayer<T> {
+public class RecentsDragLayer<T extends Context & RecentsViewContainer
+ & StatefulContainer<RecentsState>> extends BaseDragLayer<T> {
+
+ private final TaskViewRecentsTouchContext mTaskViewRecentsTouchContext =
+ new TaskViewRecentsTouchContext() {
+ @Override
+ public boolean isRecentsInteractive() {
+ return mContainer.getRootView().hasWindowFocus()
+ || mContainer.getStateManager().getState().hasLiveTile();
+ }
+
+ @Override
+ public boolean isRecentsModal() {
+ return false;
+ }
+ };
+
public RecentsDragLayer(Context context, AttributeSet attrs) {
super(context, attrs, 1 /* alphaChannelCount */);
}
@Override
public void recreateControllers() {
- mControllers = new TouchController[] {
- new RecentsTaskController(mContainer),
- new FallbackNavBarTouchController(mContainer),
- };
+ mControllers = new TouchController[]{
+ enableExpressiveDismissTaskMotion() ? new TaskViewTouchController<>(mContainer,
+ mTaskViewRecentsTouchContext) : new TaskViewTouchControllerDeprecated<>(
+ mContainer, mTaskViewRecentsTouchContext),
+ new FallbackNavBarTouchController(mContainer)};
}
}
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java b/quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java
deleted file mode 100644
index 07da379..0000000
--- a/quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java
+++ /dev/null
@@ -1,40 +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.fallback;
-
-import android.content.Context;
-
-import com.android.launcher3.statemanager.StatefulContainer;
-import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController;
-import com.android.quickstep.views.RecentsViewContainer;
-
-public class RecentsTaskController<T extends Context & RecentsViewContainer &
- StatefulContainer<RecentsState>> extends TaskViewTouchController<T> {
- public RecentsTaskController(T container) {
- super(container);
- }
-
- @Override
- protected boolean isRecentsInteractive() {
- return mContainer.getRootView().hasWindowFocus()
- || mContainer.getStateManager().getState().hasLiveTile();
- }
-
- @Override
- protected boolean isRecentsModal() {
- return false;
- }
-}
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt b/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt
index a9259d9..31a1be8 100644
--- a/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt
@@ -17,13 +17,15 @@
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.launcher3.util.WallpaperColorHints
import com.android.quickstep.DisplayModel
import com.android.quickstep.FallbackWindowInterface
import com.android.quickstep.dagger.QuickstepBaseAppComponent
@@ -31,8 +33,13 @@
import javax.inject.Inject
@LauncherAppSingleton
-class RecentsDisplayModel @Inject constructor(@ApplicationContext context: Context) :
- DisplayModel<RecentsDisplayResource>(context) {
+class RecentsDisplayModel
+@Inject
+constructor(
+ @ApplicationContext context: Context,
+ private val wallpaperColorHints: WallpaperColorHints,
+ tracker: DaggerSingletonTracker,
+) : DisplayModel<RecentsDisplayResource>(context) {
companion object {
private const val TAG = "RecentsDisplayModel"
@@ -47,19 +54,42 @@
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))
+ RecentsDisplayResource(
+ displayId,
+ context.createDisplayContext(display),
+ wallpaperColorHints.hints,
+ )
}
fun getRecentsWindowManager(displayId: Int): RecentsWindowManager? {
@@ -70,9 +100,12 @@
return getDisplayResource(displayId)?.fallbackWindowInterface
}
- data class RecentsDisplayResource(var displayId: Int, var displayContext: Context) :
- DisplayResource() {
- val recentsWindowManager = RecentsWindowManager(displayContext)
+ data class RecentsDisplayResource(
+ var displayId: Int,
+ var displayContext: Context,
+ val wallpaperColorHints: Int,
+ ) : DisplayResource() {
+ val recentsWindowManager = RecentsWindowManager(displayContext, wallpaperColorHints)
val fallbackWindowInterface: FallbackWindowInterface =
FallbackWindowInterface(recentsWindowManager)
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowContext.kt b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowContext.kt
index 52a7682..047658c 100644
--- a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowContext.kt
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowContext.kt
@@ -32,11 +32,16 @@
/**
* Window context for the Overview overlays.
+ *
* <p>
* Overlays have their own window and need a window context.
*/
-open class RecentsWindowContext(windowContext: Context) :
- ContextThemeWrapper(windowContext, Themes.getActivityThemeRes(windowContext)), ActivityContext {
+open class RecentsWindowContext(windowContext: Context, wallpaperColorHints: Int) :
+ ContextThemeWrapper(
+ windowContext,
+ Themes.getActivityThemeRes(windowContext, wallpaperColorHints),
+ ),
+ ActivityContext {
private var deviceProfile: DeviceProfile? = null
private var dragLayer: RecentsDragLayer<RecentsWindowManager> = RecentsDragLayer(this, null)
@@ -48,7 +53,9 @@
protected var windowLayoutParams: WindowManager.LayoutParams? =
createDefaultWindowLayoutParams(
- WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, windowTitle)
+ WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
+ windowTitle,
+ )
override fun getDragLayer(): BaseDragLayer<RecentsWindowManager> {
return dragLayer
@@ -56,8 +63,7 @@
override fun getDeviceProfile(): DeviceProfile {
if (deviceProfile == null) {
- deviceProfile = InvariantDeviceProfile.INSTANCE[this].getDeviceProfile(this)
- .copy(this)
+ deviceProfile = InvariantDeviceProfile.INSTANCE[this].getDeviceProfile(this).copy(this)
}
return deviceProfile!!
}
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt
index 9a38ff6..cda6c1b 100644
--- a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt
@@ -42,6 +42,7 @@
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
@@ -87,8 +88,10 @@
* To add new protologs, see [RecentsWindowProtoLogProxy]. To enable logging to logcat, see
* [QuickstepProtoLogGroup.Constants.DEBUG_RECENTS_WINDOW]
*/
-class RecentsWindowManager(context: Context) :
- RecentsWindowContext(context), RecentsViewContainer, StatefulContainer<RecentsState> {
+class RecentsWindowManager(context: Context, wallpaperColorHints: Int) :
+ RecentsWindowContext(context, wallpaperColorHints),
+ RecentsViewContainer,
+ StatefulContainer<RecentsState> {
companion object {
private const val HOME_APPEAR_DURATION: Long = 250
@@ -162,6 +165,7 @@
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(taskStackChangeListener)
callbacks?.removeListener(recentsAnimationListener)
recentsWindowTracker.onContextDestroyed(this)
+ recentsView?.destroy()
}
override fun startHome() {
@@ -350,17 +354,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 f3f73c0..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,6 +57,7 @@
private final int mTouchSlop;
private final PointF mDownPos = new PointF();
private final PointF mLastPos = new PointF();
+ private long mDownTime;
private final long mTimeForLongPress;
private int mActivePointerId = INVALID_POINTER_ID;
@@ -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,17 +122,28 @@
}
break;
case MotionEvent.ACTION_UP:
+ long tapTime = System.currentTimeMillis() - mDownTime;
boolean swipeUpOnBubbleHandle = mBubbleBarSwipeController != null
&& mBubbleBarSwipeController.isSwipeGesture();
// Anything less than a long-press is a tap
- boolean isWithinTapTime = ev.getEventTime() - ev.getDownTime() <= mTimeForLongPress;
+ 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();
@@ -126,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/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index 9bfe71f..b2a30ca 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -31,15 +31,15 @@
import static com.android.launcher3.logger.LauncherAtomExtensions.ExtendedContainers.ContainerCase.DEVICE_SEARCH_RESULT_CONTAINER;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORKSPACE_SNAPSHOT;
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DISPLAY_ROTATION__ROTATION_0;
-import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DISPLAY_ROTATION__ROTATION_90;
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DISPLAY_ROTATION__ROTATION_180;
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DISPLAY_ROTATION__ROTATION_270;
+import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DISPLAY_ROTATION__ROTATION_90;
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__ALLAPPS;
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__BACKGROUND;
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__HOME;
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__OVERVIEW;
-import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__RECENTS_ORIENTATION_HANDLER__PORTRAIT;
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__RECENTS_ORIENTATION_HANDLER__LANDSCAPE;
+import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__RECENTS_ORIENTATION_HANDLER__PORTRAIT;
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__RECENTS_ORIENTATION_HANDLER__SEASCAPE;
import android.content.Context;
@@ -69,6 +69,7 @@
import com.android.launcher3.logger.LauncherAtomExtensions.ExtendedContainers;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.model.data.CollectionInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.Executors;
@@ -386,7 +387,8 @@
// and then write to StatsLog.
app.getModel().enqueueModelUpdateTask((taskController, dataModel, apps) ->
write(event, applyOverwrites(mItemInfo.buildProto(
- dataModel.collections.get(mItemInfo.container), mContext))));
+ (CollectionInfo) dataModel.itemsIdMap.get(mItemInfo.container),
+ mContext))));
})) {
// Write log on the model thread so that logs do not go out of order
// (for eg: drop comes after drag)
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/TasksRepository.kt b/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
index 703d631..2f95413 100644
--- a/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
+++ b/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
@@ -79,6 +79,10 @@
}
}
tasks.value = MapForStateFlow(recentTasks)
+ Log.d(
+ TAG,
+ "getAllTaskData: oldTasks ${oldTaskMap.keys}, newTasks: ${recentTasks.keys}",
+ )
}
}
return tasks.map { it.values.toList() }
diff --git a/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt b/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
index dd83af6..358537c 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 -> {
@@ -251,6 +252,7 @@
fun initialize(view: View): RecentsDependencies = initialize(view.context)
fun initialize(context: Context): RecentsDependencies {
+ Log.d(TAG, "initializing")
synchronized(this) {
activeRecentsCount++
instance = RecentsDependencies(context.applicationContext)
@@ -285,10 +287,12 @@
activeRecentsCount--
if (activeRecentsCount == 0) {
instance.scopes.clear()
+ Log.d(TAG, "destroyed", Exception("Printing stack trace"))
} else {
- instance.log(
+ Log.d(
+ TAG,
"RecentsDependencies was not destroyed. " +
- "There is still an active RecentsView instance."
+ "There is still an active RecentsView instance.",
)
}
}
diff --git a/quickstep/src/com/android/quickstep/recents/domain/model/TaskModel.kt b/quickstep/src/com/android/quickstep/recents/domain/model/TaskModel.kt
new file mode 100644
index 0000000..3823100
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/recents/domain/model/TaskModel.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.recents.domain.model
+
+import android.graphics.drawable.Drawable
+import com.android.systemui.shared.recents.model.ThumbnailData
+
+/**
+ * Data class representing a task in the application.
+ *
+ * This class holds the essential information about a task, including its unique identifier, display
+ * title, associated icon, optional thumbnail data, and background color.
+ *
+ * @property id The unique identifier for this task. Must be an integer.
+ * @property title The display title of the task.
+ * @property titleDescription A content description of the task.
+ * @property icon An optional drawable resource representing an icon for the task. Can be null if no
+ * icon is required.
+ * @property thumbnail An optional [ThumbnailData] object containing thumbnail information. Can be
+ * null if no thumbnail is needed.
+ * @property backgroundColor The background color of the task, represented as an integer color
+ * value.
+ * @property isLocked Indicates whether the [Task] is locked.
+ */
+data class TaskModel(
+ val id: TaskId,
+ val title: String,
+ val titleDescription: String?,
+ val icon: Drawable?,
+ val thumbnail: ThumbnailData?,
+ val backgroundColor: Int,
+ val isLocked: Boolean,
+)
+
+typealias TaskId = Int
diff --git a/quickstep/src/com/android/quickstep/recents/domain/usecase/GetTaskUseCase.kt b/quickstep/src/com/android/quickstep/recents/domain/usecase/GetTaskUseCase.kt
new file mode 100644
index 0000000..a60144b
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/recents/domain/usecase/GetTaskUseCase.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.quickstep.recents.domain.usecase
+
+import com.android.quickstep.recents.data.RecentTasksRepository
+import com.android.quickstep.recents.domain.model.TaskModel
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+class GetTaskUseCase(private val repository: RecentTasksRepository) {
+ operator fun invoke(taskId: Int): Flow<TaskModel?> =
+ repository.getTaskDataById(taskId).map { task ->
+ if (task != null) {
+ TaskModel(
+ id = task.key.id,
+ title = task.title,
+ titleDescription = task.titleDescription,
+ icon = task.icon,
+ thumbnail = task.thumbnail,
+ backgroundColor = task.colorBackground,
+ isLocked = task.isLocked,
+ )
+ } else {
+ null
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/recents/ui/viewmodel/TaskTileUiState.kt b/quickstep/src/com/android/quickstep/recents/ui/viewmodel/TaskTileUiState.kt
new file mode 100644
index 0000000..5f98479
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/recents/ui/viewmodel/TaskTileUiState.kt
@@ -0,0 +1,57 @@
+/*
+ * 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.recents.ui.viewmodel
+
+import android.graphics.drawable.Drawable
+import com.android.systemui.shared.recents.model.ThumbnailData
+
+/**
+ * This class represents the UI state to be consumed by TaskView, GroupTaskView and DesktopTaskView.
+ * Data class representing the state of a list of tasks.
+ *
+ * This class encapsulates a list of [TaskTileUiState] objects, along with a flag indicating whether
+ * the data is being used for a live tile display.
+ *
+ * @property tasks The list of [TaskTileUiState] objects representing the individual tasks.
+ * @property isLiveTile Indicates whether this data is intended for a live tile. If `true`, the
+ * running app will be displayed instead of the thumbnail.
+ */
+data class TaskTileUiState(val tasks: List<TaskData>, val isLiveTile: Boolean)
+
+sealed interface TaskData {
+ /** When no data was found for the TaskId provided */
+ data class NoData(val taskId: Int) : TaskData
+
+ /**
+ * This class provides UI information related to a Task (App) to be displayed within a TaskView.
+ *
+ * @property taskId Identifier of the task
+ * @property title App title
+ * @property icon App icon
+ * @property thumbnailData Information related to the last snapshot retrieved from the app
+ * @property backgroundColor The background color of the task.
+ * @property isLocked Indicates whether the task is locked or not.
+ */
+ data class Data(
+ val taskId: Int,
+ val title: String,
+ val icon: Drawable?,
+ val thumbnailData: ThumbnailData?,
+ val backgroundColor: Int,
+ val isLocked: Boolean,
+ ) : TaskData
+}
diff --git a/quickstep/src/com/android/quickstep/recents/ui/viewmodel/TaskViewModel.kt b/quickstep/src/com/android/quickstep/recents/ui/viewmodel/TaskViewModel.kt
new file mode 100644
index 0000000..2e51a8a
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/recents/ui/viewmodel/TaskViewModel.kt
@@ -0,0 +1,91 @@
+/*
+ * 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.recents.ui.viewmodel
+
+import android.util.Log
+import com.android.launcher3.util.coroutines.DispatcherProvider
+import com.android.quickstep.recents.domain.model.TaskId
+import com.android.quickstep.recents.domain.model.TaskModel
+import com.android.quickstep.recents.domain.usecase.GetTaskUseCase
+import com.android.quickstep.recents.viewmodel.RecentsViewData
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+
+/**
+ * ViewModel used for [com.android.quickstep.views.TaskView],
+ * [com.android.quickstep.views.DesktopTaskView] and [com.android.quickstep.views.GroupedTaskView].
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+class TaskViewModel(
+ recentsViewData: RecentsViewData,
+ private val getTaskUseCase: GetTaskUseCase,
+ dispatcherProvider: DispatcherProvider,
+) {
+ private var taskIds = MutableStateFlow(emptySet<Int>())
+
+ private val isLiveTile =
+ combine(
+ taskIds,
+ recentsViewData.runningTaskIds,
+ recentsViewData.runningTaskShowScreenshot,
+ ) { taskIds, runningTaskIds, runningTaskShowScreenshot ->
+ runningTaskIds == taskIds && !runningTaskShowScreenshot
+ }
+ .distinctUntilChanged()
+
+ val state: Flow<TaskTileUiState> =
+ taskIds
+ .flatMapLatest { ids ->
+ // Combine Tasks requests
+ combine(
+ ids.map { id -> getTaskUseCase(id).map { taskModel -> id to taskModel } },
+ ::mapToUiState,
+ )
+ }
+ .combine(isLiveTile) { tasks, isLiveTile -> TaskTileUiState(tasks, isLiveTile) }
+ .flowOn(dispatcherProvider.background)
+
+ fun bind(vararg taskId: TaskId) {
+ Log.d(TAG, "bind: $taskId")
+ taskIds.value = taskId.toSet()
+ }
+
+ private fun mapToUiState(result: Array<Pair<TaskId, TaskModel?>>): List<TaskData> =
+ result.map { mapToUiState(it.first, it.second) }
+
+ private fun mapToUiState(taskId: TaskId, result: TaskModel?): TaskData =
+ result?.let {
+ TaskData.Data(
+ taskId = taskId,
+ title = result.title,
+ icon = result.icon,
+ thumbnailData = result.thumbnail,
+ backgroundColor = result.backgroundColor,
+ isLocked = result.isLocked,
+ )
+ } ?: TaskData.NoData(taskId)
+
+ private companion object {
+ const val TAG = "TaskViewModel"
+ }
+}
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 b040723..02baa39 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,6 +93,12 @@
defStyleAttr: Int,
) : super(context, attrs, defStyleAttr)
+ override fun onFinishInflate() {
+ super.onFinishInflate()
+
+ maybeCreateHeader()
+ }
+
override fun onAttachedToWindow() {
super.onAttachedToWindow()
viewAttachedScope =
@@ -96,22 +109,28 @@
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
@@ -127,8 +146,8 @@
}
}
- override fun onDetachedFromWindow() {
- super.onDetachedFromWindow()
+ // TODO(b/391842220): Cancel scope in onDetach instead of having a specific method for this.
+ fun destroyScopes() {
val scopeToCancel = viewAttachedScope
recentsCoroutineScope.launch(dispatcherProvider.background) {
scopeToCancel.cancel("TaskThumbnailView detaching from window")
@@ -137,6 +156,7 @@
override fun onRecycle() {
uiState = Uninitialized
+ resetViews()
}
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
@@ -179,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) {
@@ -197,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
@@ -210,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/util/TaskOverlayHelper.kt b/quickstep/src/com/android/quickstep/task/util/TaskOverlayHelper.kt
index 677875c..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
@@ -79,6 +81,8 @@
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/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/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/AppPairsController.java b/quickstep/src/com/android/quickstep/util/AppPairsController.java
index 8399792..6b8650f 100644
--- a/quickstep/src/com/android/quickstep/util/AppPairsController.java
+++ b/quickstep/src/com/android/quickstep/util/AppPairsController.java
@@ -46,7 +46,6 @@
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.allapps.AllAppsStore;
import com.android.launcher3.apppairs.AppPairIcon;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.StatsLogManager;
@@ -127,8 +126,7 @@
.anyMatch(att -> att != null && att.getItemInfo() != null
&& ((att.getItemInfo().runtimeStatusFlags
& ItemInfoWithIcon.FLAG_NOT_PINNABLE) != 0));
- if (!FeatureFlags.enableAppPairs()
- || !taskView.containsMultipleTasks()
+ if (!taskView.containsMultipleTasks()
|| hasUnpinnableApp
|| !(taskView instanceof GroupedTaskView)) {
return false;
diff --git a/quickstep/src/com/android/quickstep/util/ContextualSearchStateManager.java b/quickstep/src/com/android/quickstep/util/ContextualSearchStateManager.java
index f75d3b3..ed96399 100644
--- a/quickstep/src/com/android/quickstep/util/ContextualSearchStateManager.java
+++ b/quickstep/src/com/android/quickstep/util/ContextualSearchStateManager.java
@@ -21,7 +21,6 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_LAUNCH_OMNI_SUCCESSFUL_SYSTEM_ACTION;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
import static com.android.quickstep.util.SystemActionConstants.SYSTEM_ACTION_ID_SEARCH_SCREEN;
import android.app.PendingIntent;
@@ -44,11 +43,13 @@
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.R;
+import com.android.launcher3.dagger.ApplicationContext;
+import com.android.launcher3.dagger.LauncherAppComponent;
+import com.android.launcher3.dagger.LauncherAppSingleton;
import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.util.DaggerSingletonObject;
+import com.android.launcher3.util.DaggerSingletonTracker;
import com.android.launcher3.util.EventLogArray;
-import com.android.launcher3.util.MainThreadInitializedObject;
-import com.android.launcher3.util.ResourceBasedOverride;
-import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.util.SettingsCache;
import com.android.launcher3.util.SimpleBroadcastReceiver;
import com.android.quickstep.DeviceConfigWrapper;
@@ -58,12 +59,14 @@
import java.io.PrintWriter;
import java.util.Optional;
-/** Long-lived class to manage Contextual Search states like the user setting and availability. */
-public class ContextualSearchStateManager implements ResourceBasedOverride, SafeCloseable {
+import javax.inject.Inject;
- public static final MainThreadInitializedObject<ContextualSearchStateManager> INSTANCE =
- forOverride(ContextualSearchStateManager.class,
- R.string.contextual_search_state_manager_class);
+/** Long-lived class to manage Contextual Search states like the user setting and availability. */
+@LauncherAppSingleton
+public class ContextualSearchStateManager {
+
+ public static final DaggerSingletonObject<ContextualSearchStateManager> INSTANCE =
+ new DaggerSingletonObject<>(LauncherAppComponent::getContextualSearchStateManager);
private static final String TAG = "ContextualSearchStMgr";
private static final int MAX_DEBUG_EVENT_SIZE = 20;
@@ -73,23 +76,29 @@
private final Runnable mSysUiStateChangeListener = this::updateOverridesToSysUi;
private final SimpleBroadcastReceiver mContextualSearchPackageReceiver =
new SimpleBroadcastReceiver(UI_HELPER_EXECUTOR, (unused) -> requestUpdateProperties());
- private final SettingsCache.OnChangeListener mContextualSearchSettingChangedListener =
- this::onContextualSearchSettingChanged;
protected final EventLogArray mEventLogArray = new EventLogArray(TAG, MAX_DEBUG_EVENT_SIZE);
// Cached value whether the ContextualSearch intent filter matched any enabled components.
private boolean mIsContextualSearchIntentAvailable;
private boolean mIsContextualSearchSettingEnabled;
- protected Context mContext;
- protected String mContextualSearchPackage;
+ protected final Context mContext;
+ protected final String mContextualSearchPackage;
+ protected final SystemUiProxy mSystemUiProxy;
+ protected final TopTaskTracker mTopTaskTracker;
- public ContextualSearchStateManager() {}
-
- public ContextualSearchStateManager(Context context) {
+ @Inject
+ public ContextualSearchStateManager(
+ @ApplicationContext Context context,
+ SettingsCache settingsCache,
+ SystemUiProxy systemUiProxy,
+ TopTaskTracker topTaskTracker,
+ DaggerSingletonTracker lifeCycle) {
mContext = context;
mContextualSearchPackage = mContext.getResources().getString(
com.android.internal.R.string.config_defaultContextualSearchPackageName);
+ mSystemUiProxy = systemUiProxy;
+ mTopTaskTracker = topTaskTracker;
if (areAllContextualSearchFlagsDisabled()
|| !context.getPackageManager().hasSystemFeature(FEATURE_CONTEXTUAL_SEARCH)) {
@@ -106,11 +115,20 @@
context, mContextualSearchPackage, Intent.ACTION_PACKAGE_ADDED,
Intent.ACTION_PACKAGE_CHANGED, Intent.ACTION_PACKAGE_REMOVED);
- SettingsCache.INSTANCE.get(context).register(SEARCH_ALL_ENTRYPOINTS_ENABLED_URI,
- mContextualSearchSettingChangedListener);
- onContextualSearchSettingChanged(
- SettingsCache.INSTANCE.get(context).getValue(SEARCH_ALL_ENTRYPOINTS_ENABLED_URI));
- SystemUiProxy.INSTANCE.get(mContext).addOnStateChangeListener(mSysUiStateChangeListener);
+ SettingsCache.OnChangeListener settingChangedListener =
+ isEnabled -> mIsContextualSearchSettingEnabled = isEnabled;
+ settingsCache.register(SEARCH_ALL_ENTRYPOINTS_ENABLED_URI, settingChangedListener);
+ mIsContextualSearchSettingEnabled =
+ settingsCache.getValue(SEARCH_ALL_ENTRYPOINTS_ENABLED_URI);
+
+ systemUiProxy.addOnStateChangeListener(mSysUiStateChangeListener);
+
+ lifeCycle.addCloseable(() -> {
+ mContextualSearchPackageReceiver.unregisterReceiverSafely(mContext);
+ unregisterSearchScreenSystemAction();
+ settingsCache.unregister(SEARCH_ALL_ENTRYPOINTS_ENABLED_URI, settingChangedListener);
+ systemUiProxy.removeOnStateChangeListener(mSysUiStateChangeListener);
+ });
}
/** Return {@code true} if the Settings toggle is enabled. */
@@ -118,10 +136,6 @@
return mIsContextualSearchSettingEnabled;
}
- private void onContextualSearchSettingChanged(boolean isEnabled) {
- mIsContextualSearchSettingEnabled = isEnabled;
- }
-
/** Whether search supports showing on the lockscreen. */
protected boolean supportsShowWhenLocked() {
return false;
@@ -208,7 +222,7 @@
protected final void updateOverridesToSysUi() {
// LPH commit haptic is always enabled
- SystemUiProxy.INSTANCE.get(mContext).setOverrideHomeButtonLongPress(
+ mSystemUiProxy.setOverrideHomeButtonLongPress(
getLPHDurationMillis().orElse(0L), getLPHCustomSlopMultiplier().orElse(0f), true);
Log.i(TAG, "Sent LPH override to sysui: " + getLPHDurationMillis().orElse(0L) + ";"
+ getLPHCustomSlopMultiplier().orElse(0f));
@@ -227,10 +241,8 @@
new ContextualSearchInvoker(mContext).show(
ENTRYPOINT_SYSTEM_ACTION);
if (contextualSearchInvoked) {
- String runningPackage =
- TopTaskTracker.INSTANCE.get(mContext).getCachedTopTask(
- /* filterOnlyVisibleRecents */
- true).getPackageName();
+ String runningPackage = mTopTaskTracker.getCachedTopTask(
+ /* filterOnlyVisibleRecents */ true).getPackageName();
StatsLogManager.newInstance(mContext).logger()
.withPackageName(runningPackage)
.log(LAUNCHER_LAUNCH_OMNI_SUCCESSFUL_SYSTEM_ACTION);
@@ -259,15 +271,6 @@
}
}
- @Override
- public void close() {
- mContextualSearchPackageReceiver.unregisterReceiverSafely(mContext);
- unregisterSearchScreenSystemAction();
- SettingsCache.INSTANCE.get(mContext).unregister(SEARCH_ALL_ENTRYPOINTS_ENABLED_URI,
- mContextualSearchSettingChangedListener);
- SystemUiProxy.INSTANCE.get(mContext).removeOnStateChangeListener(mSysUiStateChangeListener);
- }
-
protected final void addEventLog(String event) {
synchronized (mEventLogArray) {
mEventLogArray.addLog(event);
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..5463cf7
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/DesktopTask.kt
@@ -0,0 +1,43 @@
+/*
+ * 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
+
+/**
+ * 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
+ return super.equals(o)
+ }
+}
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..d5bbcd3
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/GroupTask.kt
@@ -0,0 +1,111 @@
+/*
+ * 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
+
+/**
+ * An abstract class for creating [Task] containers that can be [SingleTask]s, [SplitTask]s, or
+ * [DesktopTask]s in the recent tasks list.
+ */
+abstract 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,
+) {
+ protected constructor(
+ task1: Task,
+ task2: Task?,
+ splitBounds: SplitConfigurationOptions.SplitBounds?,
+ ) : this(
+ task1,
+ task2,
+ splitBounds,
+ if (task2 != 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 */
+ abstract fun copy(): GroupTask
+
+ 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 && tasks == o.tasks
+ }
+
+ override fun hashCode() = Objects.hash(tasks, taskViewType)
+}
+
+/** A [Task] container that must contain exactly one task in the recent tasks list. */
+class SingleTask(task: Task) :
+ GroupTask(task, task2 = null, mSplitBounds = null, TaskViewType.SINGLE) {
+ override fun copy() = SingleTask(task1)
+
+ override fun toString() = "type=$taskViewType task=$task1"
+
+ override fun equals(o: Any?): Boolean {
+ if (this === o) return true
+ if (o !is SingleTask) return false
+ return super.equals(o)
+ }
+}
+
+/**
+ * A [Task] container that must contain exactly two tasks and split bounds to represent an app-pair
+ * in the recent tasks list.
+ */
+class SplitTask(task1: Task, task2: Task, splitBounds: SplitConfigurationOptions.SplitBounds) :
+ GroupTask(task1, task2, splitBounds, TaskViewType.GROUPED) {
+
+ override fun copy() = SplitTask(task1, task2!!, mSplitBounds!!)
+
+ override fun toString() = "type=$taskViewType task1=$task1 task2=$task2"
+
+ override fun equals(o: Any?): Boolean {
+ if (this === o) return true
+ if (o !is SplitTask) return false
+ if (mSplitBounds!! != o.mSplitBounds!!) return false
+ return super.equals(o)
+ }
+
+ override fun hashCode() = Objects.hash(super.hashCode(), mSplitBounds)
+}
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/SplitScreenUtils.kt b/quickstep/src/com/android/quickstep/util/SplitScreenUtils.kt
index d982e81..4005c5a 100644
--- a/quickstep/src/com/android/quickstep/util/SplitScreenUtils.kt
+++ b/quickstep/src/com/android/quickstep/util/SplitScreenUtils.kt
@@ -33,38 +33,34 @@
// TODO(b/254378592): Remove these methods when the two classes are reunited
/** Converts the shell version of SplitBounds to the launcher version */
@JvmStatic
- fun convertShellSplitBoundsToLauncher(
- shellSplitBounds: SplitBounds?
- ): SplitConfigurationOptions.SplitBounds? {
- return if (shellSplitBounds == null) {
- null
- } else {
- SplitConfigurationOptions.SplitBounds(
- shellSplitBounds.leftTopBounds,
- shellSplitBounds.rightBottomBounds,
- shellSplitBounds.leftTopTaskId,
- shellSplitBounds.rightBottomTaskId,
- shellSplitBounds.snapPosition
- )
- }
- }
+ fun convertShellSplitBoundsToLauncher(shellSplitBounds: SplitBounds) =
+ SplitConfigurationOptions.SplitBounds(
+ shellSplitBounds.leftTopBounds,
+ shellSplitBounds.rightBottomBounds,
+ shellSplitBounds.leftTopTaskId,
+ shellSplitBounds.rightBottomTaskId,
+ shellSplitBounds.snapPosition,
+ )
/**
* Given a TransitionInfo, generates the tree structure for those changes and extracts out
- * the top most root and it's two immediate children.
- * Changes can be provided in any order.
+ * the top most root and it's two immediate children. Changes can be provided in any order.
*
- * @return a [Pair] where first -> top most split root,
- * second -> [List] of 2, leftTop/bottomRight stage roots
+ * @return a [Pair] where first -> top most split root, second -> [List] of 2,
+ * leftTop/bottomRight stage roots
*/
- fun extractTopParentAndChildren(transitionInfo: TransitionInfo):
- Pair<Change, List<Change>>? {
+ fun extractTopParentAndChildren(
+ transitionInfo: TransitionInfo
+ ): Pair<Change, List<Change>>? {
val parentToChildren = mutableMapOf<Change, MutableList<Change>>()
val hasParent = mutableSetOf<Change>()
// filter out anything that isn't opening and the divider
- val taskChanges: List<Change> = transitionInfo.changes
- .filter { change -> (change.mode == TRANSIT_OPEN ||
- change.mode == TRANSIT_TO_FRONT) && change.flags < FLAG_FIRST_CUSTOM}
+ val taskChanges: List<Change> =
+ transitionInfo.changes
+ .filter { change ->
+ (change.mode == TRANSIT_OPEN || change.mode == TRANSIT_TO_FRONT) &&
+ change.flags < FLAG_FIRST_CUSTOM
+ }
.toList()
// 1. Build Parent-Child Relationships
@@ -73,8 +69,8 @@
// startAnimation() and we can know the precise taskIds of launching tasks.
change.parent?.let { parent ->
parentToChildren
- .getOrPut(transitionInfo.getChange(parent)!!) { mutableListOf() }
- .add(change)
+ .getOrPut(transitionInfo.getChange(parent)!!) { mutableListOf() }
+ .add(change)
hasParent.add(change)
}
}
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/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/AddDesktopButton.kt b/quickstep/src/com/android/quickstep/views/AddDesktopButton.kt
index f973dd0..1dab18a 100644
--- a/quickstep/src/com/android/quickstep/views/AddDesktopButton.kt
+++ b/quickstep/src/com/android/quickstep/views/AddDesktopButton.kt
@@ -17,8 +17,11 @@
package com.android.quickstep.views
import android.content.Context
+import android.graphics.drawable.ShapeDrawable
+import android.graphics.drawable.shapes.RoundRectShape
import android.util.AttributeSet
import android.widget.ImageButton
+import com.android.launcher3.R
/**
* Button for supporting multiple desktop sessions. The button will be next to the first TaskView
@@ -26,5 +29,21 @@
*/
class AddDesktopButton @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
ImageButton(context, attrs) {
- // TODO(b/382057498): add this button the overview.
+
+ override fun onFinishInflate() {
+ super.onFinishInflate()
+
+ background =
+ ShapeDrawable().apply {
+ shape =
+ RoundRectShape(
+ FloatArray(8) { R.dimen.add_desktop_button_size.toFloat() },
+ null,
+ null,
+ )
+ setTint(
+ resources.getColor(android.R.color.system_surface_bright_light, context.theme)
+ )
+ }
+ }
}
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/GroupedTaskView.kt b/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
index 3f0b520..38ffe50 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
@@ -27,7 +27,6 @@
import com.android.launcher3.Flags.enableRefactorTaskThumbnail
import com.android.launcher3.R
import com.android.launcher3.Utilities
-import com.android.launcher3.config.FeatureFlags
import com.android.launcher3.util.RunnableList
import com.android.launcher3.util.SplitConfigurationOptions
import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT
@@ -340,14 +339,6 @@
return Utilities.pointInView(this, localPos[0], localPos[1], 0f /* slop */)
}
- override fun setOverlayEnabled(overlayEnabled: Boolean) {
- if (FeatureFlags.enableAppPairs()) {
- super.setOverlayEnabled(overlayEnabled)
- } else {
- // Intentional no-op to prevent setting smart actions overlay on thumbnails
- }
- }
-
companion object {
private const val TAG = "GroupedTaskView"
}
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index 7a7a7f9..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;
@@ -256,17 +255,12 @@
@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);
+ public void onGestureAnimationStart(Task[] runningTasks) {
+ super.onGestureAnimationStart(runningTasks);
if (!ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()) {
// TODO: b/333533253 - Remove after flag rollout
DesktopVisibilityController.INSTANCE.get(mContainer).setRecentsGestureStart();
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index ab96474..c0b026b 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -74,7 +74,6 @@
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NON_ZERO_ROTATION;
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NO_RECENTS;
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NO_TASKS;
-import static com.android.quickstep.views.OverviewActionsView.HIDDEN_SPLIT_SCREEN;
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_SPLIT_SELECT_ACTIVE;
import android.animation.Animator;
@@ -209,7 +208,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 +246,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;
@@ -522,7 +519,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();
@@ -550,8 +546,12 @@
private final int mSplitPlaceholderSize;
private final int mSplitPlaceholderInset;
private final ClearAllButton mClearAllButton;
+ @Nullable
+ private AddDesktopButton mAddDesktopButton = null;
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.
@@ -903,6 +903,12 @@
mClearAllButton = (ClearAllButton) LayoutInflater.from(context)
.inflate(R.layout.overview_clear_all_button, this, false);
mClearAllButton.setOnClickListener(this::dismissAllTasks);
+
+ if (DesktopModeStatus.enableMultipleDesktops(mContext)) {
+ mAddDesktopButton = (AddDesktopButton) LayoutInflater.from(context).inflate(
+ R.layout.overview_add_desktop_button, this, false);
+ }
+
mTaskViewPool = new ViewPool<>(context, this, R.layout.task, 20 /* max size */,
10 /* initial size */);
mGroupedTaskViewPool = new ViewPool<>(context, this,
@@ -928,8 +934,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 +1224,7 @@
mIPipAnimationListener);
mOrientationState.initListeners();
mTaskOverlayFactory.initListeners();
- if (FeatureFlags.enableSplitContextually()) {
- mSplitSelectStateController.registerSplitListener(mSplitSelectionListener);
- }
+ mSplitSelectStateController.registerSplitListener(mSplitSelectionListener);
}
@Override
@@ -1237,12 +1244,24 @@
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();
+ try {
+ mTaskViewPool.killOngoingInitializations();
+ mGroupedTaskViewPool.killOngoingInitializations();
+ mDesktopTaskViewPool.killOngoingInitializations();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Ongoing initializations could not be killed", e);
+ }
+ mHelper.onDestroy();
RecentsDependencies.destroy();
}
}
@@ -1713,8 +1732,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;
}
}
@@ -1826,20 +1848,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;
}
}
}
@@ -1869,7 +1894,7 @@
}
mLoadPlanEverApplied = true;
if (taskGroups == null || taskGroups.isEmpty()) {
- removeTasksViewsAndClearAllButton();
+ removeAllTaskViews();
onTaskStackUpdated();
// With all tasks removed, touch handling in PagedView is disabled and we need to reset
// touch state or otherwise values will be obsolete.
@@ -1932,6 +1957,11 @@
taskGroups = mUtils.sortDesktopTasksToFront(taskGroups);
}
+ if (mAddDesktopButton != null) {
+ // Add `mAddDesktopButton` as the first child.
+ addView(mAddDesktopButton);
+ }
+
// Add views as children based on whether it's grouped or single task. Looping through
// taskGroups backwards populates the thumbnail grid from least recent to most recent.
for (int i = taskGroups.size() - 1; i >= 0; i--) {
@@ -1962,7 +1992,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,
@@ -1980,9 +2010,7 @@
}
}
- if (!taskGroups.isEmpty()) {
- addView(mClearAllButton);
- }
+ addView(mClearAllButton);
// Keep same previous focused task
TaskView newFocusedTaskView = null;
@@ -2079,20 +2107,21 @@
return mModel.isLoadingTasksInBackground();
}
- private void removeTasksViewsAndClearAllButton() {
+ private void removeAllTaskViews() {
// This handles an edge case where applyLoadPlan happens during a gesture when the only
// Task is one with excludeFromRecents, in which case we should not remove it.
CollectionsKt
.filter(getTaskViews(), taskView -> !isGestureActive() || !taskView.isRunningTask())
.forEach(this::removeView);
- if (!hasTaskViews() && indexOfChild(mClearAllButton) != -1) {
+ if (!hasTaskViews()) {
+ removeView(mAddDesktopButton);
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() {
@@ -2283,11 +2312,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
@@ -2307,17 +2331,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());
@@ -2326,6 +2342,12 @@
mClearAllButton.setFullscreenTranslationPrimary(accumulatedTranslationX);
+ float taskAlignmentTranslationY = getTaskAlignmentTranslationY();
+ mClearAllButton.setTaskAlignmentTranslationY(taskAlignmentTranslationY);
+ if (mAddDesktopButton != null) {
+ mAddDesktopButton.setTranslationY(taskAlignmentTranslationY);
+ }
+
updateGridProperties();
}
@@ -2346,17 +2368,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
@@ -2373,10 +2410,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,
@@ -2632,7 +2665,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".
@@ -2654,9 +2686,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,16 +2693,17 @@
}
private void onReset() {
- if (enableRefactorTaskThumbnail()) {
- mRecentsViewModel.onReset();
- removeAllViews();
- }
unloadVisibleTaskData(TaskView.FLAG_UPDATE_ALL);
setCurrentPage(0);
LayoutUtils.setViewEnabled(mActionsView, true);
if (mOrientationState.setGestureActive(false)) {
updateOrientationHandler(/* forceRecreateDragLayerControllers = */ false);
}
+ if (enableRefactorTaskThumbnail()) {
+ mRecentsViewModel.onReset();
+ // TODO(b/391842220) Remove TaskViews rather than calling specific logic to cancel scope
+ getTaskViews().forEach(TaskView::destroyScopes);
+ }
}
public int getRunningTaskViewId() {
@@ -2703,7 +2733,7 @@
}
@Nullable
- private TaskView getTaskViewFromTaskViewId(int taskViewId) {
+ TaskView getTaskViewFromTaskViewId(int taskViewId) {
if (taskViewId == -1) {
return null;
}
@@ -2781,14 +2811,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();
@@ -2905,8 +2933,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(
@@ -3004,6 +3030,9 @@
taskView = getTaskViewFromPool(TaskViewType.SINGLE);
taskView.bind(runningTasks[0], mOrientationState, mTaskOverlayFactory);
}
+ if (mAddDesktopButton != null && wasEmpty) {
+ addView(mAddDesktopButton);
+ }
addView(taskView, getRunningTaskExpectedIndex(taskView));
runningTaskViewId = taskView.getTaskViewId();
if (wasEmpty) {
@@ -3202,7 +3231,7 @@
Map<TaskView, Float> gridTranslations = new HashMap<>();
TaskView lastLargeTaskView = mUtils.getLastLargeTaskView();
- int focusedTaskShift = 0;
+ int focusedTaskViewShift = 0;
int largeTaskWidthAndSpacing = 0;
int snappedTaskRowWidth = 0;
int snappedPage = isKeyboardTaskFocusPending() ? mKeyboardTaskFocusIndex : getNextPage();
@@ -3247,7 +3276,7 @@
topRowWidth += taskWidthAndSpacing;
bottomRowWidth += taskWidthAndSpacing;
}
- gridTranslation += focusedTaskShift;
+ gridTranslation += focusedTaskViewShift;
gridTranslation += mIsRtl ? taskWidthAndSpacing : -taskWidthAndSpacing;
// Center view vertically in case it's from different orientation.
@@ -3266,9 +3295,12 @@
gridTranslation +=
mIsRtl ? largeTaskWidthAndSpacing : -largeTaskWidthAndSpacing;
} else {
- // For task before the focused task, accumulate the width and spacing to
- // calculate the distance focused task need to shift.
- focusedTaskShift += mIsRtl ? taskWidthAndSpacing : -taskWidthAndSpacing;
+ // For TaskViews before the new focused TaskView, accumulate the width and
+ // spacing to calculate the distance the new focused TaskView needs to shift.
+ // This could happen for example after multiple times of dismissing the
+ // focused TaskView, the triggered rebalance might set a non-first TaskView
+ // inside `mChildren` as the new focused TaskView.
+ focusedTaskViewShift += mIsRtl ? taskWidthAndSpacing : -taskWidthAndSpacing;
}
int taskViewId = taskView.getTaskViewId();
@@ -3339,7 +3371,7 @@
if (snappedTaskView != null) {
snappedTaskNonGridScrollAdjustment = snappedTaskView.getScrollAdjustment(
/*gridEnabled=*/false);
- snappedTaskGridTranslationX = gridTranslations.get(snappedTaskView);
+ snappedTaskGridTranslationX = gridTranslations.getOrDefault(snappedTaskView, 0f);
}
// Use the accumulated translation of the row containing the last task.
@@ -3420,10 +3452,24 @@
for (TaskView taskView : getTaskViews()) {
taskView.setGridTranslationX(
- gridTranslations.get(taskView) - snappedTaskGridTranslationX
+ gridTranslations.getOrDefault(taskView, 0f) - snappedTaskGridTranslationX
+ snappedTaskNonGridScrollAdjustment);
}
+ if (mAddDesktopButton != null) {
+ TaskView firstTaskView = getFirstTaskView();
+ float translationX = 0f;
+ if (firstTaskView != null) {
+ translationX += firstTaskView.getGridTranslationX();
+ }
+ if (focusedTaskViewShift != 0) {
+ // If the focused task is inserted between `firstTaskView` and
+ // `mAddDesktopButton`, shift `mAddDesktopButton` to accommodate.
+ translationX += largeTaskWidthAndSpacing;
+ }
+ mAddDesktopButton.setTranslationX(translationX);
+ }
+
final TaskView runningTask = getRunningTaskView();
if (showAsGrid() && enableGridOnlyOverview() && runningTask != null) {
runActionOnRemoteHandles(
@@ -3633,11 +3679,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);
}
@@ -4018,6 +4060,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;
@@ -4050,8 +4096,6 @@
} else {
removeTaskInternal(dismissedTaskView);
}
- announceForAccessibility(
- getResources().getString(R.string.task_view_closed));
mContainer.getStatsLogManager().logger()
.withItemInfo(dismissedTaskView.getFirstItemInfo())
.log(LAUNCHER_TASK_DISMISS_SWIPE_UP);
@@ -4069,7 +4113,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
@@ -4087,7 +4131,7 @@
} else {
// Won't focus next task in split select, so snap to the
// first task.
- pageToSnapTo = 0;
+ pageToSnapTo = indexOfChild(getFirstTaskView());
calculateScrollDiff = false;
}
} else {
@@ -4137,6 +4181,7 @@
if (taskCount == 1) {
removeViewInLayout(mClearAllButton);
+ removeViewInLayout(mAddDesktopButton);
if (isHomeTaskDismissed) {
updateEmptyMessage();
} else if (!mSplitSelectStateController.isSplitSelectActive()) {
@@ -4158,16 +4203,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
@@ -4180,7 +4224,7 @@
RecentsView.this);
int taskSize = (int) (
getPagedOrientationHandler().getMeasuredSize(
- taskView) * taskView
+ highestVisibleTaskView) * highestVisibleTaskView
.getSizeAdjustment(/*fullscreenEnabled=*/
false));
int taskEnd = taskStart + taskSize;
@@ -4189,7 +4233,7 @@
}
if (shouldRebalance) {
- updateGridProperties(taskView);
+ updateGridProperties(highestVisibleTaskView);
updateScrollSynchronously();
}
}
@@ -4236,6 +4280,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);
+ }
}
});
}
@@ -4339,9 +4389,6 @@
boolean isCurrentSplit = taskView instanceof GroupedTaskView;
GroupedTaskView groupedTaskView = isCurrentSplit ? (GroupedTaskView) taskView : null;
// Update flags to see if entire actions bar should be hidden.
- if (!FeatureFlags.enableAppPairs()) {
- mActionsView.updateHiddenFlags(HIDDEN_SPLIT_SCREEN, isCurrentSplit);
- }
mActionsView.updateHiddenFlags(HIDDEN_SPLIT_SELECT_ACTIVE, isSplitSelectionActive());
// Update flags to see if actions bar should show buttons for a single task or a pair of
// tasks.
@@ -4361,7 +4408,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);
}
@@ -4378,7 +4425,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);
@@ -4397,12 +4444,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());
@@ -4412,13 +4459,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) {
@@ -4462,7 +4510,7 @@
finishRecentsAnimation(true /* toRecents */, false /* shouldPip */, () -> {
UI_HELPER_EXECUTOR.getHandler().post(
ActivityManagerWrapper.getInstance()::removeAllRecentTasks);
- removeTasksViewsAndClearAllButton();
+ removeAllTaskViews();
startHome();
});
}
@@ -4621,6 +4669,11 @@
taskView.setStableAlpha(alpha);
}
mClearAllButton.setContentAlpha(mContentAlpha);
+
+ // TODO(b/389209338): Handle the visibility of the `mAddDesktopButton`.
+ if (mAddDesktopButton != null) {
+ mAddDesktopButton.setAlpha(mContentAlpha);
+ }
int alphaInt = Math.round(alpha * 255);
mEmptyMessagePaint.setAlpha(alphaInt);
mEmptyIcon.setAlpha(alphaInt);
@@ -4676,6 +4729,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();
@@ -4734,14 +4793,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() {
@@ -4806,14 +4857,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);
}
@@ -4924,10 +4968,14 @@
&& 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 if (child instanceof ClearAllButton) {
+ getPagedOrientationHandler().getPrimaryViewTranslate().set(child,
+ totalTranslationX);
+ } else {
+ // TODO(b/389209581): Handle the page offsets update of the 'mAddDesktopButton'.
+ }
if (mEnableDrawingLiveTile && i == getRunningTaskIndex()) {
runActionOnRemoteHandles(
remoteTargetHandle -> remoteTargetHandle.getTaskViewSimulator()
@@ -4935,11 +4983,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();
@@ -5055,7 +5103,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.
@@ -5063,11 +5111,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;
@@ -5365,11 +5412,7 @@
mSplitSelectStateController.launchSplitTasks(
aBoolean1 -> {
InteractionJankMonitorWrapper.end(Cuj.CUJ_SPLIT_SCREEN_ENTER);
- if (FeatureFlags.enableSplitContextually()) {
- mSplitSelectStateController.resetState();
- } else {
- resetFromSplitSelectionState();
- }
+ mSplitSelectStateController.resetState();
});
});
@@ -5391,17 +5434,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);
@@ -5413,11 +5453,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;
}
@@ -5516,15 +5551,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) {
@@ -5664,19 +5692,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);
}
@@ -5867,7 +5886,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);
@@ -6133,6 +6153,13 @@
outPageScrolls[clearAllIndex] = clearAllScroll;
}
+ int addDesktopButtonIndex = indexOfChild(mAddDesktopButton);
+ if (addDesktopButtonIndex != -1 && addDesktopButtonIndex < outPageScrolls.length) {
+ outPageScrolls[addDesktopButtonIndex] =
+ newPageScrolls[addDesktopButtonIndex] + Math.round(
+ mAddDesktopButton.getTranslationX());
+ }
+
int lastTaskScroll = getLastTaskScroll(clearAllScroll, clearAllWidth);
getTaskViews().forEachWithIndexInParent((index, taskView) -> {
float scrollDiff = taskView.getScrollAdjustment(showAsGrid);
@@ -6813,8 +6840,8 @@
return;
}
- mDesktopRecentsTransitionController.moveToDesktop(taskContainer, transitionSource);
- successCallback.run();
+ mDesktopRecentsTransitionController.moveToDesktop(taskContainer, transitionSource,
+ successCallback);
}
/**
@@ -6855,6 +6882,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/RecentsViewModelHelper.kt b/quickstep/src/com/android/quickstep/views/RecentsViewModelHelper.kt
index d92c4d0..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(
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/TaskContainer.kt b/quickstep/src/com/android/quickstep/views/TaskContainer.kt
index 5de8d1c..7ac0946 100644
--- a/quickstep/src/com/android/quickstep/views/TaskContainer.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskContainer.kt
@@ -141,6 +141,11 @@
}
}
+ // TODO(b/391842220): Cancel scope in onDetach instead of having a specific method for this.
+ fun destroyScopes() {
+ thumbnailView.destroyScopes()
+ }
+
fun bindThumbnailView() {
taskThumbnailViewModel.bind(task.key.id)
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailViewDeprecated.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailViewDeprecated.java
index 9f2bb9a..5ee5e10 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailViewDeprecated.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailViewDeprecated.java
@@ -46,7 +46,6 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Utilities;
-import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.SystemUiController.SystemUiControllerFlags;
import com.android.launcher3.util.ViewPool;
@@ -66,8 +65,6 @@
*/
@Deprecated
public class TaskThumbnailViewDeprecated extends View implements ViewPool.Reusable {
- private static final MainThreadInitializedObject<FullscreenDrawParams> TEMP_PARAMS =
- new MainThreadInitializedObject<>(FullscreenDrawParams::new);
public static final Property<TaskThumbnailViewDeprecated, Float> DIM_ALPHA =
new FloatProperty<TaskThumbnailViewDeprecated>("dimAlpha") {
@@ -145,8 +142,7 @@
mClearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
mContainer = RecentsViewContainer.containerFromContext(context);
// Initialize with placeholder value. It is overridden later by TaskView
- mFullscreenParams = TEMP_PARAMS.get(context);
-
+ mFullscreenParams = new FullscreenDrawParams(context, __ -> 0f, __ -> 0f);
mDimColor = RecentsView.getForegroundScrimDimColor(context);
mDimmingPaintAfterClearing.setColor(mDimColor);
}
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..e7a395f 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) {
@@ -622,9 +613,15 @@
borderEnabled = false
hoverBorderVisible = false
taskViewId = UNBOUND_TASK_VIEW_ID
+ // TODO(b/390583187): Clean the components UI State when TaskView is recycled.
taskContainers.forEach { it.destroy() }
}
+ fun destroyScopes() {
+ // TODO(b/391842220): Cancel scope in onDetach instead of having a specific method for this.
+ taskContainers.forEach { it.destroyScopes() }
+ }
+
// TODO: Clip-out the icon region from the thumbnail, since they are overlapping.
override fun hasOverlappingRendering() = false
@@ -633,7 +630,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 +798,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 +826,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 +1579,6 @@
gridTranslationX = 0f
gridTranslationY = 0f
boxTranslationY = 0f
- nonGridPivotTranslationX = 0f
taskContainers.forEach {
it.snapshotView.translationX = 0f
it.snapshotView.translationY = 0f
diff --git a/quickstep/src_protolog/com/android/quickstep/util/ActiveGestureProtoLogProxy.java b/quickstep/src_protolog/com/android/quickstep/util/ActiveGestureProtoLogProxy.java
index be1a4e8..37c64cf 100644
--- a/quickstep/src_protolog/com/android/quickstep/util/ActiveGestureProtoLogProxy.java
+++ b/quickstep/src_protolog/com/android/quickstep/util/ActiveGestureProtoLogProxy.java
@@ -395,11 +395,11 @@
public static void logOnRecentsAnimationStart(int appCount) {
ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
- "RecentsAnimationCallbacks.onAnimationStart (canceled): %d", appCount),
+ "RecentsAnimationCallbacks.onAnimationStart: %d", appCount),
/* gestureEvent= */ ON_START_RECENTS_ANIMATION);
if (!enableActiveGestureProtoLog() || !isProtoLogInitialized()) return;
ProtoLog.d(ACTIVE_GESTURE_LOG,
- "RecentsAnimationCallbacks.onAnimationStart (canceled): %d", appCount);
+ "RecentsAnimationCallbacks.onAnimationStart: %d", appCount);
}
public static void logStartRecentsAnimationCallback(@NonNull String callback) {
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/src/com/android/launcher3/taskbar/FallbackTaskbarUIControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/FallbackTaskbarUIControllerTest.kt
similarity index 98%
rename from quickstep/tests/src/com/android/launcher3/taskbar/FallbackTaskbarUIControllerTest.kt
rename to quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/FallbackTaskbarUIControllerTest.kt
index df98606..b39c3f1 100644
--- a/quickstep/tests/src/com/android/launcher3/taskbar/FallbackTaskbarUIControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/FallbackTaskbarUIControllerTest.kt
@@ -17,7 +17,7 @@
package com.android.launcher3.taskbar
-import androidx.test.runner.AndroidJUnit4
+import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.launcher3.statemanager.StateManager
import com.android.quickstep.RecentsActivity
import com.android.quickstep.fallback.RecentsState
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt
similarity index 98%
rename from quickstep/tests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt
rename to quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt
index d064f4a..26f1197 100644
--- a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt
@@ -99,7 +99,7 @@
keyboardQuickSwitchController,
taskbarPinningController,
optionalBubbleControllers,
- taskbarDesktopModeController
+ taskbarDesktopModeController,
)
}
}
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarKeyguardControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarKeyguardControllerTest.kt
similarity index 97%
rename from quickstep/tests/src/com/android/launcher3/taskbar/TaskbarKeyguardControllerTest.kt
rename to quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarKeyguardControllerTest.kt
index e619e7c..2431020 100644
--- a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarKeyguardControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarKeyguardControllerTest.kt
@@ -16,6 +16,7 @@
package com.android.launcher3.taskbar
import android.app.KeyguardManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BACK_DISABLED
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DOZING
@@ -23,6 +24,7 @@
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
@@ -30,6 +32,7 @@
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
+@RunWith(AndroidJUnit4::class)
class TaskbarKeyguardControllerTest : TaskbarBaseTestCase() {
private val baseDragLayer: TaskbarDragLayer = mock()
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 13880f1..9ca8a1b 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt
@@ -19,9 +19,11 @@
import android.animation.AnimatorTestRule
import android.content.ComponentName
import android.content.Intent
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
import androidx.test.core.app.ApplicationProvider
+import com.android.launcher3.Flags.FLAG_ENABLE_MULTI_INSTANCE_MENU_TASKBAR
import com.android.launcher3.Flags.FLAG_TASKBAR_OVERFLOW
import com.android.launcher3.R
import com.android.launcher3.taskbar.TaskbarControllerTestUtil.runOnMainSync
@@ -64,6 +66,7 @@
FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
FLAG_ENABLE_BUBBLE_BAR,
)
+@DisableFlags(FLAG_ENABLE_MULTI_INSTANCE_MENU_TASKBAR)
class TaskbarOverflowTest {
@get:Rule(order = 0) val setFlagsRule = SetFlagsRule()
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt
similarity index 90%
rename from quickstep/tests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt
rename to quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt
index ed0c928..c792783 100644
--- a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt
@@ -21,29 +21,38 @@
import android.content.Context
import android.content.Intent
import android.content.res.Resources
+import android.graphics.Rect
import android.os.Process
import android.os.UserHandle
-import android.platform.test.rule.TestWatcher
-import android.testing.AndroidTestingRunner
+import android.platform.test.annotations.EnableFlags
+import androidx.test.annotation.UiThreadTest
import com.android.internal.R
import com.android.launcher3.BubbleTextView.RunningAppState
+import com.android.launcher3.Flags
import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT
import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION
import com.android.launcher3.model.data.AppInfo
import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.model.data.TaskItemInfo
import com.android.launcher3.model.data.WorkspaceItemInfo
+import com.android.launcher3.taskbar.TaskbarRecentAppsController.TaskState
+import com.android.launcher3.util.LauncherMultivalentJUnit
+import com.android.launcher3.util.SplitConfigurationOptions
import com.android.quickstep.RecentsModel
import com.android.quickstep.RecentsModel.RecentTasksChangedListener
import com.android.quickstep.TaskIconCache
import com.android.quickstep.util.DesktopTask
import com.android.quickstep.util.GroupTask
+import com.android.quickstep.util.SingleTask
+import com.android.quickstep.util.SplitTask
import com.android.systemui.shared.recents.model.Task
+import com.android.wm.shell.shared.split.SplitScreenConstants
import com.google.common.truth.Truth.assertThat
import java.util.function.Consumer
import org.junit.Before
import org.junit.Rule
import org.junit.Test
+import org.junit.rules.TestWatcher
import org.junit.runner.Description
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
@@ -56,7 +65,9 @@
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
-@RunWith(AndroidTestingRunner::class)
+@UiThreadTest
+@RunWith(LauncherMultivalentJUnit::class)
+@EnableFlags(Flags.FLAG_ENABLE_MULTI_INSTANCE_MENU_TASKBAR)
class TaskbarRecentAppsControllerTest : TaskbarBaseTestCase() {
@get:Rule val mockitoRule = MockitoJUnit.rule()
@@ -86,6 +97,9 @@
private var canShowRunningAndRecentAppsAtInit = true
private var recentTasksChangedListener: RecentTasksChangedListener? = null
+ val recentShownTasks: List<Task>
+ get() = recentAppsController.shownTasks.flatMap { it.tasks }
+
@Before
fun setUp() {
super.setup()
@@ -139,7 +153,7 @@
@Test
fun canShowRunningAndRecentAppsIsFalseAfterInit_getTasksOnlyCalledInInit() {
// getTasks() should have been called once from init().
- verify(mockRecentsModel, times(1)).getTasks(any<Consumer<List<GroupTask>>>())
+ verify(mockRecentsModel, times(1)).getTasks(any<Consumer<List<GroupTask>>>(), any())
recentAppsController.canShowRunningApps = false
recentAppsController.canShowRecentApps = false
prepareHotseatAndRunningAndRecentApps(
@@ -148,29 +162,28 @@
recentTaskPackages = listOf(RECENT_PACKAGE_1, RECENT_PACKAGE_2),
)
// Verify that getTasks() was not called again after the init().
- verify(mockRecentsModel, times(1)).getTasks(any<Consumer<List<GroupTask>>>())
+ verify(mockRecentsModel, times(1)).getTasks(any<Consumer<List<GroupTask>>>(), any())
}
@Test
fun getDesktopItemState_nullItemInfo_returnsNotRunning() {
setInDesktopMode(true)
- assertThat(recentAppsController.getDesktopItemState(/* itemInfo= */ null))
- .isEqualTo(RunningAppState.NOT_RUNNING)
+ val taskState = recentAppsController.getDesktopItemState(/* itemInfo= */ null)
+ assertThat(taskState).isEqualTo(TaskState(RunningAppState.NOT_RUNNING))
}
@Test
fun getDesktopItemState_noItemPackage_returnsNotRunning() {
setInDesktopMode(true)
- assertThat(recentAppsController.getDesktopItemState(ItemInfo()))
- .isEqualTo(RunningAppState.NOT_RUNNING)
+ val taskState = recentAppsController.getDesktopItemState(ItemInfo())
+ assertThat(taskState).isEqualTo(TaskState(RunningAppState.NOT_RUNNING))
}
@Test
fun getDesktopItemState_noMatchingTasks_returnsNotRunning() {
setInDesktopMode(true)
- val itemInfo = createItemInfo("package")
- assertThat(recentAppsController.getDesktopItemState(itemInfo))
- .isEqualTo(RunningAppState.NOT_RUNNING)
+ val taskState = recentAppsController.getDesktopItemState(createItemInfo("package"))
+ assertThat(taskState).isEqualTo(TaskState(RunningAppState.NOT_RUNNING))
}
@Test
@@ -178,10 +191,10 @@
setInDesktopMode(true)
val visibleTask = createTask(id = 1, "visiblePackage", isVisible = true)
updateRecentTasks(runningTasks = listOf(visibleTask), recentTaskPackages = emptyList())
- val itemInfo = createItemInfo("visiblePackage")
- assertThat(recentAppsController.getDesktopItemState(itemInfo))
- .isEqualTo(RunningAppState.RUNNING)
+ val taskState = recentAppsController.getDesktopItemState(createItemInfo("visiblePackage"))
+
+ assertThat(taskState).isEqualTo(TaskState(RunningAppState.RUNNING, taskId = 1))
}
@Test
@@ -189,10 +202,10 @@
setInDesktopMode(true)
val minimizedTask = createTask(id = 1, "minimizedPackage", isVisible = false)
updateRecentTasks(runningTasks = listOf(minimizedTask), recentTaskPackages = emptyList())
- val itemInfo = createItemInfo("minimizedPackage")
- assertThat(recentAppsController.getDesktopItemState(itemInfo))
- .isEqualTo(RunningAppState.MINIMIZED)
+ val taskState = recentAppsController.getDesktopItemState(createItemInfo("minimizedPackage"))
+
+ assertThat(taskState).isEqualTo(TaskState(RunningAppState.MINIMIZED, taskId = 1))
}
@Test
@@ -206,10 +219,10 @@
),
recentTaskPackages = emptyList(),
)
- val itemInfo = createItemInfo("package")
- assertThat(recentAppsController.getDesktopItemState(itemInfo))
- .isEqualTo(RunningAppState.RUNNING)
+ val taskState = recentAppsController.getDesktopItemState(createItemInfo("package"))
+
+ assertThat(taskState).isEqualTo(TaskState(RunningAppState.RUNNING, taskId = 2))
}
@Test
@@ -223,10 +236,11 @@
),
recentTaskPackages = emptyList(),
)
- val itemInfo = createItemInfo("package", USER_HANDLE_2)
- assertThat(recentAppsController.getDesktopItemState(itemInfo))
- .isEqualTo(RunningAppState.NOT_RUNNING)
+ val taskState =
+ recentAppsController.getDesktopItemState(createItemInfo("package", USER_HANDLE_2))
+
+ assertThat(taskState).isEqualTo(TaskState(RunningAppState.NOT_RUNNING))
}
@Test
@@ -323,8 +337,12 @@
assertThat(hotseatItem1.taskId).isEqualTo(1)
}
+ /**
+ * Tests that in desktop mode, when two tasks have the same package name and one is in the
+ * hotseat, only the hotseat item represents the app, and no duplicate is shown in recent apps.
+ */
@Test
- fun updateHotseatItemInfos_inDesktopMode_twoRunningTasksSamePackage_hotseatCoversFirstTask() {
+ fun updateHotseatItemInfos_inDesktopMode_twoRunningTasksSamePackage_onlyHotseatCoversTask() {
setInDesktopMode(true)
val newHotseatItems =
@@ -338,16 +356,15 @@
recentTaskPackages = emptyList(),
)
- // First task is in Hotseat Items
+ // The task is in Hotseat Items
assertThat(newHotseatItems).hasLength(2)
assertThat(newHotseatItems[0]).isInstanceOf(TaskItemInfo::class.java)
assertThat(newHotseatItems[1]).isNotInstanceOf(TaskItemInfo::class.java)
val hotseatItem1 = newHotseatItems[0] as TaskItemInfo
- assertThat(hotseatItem1.taskId).isEqualTo(1)
- // Second task is in shownTasks
- val shownTasks = recentAppsController.shownTasks.map { it.task1 }
- assertThat(shownTasks)
- .containsExactlyElementsIn(listOf(createTask(id = 2, HOTSEAT_PACKAGE_1)))
+ assertThat(hotseatItem1.targetPackage).isEqualTo(HOTSEAT_PACKAGE_1)
+
+ // The other task of the same package is not in recentShownTasks
+ assertThat(recentShownTasks).isEmpty()
}
@Test
@@ -430,8 +447,7 @@
runningTasks = listOf(task1, task2),
recentTaskPackages = emptyList(),
)
- val shownTasks = recentAppsController.shownTasks.map { it.task1 }
- assertThat(shownTasks).containsExactlyElementsIn(listOf(task1, task2))
+ assertThat(recentShownTasks).containsExactlyElementsIn(listOf(task1, task2))
}
@Test
@@ -526,12 +542,15 @@
recentTaskPackages = emptyList(),
)
- val shownTasks = recentAppsController.shownTasks.map { it.task1 }
- assertThat(shownTasks).isEqualTo(listOf(task1, task2))
+ assertThat(recentShownTasks).isEqualTo(listOf(task1, task2))
}
+ /**
+ * Tests that when multiple instances of the same app are running in desktop mode and the app is
+ * not in the hotseat, only one instance is shown in the recent apps section.
+ */
@Test
- fun onRecentTasksChanged_inDesktopMode_multiInstance_shownTasks_maintainsOrder() {
+ fun onRecentTasksChanged_inDesktopMode_multiInstance_noHotseat_shownTasksHasOneInstance() {
setInDesktopMode(true)
val task1 = createTask(id = 1, RUNNING_APP_PACKAGE_1)
val task2 = createTask(id = 2, RUNNING_APP_PACKAGE_1)
@@ -541,43 +560,9 @@
recentTaskPackages = emptyList(),
)
- prepareHotseatAndRunningAndRecentApps(
- hotseatPackages = emptyList(),
- runningTasks = listOf(task2, task1),
- recentTaskPackages = emptyList(),
- )
-
- val shownTasks = recentAppsController.shownTasks.map { it.task1 }
- assertThat(shownTasks).isEqualTo(listOf(task1, task2))
- }
-
- @Test
- fun updateHotseatItems_inDesktopMode_multiInstanceHotseatPackage_shownItems_maintainsOrder() {
- setInDesktopMode(true)
- val task1 = createTask(id = 1, RUNNING_APP_PACKAGE_1)
- val task2 = createTask(id = 2, RUNNING_APP_PACKAGE_1)
- prepareHotseatAndRunningAndRecentApps(
- hotseatPackages = listOf(RUNNING_APP_PACKAGE_1),
- runningTasks = listOf(task1, task2),
- recentTaskPackages = emptyList(),
- )
- updateRecentTasks( // Trigger a recent-tasks change before calling updateHotseatItems()
- runningTasks = listOf(task2, task1),
- recentTaskPackages = emptyList(),
- )
-
- prepareHotseatAndRunningAndRecentApps(
- hotseatPackages = listOf(RUNNING_APP_PACKAGE_1),
- runningTasks = listOf(task2, task1),
- recentTaskPackages = emptyList(),
- )
-
- val newHotseatItems = recentAppsController.shownHotseatItems
- assertThat(newHotseatItems).hasSize(1)
- assertThat(newHotseatItems[0]).isInstanceOf(TaskItemInfo::class.java)
- assertThat((newHotseatItems[0] as TaskItemInfo).taskId).isEqualTo(1)
- val shownTasks = recentAppsController.shownTasks.map { it.task1 }
- assertThat(shownTasks).isEqualTo(listOf(task2))
+ // Assert that recentShownTasks contains only one instance of the app
+ assertThat(recentShownTasks).hasSize(1)
+ assertThat(recentShownTasks[0].key.packageName).isEqualTo(RUNNING_APP_PACKAGE_1)
}
@Test
@@ -859,8 +844,7 @@
runningTasks = runningTasks,
recentTaskPackages = emptyList(),
)
- val shownTasks = recentAppsController.shownTasks.map { it.task1 }
- assertThat(shownTasks).contains(runningTask)
+ assertThat(recentShownTasks).contains(runningTask)
assertThat(recentAppsController.runningTaskIds).containsExactlyElementsIn(listOf(1))
}
@@ -903,7 +887,7 @@
taskListChangeId
}
.whenever(mockRecentsModel)
- .getTasks(any<Consumer<List<GroupTask>>>())
+ .getTasks(any<Consumer<List<GroupTask>>>(), any())
recentTasksChangedListener?.onRecentTasksChanged()
}
@@ -934,15 +918,21 @@
return packageNames.map { packageName ->
if (packageName.startsWith("split")) {
val splitPackages = packageName.split("_")
- GroupTask(
+ SplitTask(
createTask(100, splitPackages[0]),
createTask(101, splitPackages[1]),
- /* splitBounds = */ null,
+ SplitConfigurationOptions.SplitBounds(
+ /* leftTopBounds = */ Rect(),
+ /* rightBottomBounds = */ Rect(),
+ /* leftTopTaskId = */ -1,
+ /* rightBottomTaskId = */ -1,
+ /* snapPosition = */ SplitScreenConstants.SNAP_TO_2_50_50,
+ ),
)
} else {
// Use the number at the end of the test packageName as the id.
val id = 1000 + packageName[packageName.length - 1].code
- GroupTask(createTask(id, packageName))
+ SingleTask(createTask(id, packageName))
}
}
}
@@ -967,7 +957,7 @@
}
private fun setInDesktopMode(inDesktopMode: Boolean) {
- whenever(taskbarControllers.taskbarDesktopModeController.areDesktopTasksVisible)
+ whenever(taskbarControllers.taskbarDesktopModeController.shouldShowDesktopTasksInTaskbar())
.thenReturn(inDesktopMode)
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarViewTestUtil.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarViewTestUtil.kt
index f2dcf77..df70b10 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarViewTestUtil.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarViewTestUtil.kt
@@ -28,6 +28,7 @@
import com.android.launcher3.taskbar.TaskbarIconType.OVERFLOW
import com.android.launcher3.taskbar.TaskbarIconType.RECENT
import com.android.quickstep.util.GroupTask
+import com.android.quickstep.util.SingleTask
import com.android.systemui.shared.recents.model.Task
import com.android.systemui.shared.recents.model.Task.TaskKey
import com.google.common.truth.FailureMetadata
@@ -56,7 +57,7 @@
/** Creates a list of fake recent tasks. */
fun createRecents(size: Int): List<GroupTask> {
return List(size) {
- GroupTask(
+ SingleTask(
Task().apply {
key =
TaskKey(
@@ -99,9 +100,9 @@
/** Verifies that recents from [startIndex] have IDs that match [expectedIds] in order. */
fun hasRecentsOrder(startIndex: Int, expectedIds: List<Int>) {
val actualIds =
- view.iconViews.slice(startIndex..<startIndex + expectedIds.size).map {
+ view.iconViews.slice(startIndex..<startIndex + expectedIds.size).flatMap {
assertThat(it.tag).isInstanceOf(GroupTask::class.java)
- (it.tag as? GroupTask)?.task1?.key?.id
+ (it.tag as GroupTask).tasks.map { task -> task.key.id }
}
assertThat(actualIds).containsExactlyElementsIn(expectedIds).inOrder()
}
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 3ca36ec..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
@@ -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/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/TaskbarWindowSandboxContext.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarWindowSandboxContext.kt
index 31c5a4c..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,17 +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
@@ -113,11 +121,32 @@
}
}
+/** A wrapper over display controller which allows modifying the underlying info */
@LauncherAppSingleton
-@Component(modules = [LauncherAppModule::class])
-interface TaskbarSandboxComponent : LauncherAppComponent {
+class DisplayControllerSpy
+@Inject
+constructor(
+ @ApplicationContext context: Context,
+ wmProxy: WindowManagerProxy,
+ prefs: LauncherPrefs,
+ lifecycle: DaggerSingletonTracker,
+) : DisplayController(context, wmProxy, prefs, lifecycle) {
- override fun getLauncherPrefs(): FakeLauncherPrefs
+ 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 {
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java
index c334552..0c74610 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java
@@ -18,6 +18,8 @@
import static com.android.quickstep.AbsSwipeUpHandler.STATE_HANDLER_INVALIDATED;
import static com.android.wm.shell.shared.ShellSharedConstants.KEY_EXTRA_SHELL_CAN_HAND_OFF_ANIMATION;
+import static com.android.wm.shell.shared.split.SplitBounds.KEY_EXTRA_SPLIT_BOUNDS;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50;
import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertTrue;
@@ -68,6 +70,7 @@
import com.android.quickstep.views.RecentsViewContainer;
import com.android.systemui.shared.Flags;
import com.android.systemui.shared.system.InputConsumerController;
+import com.android.wm.shell.shared.split.SplitBounds;
import com.google.android.msdl.data.model.MSDLToken;
@@ -118,7 +121,6 @@
protected RecentsAnimationTargets mRecentsAnimationTargets;
protected TaskAnimationManager mTaskAnimationManager;
- protected RecentsAnimationDeviceState mRecentsAnimationDeviceState;
@Mock protected CONTAINER_INTERFACE mActivityInterface;
@Mock protected ContextInitListener<?> mContextInitListener;
@@ -141,6 +143,12 @@
public void setUpAnimationTargets() {
Bundle extras = new Bundle();
extras.putBoolean(KEY_EXTRA_SHELL_CAN_HAND_OFF_ANIMATION, true);
+ extras.putParcelable(KEY_EXTRA_SPLIT_BOUNDS, new SplitBounds(
+ /* leftTopBounds = */ new Rect(),
+ /* rightBottomBounds = */ new Rect(),
+ /* leftTopTaskId = */ -1,
+ /* rightBottomTaskId = */ -1,
+ /* snapPosition = */ SNAP_TO_2_50_50));
mRecentsAnimationTargets = new RecentsAnimationTargets(
new RemoteAnimationTarget[] {mRemoteAnimationTarget},
new RemoteAnimationTarget[] {mRemoteAnimationTarget},
@@ -180,7 +188,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 +207,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 +373,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..af741f6 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,32 @@
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)
+ .bindRotationHelper(mock(RotationTouchHelper::class.java))
+ .bindRecentsState(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,
@@ -98,20 +111,14 @@
gestureState.setTrackpadGestureType(GestureState.TrackpadGestureType.THREE_FINGER)
underTest.onGestureEnded(flingSpeed, PointF())
verify(systemUiProxy)
- .updateContextualEduStats(
- /* isTrackpadGesture= */ eq(true),
- eq(GestureType.HOME.toString()),
- )
+ .updateContextualEduStats(/* isTrackpadGesture= */ eq(true), eq(GestureType.HOME))
}
@Test
fun goHomeFromAppByTouch_updateEduStats() {
underTest.onGestureEnded(flingSpeed, PointF())
verify(systemUiProxy)
- .updateContextualEduStats(
- /* isTrackpadGesture= */ eq(false),
- eq(GestureType.HOME.toString()),
- )
+ .updateContextualEduStats(/* isTrackpadGesture= */ eq(false), eq(GestureType.HOME))
}
}
@@ -122,7 +129,9 @@
interface Builder : LauncherAppComponent.Builder {
@BindsInstance fun bindSystemUiProxy(proxy: SystemUiProxy): Builder
- @BindsInstance fun bindRecentsDisplayModel(model: RecentsDisplayModel): Builder
+ @BindsInstance fun bindRotationHelper(helper: RotationTouchHelper): Builder
+
+ @BindsInstance fun bindRecentsState(state: RecentsAnimationDeviceState): 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..66c4ab5 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2TestCase.java
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2TestCase.java
@@ -33,11 +33,13 @@
import com.android.quickstep.views.RecentsView;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@SmallTest
@RunWith(LauncherMultivalentJUnit.class)
+@Ignore
public class LauncherSwipeHandlerV2TestCase extends AbsSwipeUpHandlerTestCase<
LauncherState,
QuickstepLauncher,
@@ -73,7 +75,6 @@
long touchTimeMs, boolean continuingLastGesture) {
return new LauncherSwipeHandlerV2(
mContext,
- mRecentsAnimationDeviceState,
mTaskAnimationManager,
mGestureState,
touchTimeMs,
diff --git a/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentTasksListTest.java
similarity index 75%
rename from quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java
rename to quickstep/tests/multivalentTests/src/com/android/quickstep/RecentTasksListTest.java
index b3c486c..cad3b99 100644
--- a/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentTasksListTest.java
@@ -21,7 +21,9 @@
import static junit.framework.TestCase.assertNull;
import static org.junit.Assert.assertEquals;
-import static org.mockito.Matchers.anyInt;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -33,7 +35,9 @@
import android.app.TaskInfo;
import android.content.Context;
import android.content.res.Resources;
+import android.graphics.Rect;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.R;
@@ -42,9 +46,12 @@
import com.android.quickstep.views.TaskViewType;
import com.android.systemui.shared.recents.model.Task;
import com.android.wm.shell.shared.GroupedTaskInfo;
+import com.android.wm.shell.shared.split.SplitBounds;
+import com.android.wm.shell.shared.split.SplitScreenConstants;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -56,6 +63,7 @@
import java.util.stream.Collectors;
@SmallTest
+@RunWith(AndroidJUnit4.class)
public class RecentTasksListTest {
@Mock
@@ -94,7 +102,12 @@
@Test
public void loadTasksInBackground_onlyKeys_noValidTaskDescription() throws Exception {
GroupedTaskInfo recentTaskInfos = GroupedTaskInfo.forSplitTasks(
- new RecentTaskInfo(), new RecentTaskInfo(), null);
+ new RecentTaskInfo(), new RecentTaskInfo(), new SplitBounds(
+ /* leftTopBounds = */ new Rect(),
+ /* rightBottomBounds = */ new Rect(),
+ /* leftTopTaskId = */ -1,
+ /* rightBottomTaskId = */ -1,
+ /* snapPosition = */ SplitScreenConstants.SNAP_TO_2_50_50));
when(mSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
.thenReturn(new ArrayList<>(Collections.singletonList(recentTaskInfos)));
@@ -102,8 +115,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
@@ -125,7 +137,13 @@
task1.taskDescription = new ActivityManager.TaskDescription(taskDescription);
RecentTaskInfo task2 = new RecentTaskInfo();
task2.taskDescription = new ActivityManager.TaskDescription();
- GroupedTaskInfo recentTaskInfos = GroupedTaskInfo.forSplitTasks(task1, task2, null);
+ GroupedTaskInfo recentTaskInfos = GroupedTaskInfo.forSplitTasks(task1, task2,
+ new SplitBounds(
+ /* leftTopBounds = */ new Rect(),
+ /* rightBottomBounds = */ new Rect(),
+ /* leftTopTaskId = */ -1,
+ /* rightBottomTaskId = */ -1,
+ /* snapPosition = */ SplitScreenConstants.SNAP_TO_2_50_50));
when(mSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
.thenReturn(new ArrayList<>(Collections.singletonList(recentTaskInfos)));
@@ -133,8 +151,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
@@ -156,12 +176,15 @@
List<Task> actualFreeformTasks = taskList.get(0).getTasks();
assertEquals(3, actualFreeformTasks.size());
assertEquals(1, actualFreeformTasks.get(0).key.id);
+ assertFalse(actualFreeformTasks.get(0).isMinimized);
assertEquals(4, actualFreeformTasks.get(1).key.id);
+ assertFalse(actualFreeformTasks.get(1).isMinimized);
assertEquals(5, actualFreeformTasks.get(2).key.id);
+ assertFalse(actualFreeformTasks.get(2).isMinimized);
}
@Test
- public void loadTasksInBackground_freeformTask_onlyMinimizedTasks_doesNotCreateDesktopTask()
+ public void loadTasksInBackground_freeformTask_onlyMinimizedTasks_createDesktopTask()
throws Exception {
List<TaskInfo> tasks = Arrays.asList(
createRecentTaskInfo(1 /* taskId */),
@@ -176,7 +199,16 @@
List<GroupTask> taskList = mRecentTasksList.loadTasksInBackground(
Integer.MAX_VALUE /* numTasks */, -1 /* requestId */, false /* loadKeysOnly */);
- assertEquals(0, taskList.size());
+ assertEquals(1, taskList.size());
+ assertEquals(TaskViewType.DESKTOP, taskList.get(0).taskViewType);
+ List<Task> actualFreeformTasks = taskList.get(0).getTasks();
+ assertEquals(3, actualFreeformTasks.size());
+ assertEquals(1, actualFreeformTasks.get(0).key.id);
+ assertTrue(actualFreeformTasks.get(0).isMinimized);
+ assertEquals(4, actualFreeformTasks.get(1).key.id);
+ assertTrue(actualFreeformTasks.get(1).isMinimized);
+ assertEquals(5, actualFreeformTasks.get(2).key.id);
+ assertTrue(actualFreeformTasks.get(2).isMinimized);
}
private TaskInfo createRecentTaskInfo(int taskId) {
diff --git a/quickstep/tests/src/com/android/quickstep/RecentsAnimationDeviceStateTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentsAnimationDeviceStateTest.kt
similarity index 66%
rename from quickstep/tests/src/com/android/quickstep/RecentsAnimationDeviceStateTest.kt
rename to quickstep/tests/multivalentTests/src/com/android/quickstep/RecentsAnimationDeviceStateTest.kt
index 80fbce7..b652ee8 100644
--- a/quickstep/tests/src/com/android/quickstep/RecentsAnimationDeviceStateTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentsAnimationDeviceStateTest.kt
@@ -1,14 +1,17 @@
package com.android.quickstep
-import android.content.Context
-import android.testing.AndroidTestingRunner
-import androidx.test.core.app.ApplicationProvider
+import androidx.test.annotation.UiThreadTest
import androidx.test.filters.SmallTest
+import com.android.launcher3.dagger.LauncherComponentProvider
import com.android.launcher3.util.DisplayController.CHANGE_DENSITY
import com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE
import com.android.launcher3.util.DisplayController.CHANGE_ROTATION
import com.android.launcher3.util.DisplayController.Info
+import com.android.launcher3.util.Executors.MAIN_EXECUTOR
+import com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR
+import com.android.launcher3.util.LauncherMultivalentJUnit
import com.android.launcher3.util.NavigationMode
+import com.android.launcher3.util.SandboxApplication
import com.android.quickstep.util.GestureExclusionManager
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DREAMING
@@ -22,7 +25,9 @@
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED
import com.google.common.truth.Truth.assertThat
+import org.junit.After
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
@@ -30,24 +35,43 @@
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.doReturn
-import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.verifyNoMoreInteractions
import org.mockito.kotlin.whenever
/** Unit test for [RecentsAnimationDeviceState]. */
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@UiThreadTest
+@RunWith(LauncherMultivalentJUnit::class)
class RecentsAnimationDeviceStateTest {
+ @get:Rule val context = SandboxApplication()
+
@Mock private lateinit var exclusionManager: GestureExclusionManager
@Mock private lateinit var info: Info
- private val context = ApplicationProvider.getApplicationContext() as Context
private lateinit var underTest: RecentsAnimationDeviceState
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- underTest = RecentsAnimationDeviceState(context, exclusionManager)
+
+ val component = LauncherComponentProvider.get(context)
+ underTest =
+ RecentsAnimationDeviceState(
+ context,
+ exclusionManager,
+ component.displayController,
+ component.contextualSearchStateManager,
+ component.rotationTouchHelper,
+ component.settingsCache,
+ component.daggerSingletonTracker,
+ )
+ }
+
+ @After
+ fun tearDown() {
+ UI_HELPER_EXECUTOR.submit {}.get()
+ MAIN_EXECUTOR.submit {}.get()
}
@Test
@@ -64,7 +88,7 @@
underTest.registerExclusionListener()
- verifyZeroInteractions(exclusionManager)
+ verifyNoMoreInteractions(exclusionManager)
}
@Test
@@ -85,7 +109,7 @@
underTest.unregisterExclusionListener()
- verifyZeroInteractions(exclusionManager)
+ verifyNoMoreInteractions(exclusionManager)
}
@Test
@@ -116,13 +140,13 @@
underTest.onDisplayInfoChanged(context, info, CHANGE_DENSITY)
- verifyZeroInteractions(exclusionManager)
+ verifyNoMoreInteractions(exclusionManager)
}
@Test
fun trackpadGesturesNotAllowedForSelectedStates() {
- val disablingStates = GESTURE_DISABLING_SYSUI_STATES +
- SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED
+ val disablingStates =
+ GESTURE_DISABLING_SYSUI_STATES + SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED
allSysUiStates().forEach { state ->
val canStartGesture = !disablingStates.contains(state)
@@ -133,13 +157,13 @@
@Test
fun trackpadGesturesNotAllowedIfHomeAndOverviewIsDisabled() {
- val stateToExpectedResult = mapOf(
- SYSUI_STATE_HOME_DISABLED to true,
- SYSUI_STATE_OVERVIEW_DISABLED to true,
- DEFAULT_STATE
- .enable(SYSUI_STATE_OVERVIEW_DISABLED)
- .enable(SYSUI_STATE_HOME_DISABLED) to false
- )
+ val stateToExpectedResult =
+ mapOf(
+ SYSUI_STATE_HOME_DISABLED to true,
+ SYSUI_STATE_OVERVIEW_DISABLED to true,
+ DEFAULT_STATE.enable(SYSUI_STATE_OVERVIEW_DISABLED)
+ .enable(SYSUI_STATE_HOME_DISABLED) to false,
+ )
stateToExpectedResult.forEach { (state, allowed) ->
underTest.setSystemUiFlags(state)
@@ -160,22 +184,19 @@
@Test
fun systemGesturesNotAllowedWhenGestureStateDisabledAndNavBarVisible() {
- val stateToExpectedResult = mapOf(
- DEFAULT_STATE
- .enable(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY)
- .disable(SYSUI_STATE_NAV_BAR_HIDDEN) to true,
- DEFAULT_STATE
- .enable(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY)
- .enable(SYSUI_STATE_NAV_BAR_HIDDEN) to true,
- DEFAULT_STATE
- .disable(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY)
- .disable(SYSUI_STATE_NAV_BAR_HIDDEN) to true,
- DEFAULT_STATE
- .disable(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY)
- .enable(SYSUI_STATE_NAV_BAR_HIDDEN) to false,
- )
+ val stateToExpectedResult =
+ mapOf(
+ DEFAULT_STATE.enable(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY)
+ .disable(SYSUI_STATE_NAV_BAR_HIDDEN) to true,
+ DEFAULT_STATE.enable(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY)
+ .enable(SYSUI_STATE_NAV_BAR_HIDDEN) to true,
+ DEFAULT_STATE.disable(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY)
+ .disable(SYSUI_STATE_NAV_BAR_HIDDEN) to true,
+ DEFAULT_STATE.disable(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY)
+ .enable(SYSUI_STATE_NAV_BAR_HIDDEN) to false,
+ )
- stateToExpectedResult.forEach {(state, gestureAllowed) ->
+ stateToExpectedResult.forEach { (state, gestureAllowed) ->
underTest.setSystemUiFlags(state)
assertThat(underTest.canStartSystemGesture()).isEqualTo(gestureAllowed)
}
@@ -187,14 +208,15 @@
}
companion object {
- private val GESTURE_DISABLING_SYSUI_STATES = listOf(
- SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED,
- SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING,
- SYSUI_STATE_QUICK_SETTINGS_EXPANDED,
- SYSUI_STATE_MAGNIFICATION_OVERLAP,
- SYSUI_STATE_DEVICE_DREAMING,
- SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION,
- )
+ private val GESTURE_DISABLING_SYSUI_STATES =
+ listOf(
+ SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED,
+ SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING,
+ SYSUI_STATE_QUICK_SETTINGS_EXPANDED,
+ SYSUI_STATE_MAGNIFICATION_OVERLAP,
+ SYSUI_STATE_DEVICE_DREAMING,
+ SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION,
+ )
private const val SYSUI_STATES_COUNT = 33
private const val DEFAULT_STATE = 0L
}
diff --git a/quickstep/tests/src/com/android/quickstep/RecentsModelTest.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentsModelTest.java
similarity index 83%
rename from quickstep/tests/src/com/android/quickstep/RecentsModelTest.java
rename to quickstep/tests/multivalentTests/src/com/android/quickstep/RecentsModelTest.java
index ef4591e..99a1c59 100644
--- a/quickstep/tests/src/com/android/quickstep/RecentsModelTest.java
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentsModelTest.java
@@ -32,21 +32,28 @@
import android.app.ActivityManager;
import android.content.Context;
import android.content.res.Resources;
+import android.graphics.Rect;
import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.annotation.UiThreadTest;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
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.launcher3.util.SplitConfigurationOptions;
import com.android.quickstep.util.GroupTask;
+import com.android.quickstep.util.SplitTask;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.TaskStackChangeListeners;
+import com.android.wm.shell.shared.split.SplitScreenConstants;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -55,6 +62,7 @@
import java.util.function.Consumer;
@SmallTest
+@RunWith(AndroidJUnit4.class)
public class RecentsModelTest {
@Mock
private Context mContext;
@@ -93,7 +101,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);
@@ -110,10 +119,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
@@ -166,7 +177,13 @@
Task.TaskKey taskKey2 = new Task.TaskKey(taskInfo2);
Task task2 = Task.from(taskKey2, taskInfo2, false);
- allTasks.add(new GroupTask(task1, task2, null));
+ allTasks.add(
+ new SplitTask(task1, task2, new SplitConfigurationOptions.SplitBounds(
+ /* leftTopBounds = */ new Rect(),
+ /* rightBottomBounds = */ new Rect(),
+ /* leftTopTaskId = */ -1,
+ /* rightBottomTaskId = */ -1,
+ /* snapPosition = */ SplitScreenConstants.SNAP_TO_2_50_50)));
return allTasks;
}
}
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/src/com/android/quickstep/TaskAnimationManagerTest.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/TaskAnimationManagerTest.java
similarity index 90%
rename from quickstep/tests/src/com/android/quickstep/TaskAnimationManagerTest.java
rename to quickstep/tests/multivalentTests/src/com/android/quickstep/TaskAnimationManagerTest.java
index 098b417..6e9885a 100644
--- a/quickstep/tests/src/com/android/quickstep/TaskAnimationManagerTest.java
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/TaskAnimationManagerTest.java
@@ -27,16 +27,19 @@
import android.content.Context;
import android.content.Intent;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@SmallTest
+@RunWith(AndroidJUnit4.class)
public class TaskAnimationManagerTest {
protected final Context mContext =
@@ -46,18 +49,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/quickstep/tests/src/com/android/quickstep/TaskThumbnailCacheTest.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/TaskThumbnailCacheTest.java
similarity index 96%
rename from quickstep/tests/src/com/android/quickstep/TaskThumbnailCacheTest.java
rename to quickstep/tests/multivalentTests/src/com/android/quickstep/TaskThumbnailCacheTest.java
index 4e04261..3686c16 100644
--- a/quickstep/tests/src/com/android/quickstep/TaskThumbnailCacheTest.java
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/TaskThumbnailCacheTest.java
@@ -28,6 +28,7 @@
import android.content.Context;
import android.content.res.Resources;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.launcher3.R;
@@ -35,12 +36,14 @@
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.concurrent.Executor;
@SmallTest
+@RunWith(AndroidJUnit4.class)
public class TaskThumbnailCacheTest {
@Mock
private Context mContext;
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/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/FakeTasksRepository.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTasksRepository.kt
index 1c9ce0b..35af29f 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTasksRepository.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTasksRepository.kt
@@ -58,10 +58,10 @@
tasks.value.map {
it.apply {
thumbnail = thumbnailDataMap[it.key.id]
- taskIconDataMap[it.key.id].let { data ->
- title = data?.title
- titleDescription = data?.titleDescription
- icon = data?.icon
+ taskIconDataMap[it.key.id]?.let { data ->
+ title = data.title
+ titleDescription = data.titleDescription
+ icon = data.icon
}
}
}
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 b6cf5bd..823f808 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt
@@ -19,13 +19,17 @@
import android.content.ComponentName
import android.content.Intent
import android.graphics.Bitmap
+import android.graphics.Rect
import android.graphics.drawable.Drawable
import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.launcher3.util.SplitConfigurationOptions
import com.android.launcher3.util.TestDispatcherProvider
import com.android.quickstep.util.DesktopTask
-import com.android.quickstep.util.GroupTask
+import com.android.quickstep.util.SingleTask
+import com.android.quickstep.util.SplitTask
import com.android.systemui.shared.recents.model.Task
import com.android.systemui.shared.recents.model.ThumbnailData
+import com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.first
@@ -48,8 +52,18 @@
private val tasks = (0..5).map(::createTaskWithId)
private val defaultTaskList =
listOf(
- GroupTask(tasks[0]),
- GroupTask(tasks[1], tasks[2], null),
+ SingleTask(tasks[0]),
+ SplitTask(
+ tasks[1],
+ tasks[2],
+ SplitConfigurationOptions.SplitBounds(
+ /* leftTopBounds = */ Rect(),
+ /* rightBottomBounds = */ Rect(),
+ /* leftTopTaskId = */ -1,
+ /* rightBottomTaskId = */ -1,
+ /* snapPosition = */ SNAP_TO_2_50_50,
+ ),
+ ),
DesktopTask(tasks.subList(3, 6)),
)
private val recentsModel = FakeRecentTasksDataSource()
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/domain/usecase/GetTaskUseCaseTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/domain/usecase/GetTaskUseCaseTest.kt
new file mode 100644
index 0000000..b036bce
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/domain/usecase/GetTaskUseCaseTest.kt
@@ -0,0 +1,107 @@
+/*
+ * 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.recents.domain.usecase
+
+import android.content.ComponentName
+import android.content.Intent
+import android.graphics.Color
+import android.graphics.drawable.ShapeDrawable
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.quickstep.recents.data.FakeTasksRepository
+import com.android.quickstep.recents.domain.model.TaskModel
+import com.android.systemui.shared.recents.model.Task
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.firstOrNull
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+class GetTaskUseCaseTest {
+ private val unconfinedTestDispatcher = UnconfinedTestDispatcher()
+ private val testScope = TestScope(unconfinedTestDispatcher)
+
+ private val tasksRepository = FakeTasksRepository()
+ private val sut = GetTaskUseCase(repository = tasksRepository)
+
+ @Before
+ fun setUp() {
+ tasksRepository.seedTasks(listOf(TASK_1))
+ }
+
+ @Test
+ fun taskNotSeeded_returnsNull() =
+ testScope.runTest {
+ val result = sut.invoke(NOT_FOUND_TASK_ID).firstOrNull()
+ assertThat(result).isNull()
+ }
+
+ @Test
+ fun taskNotVisible_returnsNull() =
+ testScope.runTest {
+ val result = sut.invoke(TASK_1_ID).firstOrNull()
+ assertThat(result).isNull()
+ }
+
+ @Test
+ fun taskVisible_returnsData() =
+ testScope.runTest {
+ tasksRepository.setVisibleTasks(setOf(TASK_1_ID))
+ val expectedResult =
+ TaskModel(
+ id = TASK_1_ID,
+ title = "Title $TASK_1_ID",
+ titleDescription = "Content Description $TASK_1_ID",
+ icon = TASK_1_ICON,
+ thumbnail = null,
+ backgroundColor = Color.BLACK,
+ isLocked = false,
+ )
+ val result = sut.invoke(TASK_1_ID).firstOrNull()
+ assertThat(result).isEqualTo(expectedResult)
+ }
+
+ private companion object {
+ const val NOT_FOUND_TASK_ID = 404
+ private const val TASK_1_ID = 1
+ private val TASK_1_ICON = ShapeDrawable()
+ private val TASK_1 =
+ Task(
+ Task.TaskKey(
+ /* id = */ TASK_1_ID,
+ /* windowingMode = */ 0,
+ /* intent = */ Intent(),
+ /* sourceComponent = */ ComponentName("", ""),
+ /* userId = */ 0,
+ /* lastActiveTime = */ 2000,
+ )
+ )
+ .apply {
+ title = "Title 1"
+ titleDescription = "Content Description 1"
+ colorBackground = Color.BLACK
+ icon = TASK_1_ICON
+ thumbnail = null
+ isLocked = false
+ }
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/ui/viewmodel/TaskViewModelTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/ui/viewmodel/TaskViewModelTest.kt
new file mode 100644
index 0000000..54a27e9
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/ui/viewmodel/TaskViewModelTest.kt
@@ -0,0 +1,216 @@
+/*
+ * 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.recents.ui.viewmodel
+
+import android.graphics.Color
+import android.graphics.drawable.ShapeDrawable
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.launcher3.util.TestDispatcherProvider
+import com.android.quickstep.recents.domain.model.TaskModel
+import com.android.quickstep.recents.domain.usecase.GetTaskUseCase
+import com.android.quickstep.recents.viewmodel.RecentsViewData
+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.flow.flow
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+class TaskViewModelTest {
+ private val unconfinedTestDispatcher = UnconfinedTestDispatcher()
+ private val testScope = TestScope(unconfinedTestDispatcher)
+
+ private val recentsViewData = RecentsViewData()
+ private val getTaskUseCase = mock<GetTaskUseCase>()
+ private val sut =
+ TaskViewModel(
+ recentsViewData = recentsViewData,
+ getTaskUseCase = getTaskUseCase,
+ dispatcherProvider = TestDispatcherProvider(unconfinedTestDispatcher),
+ )
+
+ @Before
+ fun setUp() {
+ whenever(getTaskUseCase.invoke(TASK_MODEL_1.id)).thenReturn(flow { emit(TASK_MODEL_1) })
+ whenever(getTaskUseCase.invoke(TASK_MODEL_2.id)).thenReturn(flow { emit(TASK_MODEL_2) })
+ whenever(getTaskUseCase.invoke(TASK_MODEL_3.id)).thenReturn(flow { emit(TASK_MODEL_3) })
+ whenever(getTaskUseCase.invoke(INVALID_TASK_ID)).thenReturn(flow { emit(null) })
+ recentsViewData.runningTaskIds.value = emptySet()
+ }
+
+ @Test
+ fun singleTaskRetrieved_when_validTaskId() =
+ testScope.runTest {
+ sut.bind(TASK_MODEL_1.id)
+ val expectedResult = TaskTileUiState(listOf(TASK_MODEL_1.toUiState()), false)
+ assertThat(sut.state.first()).isEqualTo(expectedResult)
+ }
+
+ @Test
+ fun multipleTasksRetrieved_when_validTaskIds() =
+ testScope.runTest {
+ sut.bind(TASK_MODEL_1.id, TASK_MODEL_2.id, TASK_MODEL_3.id, INVALID_TASK_ID)
+ val expectedResult =
+ TaskTileUiState(
+ tasks =
+ listOf(
+ TASK_MODEL_1.toUiState(),
+ TASK_MODEL_2.toUiState(),
+ TASK_MODEL_3.toUiState(),
+ TaskData.NoData(INVALID_TASK_ID),
+ ),
+ isLiveTile = false,
+ )
+ assertThat(sut.state.first()).isEqualTo(expectedResult)
+ }
+
+ @Test
+ fun isLiveTile_when_runningTasksMatchTasks() =
+ testScope.runTest {
+ recentsViewData.runningTaskShowScreenshot.value = false
+ recentsViewData.runningTaskIds.value =
+ setOf(TASK_MODEL_1.id, TASK_MODEL_2.id, TASK_MODEL_3.id)
+ sut.bind(TASK_MODEL_1.id, TASK_MODEL_2.id, TASK_MODEL_3.id)
+ val expectedResult =
+ TaskTileUiState(
+ tasks =
+ listOf(
+ TASK_MODEL_1.toUiState(),
+ TASK_MODEL_2.toUiState(),
+ TASK_MODEL_3.toUiState(),
+ ),
+ isLiveTile = true,
+ )
+ assertThat(sut.state.first()).isEqualTo(expectedResult)
+ }
+
+ @Test
+ fun isNotLiveTile_when_runningTaskShowScreenshotIsTrue() =
+ testScope.runTest {
+ recentsViewData.runningTaskShowScreenshot.value = true
+ recentsViewData.runningTaskIds.value =
+ setOf(TASK_MODEL_1.id, TASK_MODEL_2.id, TASK_MODEL_3.id)
+ sut.bind(TASK_MODEL_1.id, TASK_MODEL_2.id, TASK_MODEL_3.id)
+ val expectedResult =
+ TaskTileUiState(
+ tasks =
+ listOf(
+ TASK_MODEL_1.toUiState(),
+ TASK_MODEL_2.toUiState(),
+ TASK_MODEL_3.toUiState(),
+ ),
+ isLiveTile = false,
+ )
+ assertThat(sut.state.first()).isEqualTo(expectedResult)
+ }
+
+ @Test
+ fun isNotLiveTile_when_runningTasksMatchPartialTasks_lessRunningTasks() =
+ testScope.runTest {
+ recentsViewData.runningTaskShowScreenshot.value = false
+ recentsViewData.runningTaskIds.value = setOf(TASK_MODEL_1.id, TASK_MODEL_2.id)
+ sut.bind(TASK_MODEL_1.id, TASK_MODEL_2.id, TASK_MODEL_3.id)
+ val expectedResult =
+ TaskTileUiState(
+ tasks =
+ listOf(
+ TASK_MODEL_1.toUiState(),
+ TASK_MODEL_2.toUiState(),
+ TASK_MODEL_3.toUiState(),
+ ),
+ isLiveTile = false,
+ )
+ assertThat(sut.state.first()).isEqualTo(expectedResult)
+ }
+
+ @Test
+ fun isNotLiveTile_when_runningTasksMatchPartialTasks_moreRunningTasks() =
+ testScope.runTest {
+ recentsViewData.runningTaskShowScreenshot.value = false
+ recentsViewData.runningTaskIds.value =
+ setOf(TASK_MODEL_1.id, TASK_MODEL_2.id, TASK_MODEL_3.id)
+ sut.bind(TASK_MODEL_1.id, TASK_MODEL_2.id)
+ val expectedResult =
+ TaskTileUiState(
+ tasks = listOf(TASK_MODEL_1.toUiState(), TASK_MODEL_2.toUiState()),
+ isLiveTile = false,
+ )
+ assertThat(sut.state.first()).isEqualTo(expectedResult)
+ }
+
+ @Test
+ fun noDataAvailable_when_InvalidTaskId() =
+ testScope.runTest {
+ sut.bind(INVALID_TASK_ID)
+ val expectedResult =
+ TaskTileUiState(listOf(TaskData.NoData(INVALID_TASK_ID)), isLiveTile = false)
+ assertThat(sut.state.first()).isEqualTo(expectedResult)
+ }
+
+ private fun TaskModel.toUiState() =
+ TaskData.Data(
+ taskId = id,
+ title = title,
+ icon = icon!!,
+ thumbnailData = thumbnail,
+ backgroundColor = backgroundColor,
+ isLocked = isLocked,
+ )
+
+ companion object {
+ const val INVALID_TASK_ID = -1
+ val TASK_MODEL_1 =
+ TaskModel(
+ 1,
+ "Title 1",
+ "Content Description 1",
+ ShapeDrawable(),
+ ThumbnailData(),
+ Color.BLACK,
+ false,
+ )
+ val TASK_MODEL_2 =
+ TaskModel(
+ 2,
+ "Title 2",
+ "Content Description 2",
+ ShapeDrawable(),
+ ThumbnailData(),
+ Color.RED,
+ true,
+ )
+ val TASK_MODEL_3 =
+ TaskModel(
+ 3,
+ "Title 3",
+ "Content Description 3",
+ ShapeDrawable(),
+ ThumbnailData(),
+ Color.BLUE,
+ false,
+ )
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/usecase/GetThumbnailUseCaseTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/usecase/GetThumbnailUseCaseTest.kt
index 73aa460..0044631 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/usecase/GetThumbnailUseCaseTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/usecase/GetThumbnailUseCaseTest.kt
@@ -76,7 +76,7 @@
assertThat(systemUnderTest.run(TASK_ID)).isEqualTo(thumbnailData.thumbnail)
}
- companion object {
+ private companion object {
const val TASK_ID = 0
const val THUMBNAIL_WIDTH = 100
const val THUMBNAIL_HEIGHT = 200
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/src/com/android/quickstep/taskbar/controllers/TaskbarPinningControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/taskbar/controllers/TaskbarPinningControllerTest.kt
similarity index 97%
rename from quickstep/tests/src/com/android/quickstep/taskbar/controllers/TaskbarPinningControllerTest.kt
rename to quickstep/tests/multivalentTests/src/com/android/quickstep/taskbar/controllers/TaskbarPinningControllerTest.kt
index 3148737..5b42d6c 100644
--- a/quickstep/tests/src/com/android/quickstep/taskbar/controllers/TaskbarPinningControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/taskbar/controllers/TaskbarPinningControllerTest.kt
@@ -56,9 +56,9 @@
private val taskbarSharedState = mock<TaskbarSharedState>()
private var isInDesktopMode = false
private val launcherPrefs =
- mock<LauncherPrefs> {
- on { get(TASKBAR_PINNING) } doReturn false
- on { get(TASKBAR_PINNING_IN_DESKTOP_MODE) } doReturn false
+ mock<LauncherPrefs>().apply {
+ doReturn(false).whenever(this).get(TASKBAR_PINNING)
+ doReturn(false).whenever(this).get(TASKBAR_PINNING_IN_DESKTOP_MODE)
}
private val statsLogger = mock<StatsLogManager.StatsLogger>()
private val statsLogManager = mock<StatsLogManager> { on { logger() } doReturn statsLogger }
diff --git a/quickstep/tests/src/com/android/quickstep/util/GestureExclusionManagerTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/GestureExclusionManagerTest.kt
similarity index 90%
rename from quickstep/tests/src/com/android/quickstep/util/GestureExclusionManagerTest.kt
rename to quickstep/tests/multivalentTests/src/com/android/quickstep/util/GestureExclusionManagerTest.kt
index c190cfe..555e62b 100644
--- a/quickstep/tests/src/com/android/quickstep/util/GestureExclusionManagerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/GestureExclusionManagerTest.kt
@@ -18,11 +18,12 @@
import android.graphics.Rect
import android.graphics.Region
-import android.testing.AndroidTestingRunner
import android.view.Display.DEFAULT_DISPLAY
import android.view.IWindowManager
+import androidx.test.annotation.UiThreadTest
import androidx.test.filters.SmallTest
import com.android.launcher3.util.Executors
+import com.android.launcher3.util.LauncherMultivalentJUnit
import com.android.quickstep.util.GestureExclusionManager.ExclusionListener
import org.junit.Before
import org.junit.Test
@@ -31,11 +32,12 @@
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.reset
import org.mockito.kotlin.verify
-import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.verifyNoMoreInteractions
/** Unit test for [GestureExclusionManager]. */
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@UiThreadTest
+@RunWith(LauncherMultivalentJUnit::class)
class GestureExclusionManagerTest {
@Mock private lateinit var windowManager: IWindowManager
@@ -72,7 +74,7 @@
underTest.addListener(listener2)
awaitTasksCompleted()
- verifyZeroInteractions(windowManager)
+ verifyNoMoreInteractions(windowManager)
}
@Test
@@ -98,7 +100,7 @@
underTest.removeListener(listener1)
awaitTasksCompleted()
- verifyZeroInteractions(windowManager)
+ verifyNoMoreInteractions(windowManager)
}
@Test
@@ -119,12 +121,12 @@
awaitTasksCompleted()
underTest.addListener(listener1)
awaitTasksCompleted()
- verifyZeroInteractions(listener1)
+ verifyNoMoreInteractions(listener1)
underTest.addListener(listener2)
awaitTasksCompleted()
- verifyZeroInteractions(listener1)
+ verifyNoMoreInteractions(listener1)
verify(listener2).onGestureExclusionChanged(r1, r2)
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/GroupTaskTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/GroupTaskTest.kt
index 108cfb5..fa043b9 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/GroupTaskTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/GroupTaskTest.kt
@@ -21,7 +21,6 @@
import android.graphics.Rect
import com.android.launcher3.util.LauncherMultivalentJUnit
import com.android.launcher3.util.SplitConfigurationOptions
-import com.android.quickstep.views.TaskViewType
import com.android.systemui.shared.recents.model.Task
import com.android.wm.shell.shared.split.SplitScreenConstants
import com.google.common.truth.Truth.assertThat
@@ -33,28 +32,28 @@
@Test
fun testGroupTask_sameInstance_isEqual() {
- val task = GroupTask(createTask(1))
+ val task = SingleTask(createTask(1))
assertThat(task).isEqualTo(task)
}
@Test
fun testGroupTask_identicalConstructor_isEqual() {
- val task1 = GroupTask(createTask(1))
- val task2 = GroupTask(createTask(1))
+ val task1 = SingleTask(createTask(1))
+ val task2 = SingleTask(createTask(1))
assertThat(task1).isEqualTo(task2)
}
@Test
fun testGroupTask_copy_isEqual() {
- val task1 = GroupTask(createTask(1))
+ val task1 = SingleTask(createTask(1))
val task2 = task1.copy()
assertThat(task1).isEqualTo(task2)
}
@Test
fun testGroupTask_differentId_isNotEqual() {
- val task1 = GroupTask(createTask(1))
- val task2 = GroupTask(createTask(2))
+ val task1 = SingleTask(createTask(1))
+ val task2 = SingleTask(createTask(2))
assertThat(task1).isNotEqualTo(task2)
}
@@ -66,10 +65,10 @@
Rect(),
1,
2,
- SplitScreenConstants.SNAP_TO_2_50_50
+ SplitScreenConstants.SNAP_TO_2_50_50,
)
- val task1 = GroupTask(createTask(1), createTask(2), splitBounds, TaskViewType.GROUPED)
- val task2 = GroupTask(createTask(1), createTask(2), splitBounds, TaskViewType.GROUPED)
+ val task1 = SplitTask(createTask(1), createTask(2), splitBounds)
+ val task2 = SplitTask(createTask(1), createTask(2), splitBounds)
assertThat(task1).isEqualTo(task2)
}
@@ -81,7 +80,7 @@
Rect(),
1,
2,
- SplitScreenConstants.SNAP_TO_2_50_50
+ SplitScreenConstants.SNAP_TO_2_50_50,
)
val splitBounds2 =
SplitConfigurationOptions.SplitBounds(
@@ -89,17 +88,17 @@
Rect(),
1,
2,
- SplitScreenConstants.SNAP_TO_2_33_66
+ SplitScreenConstants.SNAP_TO_2_33_66,
)
- val task1 = GroupTask(createTask(1), createTask(2), splitBounds1, TaskViewType.GROUPED)
- val task2 = GroupTask(createTask(1), createTask(2), splitBounds2, TaskViewType.GROUPED)
+ val task1 = SplitTask(createTask(1), createTask(2), splitBounds1)
+ val task2 = SplitTask(createTask(1), createTask(2), splitBounds2)
assertThat(task1).isNotEqualTo(task2)
}
@Test
fun testGroupTask_differentType_isNotEqual() {
- val task1 = GroupTask(createTask(1), null, null, TaskViewType.SINGLE)
- val task2 = GroupTask(createTask(1), null, null, TaskViewType.DESKTOP)
+ val task1 = SingleTask(createTask(1))
+ val task2 = DesktopTask(listOf(createTask(1)))
assertThat(task1).isNotEqualTo(task2)
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
index 708273e..0491c07 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
@@ -660,10 +660,16 @@
intent.component = task2ComponentName
taskInfo.baseIntent = intent
task2.key = Task.TaskKey(taskInfo)
- return GroupTask(
+ return SplitTask(
task1,
task2,
- SplitConfigurationOptions.SplitBounds(Rect(), Rect(), -1, -1, SNAP_TO_2_50_50),
+ SplitConfigurationOptions.SplitBounds(
+ /* leftTopBounds = */ Rect(),
+ /* rightBottomBounds = */ Rect(),
+ /* leftTopTaskId = */ -1,
+ /* rightBottomTaskId = */ -1,
+ /* snapPosition = */ SNAP_TO_2_50_50,
+ ),
)
}
@@ -692,10 +698,16 @@
intent.component = task2ComponentName
taskInfo.baseIntent = intent
task2.key = Task.TaskKey(taskInfo)
- return GroupTask(
+ return SplitTask(
task1,
task2,
- SplitConfigurationOptions.SplitBounds(Rect(), Rect(), -1, -1, SNAP_TO_2_50_50),
+ SplitConfigurationOptions.SplitBounds(
+ /* leftTopBounds = */ Rect(),
+ /* rightBottomBounds = */ Rect(),
+ /* leftTopTaskId = */ -1,
+ /* rightBottomTaskId = */ -1,
+ /* snapPosition = */ SNAP_TO_2_50_50,
+ ),
)
}
}
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/launcher3/taskbar/TaskbarHoverToolTipControllerTest.java b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarHoverToolTipControllerTest.java
index 67a0ee4..3f7c85c 100644
--- a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarHoverToolTipControllerTest.java
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarHoverToolTipControllerTest.java
@@ -127,11 +127,11 @@
when(mMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_ENTER);
when(mMotionEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_HOVER_ENTER);
- boolean hoverHandled =
+ boolean hoverConsumed =
mTaskbarHoverToolTipController.onHover(mHoverBubbleTextView, mMotionEvent);
waitForIdleSync();
- assertThat(hoverHandled).isTrue();
+ assertThat(hoverConsumed).isFalse();
verify(taskbarActivityContext).setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS,
true);
}
@@ -141,11 +141,11 @@
when(mMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_EXIT);
when(mMotionEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_HOVER_EXIT);
- boolean hoverHandled =
+ boolean hoverConsumed =
mTaskbarHoverToolTipController.onHover(mHoverBubbleTextView, mMotionEvent);
waitForIdleSync();
- assertThat(hoverHandled).isTrue();
+ assertThat(hoverConsumed).isFalse();
verify(taskbarActivityContext).setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS,
false);
}
@@ -155,11 +155,11 @@
when(mMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_ENTER);
when(mMotionEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_HOVER_ENTER);
- boolean hoverHandled =
+ boolean hoverConsumed =
mTaskbarHoverToolTipController.onHover(mHoverFolderIcon, mMotionEvent);
waitForIdleSync();
- assertThat(hoverHandled).isTrue();
+ assertThat(hoverConsumed).isFalse();
verify(taskbarActivityContext).setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS,
true);
}
@@ -169,11 +169,11 @@
when(mMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_EXIT);
when(mMotionEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_HOVER_EXIT);
- boolean hoverHandled =
+ boolean hoverConsumed =
mTaskbarHoverToolTipController.onHover(mHoverFolderIcon, mMotionEvent);
waitForIdleSync();
- assertThat(hoverHandled).isTrue();
+ assertThat(hoverConsumed).isFalse();
verify(taskbarActivityContext).setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS,
false);
}
@@ -184,11 +184,11 @@
when(mMotionEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_HOVER_EXIT);
doReturn(true).when(mSpyFolderView).isOpen();
- boolean hoverHandled =
+ boolean hoverConsumed =
mTaskbarHoverToolTipController.onHover(mHoverFolderIcon, mMotionEvent);
waitForIdleSync();
- assertThat(hoverHandled).isTrue();
+ assertThat(hoverConsumed).isFalse();
verify(taskbarActivityContext).setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS,
false);
}
@@ -199,10 +199,10 @@
when(mMotionEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_HOVER_ENTER);
doReturn(true).when(mSpyFolderView).isOpen();
- boolean hoverHandled =
+ boolean hoverConsumed =
mTaskbarHoverToolTipController.onHover(mHoverFolderIcon, mMotionEvent);
- assertThat(hoverHandled).isTrue();
+ assertThat(hoverConsumed).isFalse();
}
@Test
@@ -210,10 +210,10 @@
when(mMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_MOVE);
when(mMotionEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_HOVER_MOVE);
- boolean hoverHandled =
+ boolean hoverConsumed =
mTaskbarHoverToolTipController.onHover(mHoverFolderIcon, mMotionEvent);
- assertThat(hoverHandled).isTrue();
+ assertThat(hoverConsumed).isFalse();
}
@Test
@@ -221,11 +221,11 @@
when(mMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_ENTER);
when(mMotionEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_HOVER_ENTER);
- boolean hoverHandled =
+ boolean hoverConsumed =
mTaskbarHoverToolTipController.onHover(mAppPairIcon, mMotionEvent);
waitForIdleSync();
- assertThat(hoverHandled).isTrue();
+ assertThat(hoverConsumed).isFalse();
verify(taskbarActivityContext).setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS,
true);
}
@@ -235,11 +235,11 @@
when(mMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_EXIT);
when(mMotionEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_HOVER_EXIT);
- boolean hoverHandled =
+ boolean hoverConsumed =
mTaskbarHoverToolTipController.onHover(mAppPairIcon, mMotionEvent);
waitForIdleSync();
- assertThat(hoverHandled).isTrue();
+ assertThat(hoverConsumed).isFalse();
verify(taskbarActivityContext).setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS,
false);
}
@@ -250,11 +250,11 @@
when(mMotionEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_HOVER_ENTER);
when(taskbarActivityContext.isIconAlignedWithHotseat()).thenReturn(true);
- boolean hoverHandled =
+ boolean hoverConsumed =
mTaskbarHoverToolTipController.onHover(mHoverBubbleTextView, mMotionEvent);
waitForIdleSync();
- assertThat(hoverHandled).isTrue();
+ assertThat(hoverConsumed).isFalse();
verify(taskbarActivityContext).setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS,
true);
}
diff --git a/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt b/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt
index 94e7c2e..52bd2ea 100644
--- a/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt
@@ -17,27 +17,33 @@
package com.android.quickstep
import android.content.ComponentName
+import android.content.Context
import android.content.Intent
+import android.platform.test.annotations.EnableFlags
+import android.view.Display.DEFAULT_DISPLAY
+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.internal.R
import com.android.launcher3.AbstractFloatingView
import com.android.launcher3.AbstractFloatingViewHelper
import com.android.launcher3.Flags.enableRefactorTaskThumbnail
import com.android.launcher3.logging.StatsLogManager
import com.android.launcher3.logging.StatsLogManager.LauncherEvent
import com.android.launcher3.model.data.TaskViewItemInfo
-import com.android.launcher3.uioverrides.QuickstepLauncher
import com.android.launcher3.util.SplitConfigurationOptions
import com.android.launcher3.util.TransformingTouchDelegate
import com.android.quickstep.TaskOverlayFactory.TaskOverlay
import com.android.quickstep.task.thumbnail.TaskThumbnailView
import com.android.quickstep.views.LauncherRecentsView
+import com.android.quickstep.views.RecentsViewContainer
import com.android.quickstep.views.TaskContainer
import com.android.quickstep.views.TaskThumbnailViewDeprecated
import com.android.quickstep.views.TaskView
import com.android.quickstep.views.TaskViewIcon
import com.android.systemui.shared.recents.model.Task
import com.android.systemui.shared.recents.model.Task.TaskKey
+import com.android.window.flags.Flags
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource
import com.google.common.truth.Truth.assertThat
@@ -57,7 +63,7 @@
/** Test for [DesktopSystemShortcut] */
class DesktopSystemShortcutTest {
- private val launcher: QuickstepLauncher = mock()
+ private val launcher: RecentsViewContainer = mock()
private val statsLogManager: StatsLogManager = mock()
private val statsLogger: StatsLogManager.StatsLogger = mock()
private val recentsView: LauncherRecentsView = mock()
@@ -66,6 +72,7 @@
private val overlayFactory: TaskOverlayFactory = mock()
private val factory: TaskShortcutFactory =
DesktopSystemShortcut.createFactory(abstractFloatingViewHelper)
+ private val context: Context = InstrumentationRegistry.getInstrumentation().targetContext
private lateinit var mockitoSession: StaticMockitoSession
@@ -78,6 +85,7 @@
.startMocking()
whenever(DesktopModeStatus.canEnterDesktopMode(any())).thenReturn(true)
whenever(overlayFactory.createOverlay(any())).thenReturn(mock<TaskOverlay<*>>())
+ whenever(launcher.asContext()).thenReturn(context)
}
@After
@@ -96,6 +104,53 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
+ fun createDesktopTaskShortcutFactory_transparentTask() {
+ val baseComponent = ComponentName("", /* class */ "")
+ val taskKey =
+ TaskKey(
+ /* id */ 1,
+ /* windowingMode */ 0,
+ Intent(),
+ baseComponent,
+ /* userId */ 0,
+ /* lastActiveTime */ 2000,
+ DEFAULT_DISPLAY,
+ baseComponent,
+ /* numActivities */ 1,
+ /* isTopActivityNoDisplay */ false,
+ /* isActivityStackTransparent */ true,
+ )
+ val taskContainer = createTaskContainer(Task(taskKey))
+ val shortcuts = factory.getShortcuts(launcher, taskContainer)
+ assertThat(shortcuts).isNull()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
+ fun createDesktopTaskShortcutFactory_systemUiTask() {
+ val sysUiPackageName: String = context.resources.getString(R.string.config_systemUi)
+ val baseComponent = ComponentName(sysUiPackageName, /* class */ "")
+ val taskKey =
+ TaskKey(
+ /* id */ 1,
+ /* windowingMode */ 0,
+ Intent(),
+ baseComponent,
+ /* userId */ 0,
+ /* lastActiveTime */ 2000,
+ DEFAULT_DISPLAY,
+ baseComponent,
+ /* numActivities */ 1,
+ /* isTopActivityNoDisplay */ false,
+ /* isActivityStackTransparent */ false,
+ )
+ val taskContainer = createTaskContainer(Task(taskKey))
+ val shortcuts = factory.getShortcuts(launcher, taskContainer)
+ assertThat(shortcuts).isNull()
+ }
+
+ @Test
fun createDesktopTaskShortcutFactory_undockable() {
val unDockableTask = createTask().apply { isDockable = false }
val taskContainer = createTaskContainer(unDockableTask)
@@ -113,6 +168,7 @@
whenever(launcher.statsLogManager).thenReturn(statsLogManager)
whenever(statsLogManager.logger()).thenReturn(statsLogger)
whenever(statsLogger.withItemInfo(any())).thenReturn(statsLogger)
+ whenever(taskView.context).thenReturn(context)
whenever(recentsView.moveTaskToDesktop(any(), any(), any())).thenAnswer {
val successCallback = it.getArgument<Runnable>(2)
successCallback.run()
@@ -142,7 +198,22 @@
}
private fun createTask() =
- Task(TaskKey(1, 0, Intent(), ComponentName("", ""), 0, 2000)).apply { isDockable = true }
+ Task(
+ TaskKey(
+ /* id */ 1,
+ /* windowingMode */ 0,
+ Intent(),
+ ComponentName("", ""),
+ /* userId */ 0,
+ /* lastActiveTime */ 2000,
+ DEFAULT_DISPLAY,
+ ComponentName("", ""),
+ /* numActivities */ 1,
+ /* isTopActivityNoDisplay */ false,
+ /* isActivityStackTransparent */ false,
+ )
+ )
+ .apply { isDockable = true }
private fun createTaskContainer(task: Task) =
TaskContainer(
diff --git a/quickstep/tests/src/com/android/quickstep/ExternalDisplaySystemShortcutTest.kt b/quickstep/tests/src/com/android/quickstep/ExternalDisplaySystemShortcutTest.kt
index 9c2c13c..4111dec 100644
--- a/quickstep/tests/src/com/android/quickstep/ExternalDisplaySystemShortcutTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/ExternalDisplaySystemShortcutTest.kt
@@ -17,23 +17,27 @@
package com.android.quickstep
import android.content.ComponentName
+import android.content.Context
import android.content.Intent
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
+import android.view.Display.DEFAULT_DISPLAY
+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.internal.R
import com.android.launcher3.AbstractFloatingView
import com.android.launcher3.AbstractFloatingViewHelper
import com.android.launcher3.Flags.enableRefactorTaskThumbnail
import com.android.launcher3.logging.StatsLogManager
import com.android.launcher3.logging.StatsLogManager.LauncherEvent
import com.android.launcher3.model.data.TaskViewItemInfo
-import com.android.launcher3.uioverrides.QuickstepLauncher
import com.android.launcher3.util.SplitConfigurationOptions
import com.android.launcher3.util.TransformingTouchDelegate
import com.android.quickstep.TaskOverlayFactory.TaskOverlay
import com.android.quickstep.task.thumbnail.TaskThumbnailView
import com.android.quickstep.views.LauncherRecentsView
+import com.android.quickstep.views.RecentsViewContainer
import com.android.quickstep.views.TaskContainer
import com.android.quickstep.views.TaskThumbnailViewDeprecated
import com.android.quickstep.views.TaskView
@@ -62,7 +66,7 @@
@get:Rule val setFlagsRule = SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT)
- private val launcher: QuickstepLauncher = mock()
+ private val launcher: RecentsViewContainer = mock()
private val statsLogManager: StatsLogManager = mock()
private val statsLogger: StatsLogManager.StatsLogger = mock()
private val recentsView: LauncherRecentsView = mock()
@@ -71,6 +75,7 @@
private val overlayFactory: TaskOverlayFactory = mock()
private val factory: TaskShortcutFactory =
ExternalDisplaySystemShortcut.createFactory(abstractFloatingViewHelper)
+ private val context: Context = InstrumentationRegistry.getInstrumentation().targetContext
private lateinit var mockitoSession: StaticMockitoSession
@@ -83,6 +88,7 @@
.startMocking()
whenever(DesktopModeStatus.canEnterDesktopMode(any())).thenReturn(true)
whenever(overlayFactory.createOverlay(any())).thenReturn(mock<TaskOverlay<*>>())
+ whenever(launcher.asContext()).thenReturn(context)
}
@After
@@ -102,6 +108,59 @@
}
@Test
+ @EnableFlags(
+ Flags.FLAG_MOVE_TO_EXTERNAL_DISPLAY_SHORTCUT,
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
+ )
+ fun createExternalDisplayTaskShortcut_transparentTask() {
+ val baseComponent = ComponentName("", /* class */ "")
+ val taskKey =
+ TaskKey(
+ /* id */ 1,
+ /* windowingMode */ 0,
+ Intent(),
+ baseComponent,
+ /* userId */ 0,
+ /* lastActiveTime */ 2000,
+ DEFAULT_DISPLAY,
+ baseComponent,
+ /* numActivities */ 1,
+ /* isTopActivityNoDisplay */ false,
+ /* isActivityStackTransparent */ true,
+ )
+ val taskContainer = createTaskContainer(Task(taskKey))
+ val shortcuts = factory.getShortcuts(launcher, taskContainer)
+ assertThat(shortcuts).isNull()
+ }
+
+ @Test
+ @EnableFlags(
+ Flags.FLAG_MOVE_TO_EXTERNAL_DISPLAY_SHORTCUT,
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
+ )
+ fun createExternalDisplayTaskShortcut_systemUiTask() {
+ val sysUiPackageName: String = context.resources.getString(R.string.config_systemUi)
+ val baseComponent = ComponentName(sysUiPackageName, /* class */ "")
+ val taskKey =
+ TaskKey(
+ /* id */ 1,
+ /* windowingMode */ 0,
+ Intent(),
+ baseComponent,
+ /* userId */ 0,
+ /* lastActiveTime */ 2000,
+ DEFAULT_DISPLAY,
+ baseComponent,
+ /* numActivities */ 1,
+ /* isTopActivityNoDisplay */ false,
+ /* isActivityStackTransparent */ false,
+ )
+ val taskContainer = createTaskContainer(Task(taskKey))
+ val shortcuts = factory.getShortcuts(launcher, taskContainer)
+ assertThat(shortcuts).isNull()
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_MOVE_TO_EXTERNAL_DISPLAY_SHORTCUT)
fun externalDisplaySystemShortcutClicked() {
val task = createTask()
@@ -134,7 +193,22 @@
verify(statsLogger).log(LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_EXTERNAL_DISPLAY_TAP)
}
- private fun createTask() = Task(TaskKey(1, 0, Intent(), ComponentName("", ""), 0, 2000))
+ private fun createTask() =
+ Task(
+ TaskKey(
+ /* id */ 1,
+ /* windowingMode */ 0,
+ Intent(),
+ ComponentName("", ""),
+ /* userId */ 0,
+ /* lastActiveTime */ 2000,
+ DEFAULT_DISPLAY,
+ ComponentName("", ""),
+ /* numActivities */ 1,
+ /* isTopActivityNoDisplay */ false,
+ /* isActivityStackTransparent */ false,
+ )
+ )
private fun createTaskContainer(task: Task) =
TaskContainer(
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..e2ca91a 100644
--- a/quickstep/tests/src/com/android/quickstep/InputConsumerUtilsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/InputConsumerUtilsTest.java
@@ -16,8 +16,6 @@
package com.android.quickstep;
-import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
-
import static com.android.quickstep.InputConsumerUtils.newBaseConsumer;
import static com.android.quickstep.InputConsumerUtils.newConsumer;
@@ -40,6 +38,9 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.anim.AnimatedFloat;
+import com.android.launcher3.dagger.LauncherAppComponent;
+import com.android.launcher3.dagger.LauncherAppModule;
+import com.android.launcher3.dagger.LauncherAppSingleton;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarManager;
import com.android.launcher3.taskbar.bubbles.BubbleBarController;
@@ -54,7 +55,7 @@
import com.android.launcher3.taskbar.bubbles.BubbleStashedHandleViewController;
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController;
import com.android.launcher3.util.LockedUserState;
-import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.SandboxApplication;
import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.inputconsumers.AccessibilityInputConsumer;
import com.android.quickstep.inputconsumers.BubbleBarInputConsumer;
@@ -75,6 +76,9 @@
import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.shared.system.InputMonitorCompat;
+import dagger.BindsInstance;
+import dagger.Component;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -93,8 +97,8 @@
@RunWith(AndroidJUnit4.class)
public class InputConsumerUtilsTest {
- @NonNull private final MainThreadInitializedObject.SandboxContext mContext =
- new MainThreadInitializedObject.SandboxContext(getApplicationContext());
+ @Rule public final SandboxApplication mContext = new SandboxApplication();
+
@NonNull private final InputMonitorCompat mInputMonitorCompat = new InputMonitorCompat("", 0);
private TaskAnimationManager mTaskAnimationManager;
@@ -125,8 +129,12 @@
}
@Before
- public void setupMainThreadInitializedObjects() {
- mContext.putObject(LockedUserState.INSTANCE, mLockedUserState);
+ public void setupDaggerGraphOverrides() {
+ mContext.initDaggerComponent(DaggerInputConsumerUtilsTest_TestComponent
+ .builder()
+ .bindLockedState(mLockedUserState)
+ .bindRotationHelper(mock(RotationTouchHelper.class))
+ .bindRecentsState(mDeviceState));
}
@Before
@@ -193,7 +201,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
@@ -594,4 +601,18 @@
return bubbleControllers;
}
+
+ @LauncherAppSingleton
+ @Component(modules = {LauncherAppModule.class})
+ interface TestComponent extends LauncherAppComponent {
+ @Component.Builder
+ interface Builder extends LauncherAppComponent.Builder {
+ @BindsInstance Builder bindLockedState(LockedUserState state);
+ @BindsInstance Builder bindRotationHelper(RotationTouchHelper helper);
+ @BindsInstance Builder bindRecentsState(RecentsAnimationDeviceState state);
+
+ @Override
+ TestComponent build();
+ }
+ }
}
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/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/TaplTestsLockedTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsLockedTaskbar.java
new file mode 100644
index 0000000..8fedf5c
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsLockedTaskbar.java
@@ -0,0 +1,99 @@
+/*
+ * 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 static android.view.Display.DEFAULT_DISPLAY;
+
+import static androidx.test.InstrumentationRegistry.getTargetContext;
+
+import static com.android.quickstep.TaskbarModeSwitchRule.Mode.PERSISTENT;
+import static com.android.wm.shell.shared.desktopmode.DesktopModeStatus.ENTER_DESKTOP_BY_DEFAULT_ON_FREEFORM_DISPLAY_SYS_PROP;
+
+import android.app.WindowConfiguration;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.WindowManagerGlobal;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
+import com.android.launcher3.util.rule.SetPropRule;
+import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
+import com.android.quickstep.TaskbarModeSwitchRule.TaskbarModeSwitch;
+import com.android.window.flags.Flags;
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
+
+import org.junit.Assume;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class TaplTestsLockedTaskbar extends AbstractTaplTestsTaskbar {
+ private static final String TAG = "TaplTestsLockedTaskbar";
+
+ @Rule
+ public SetPropRule mSetPropRule =
+ new SetPropRule(ENTER_DESKTOP_BY_DEFAULT_ON_FREEFORM_DISPLAY_SYS_PROP, "true");
+
+ @Override
+ public void setUp() throws Exception {
+ Assume.assumeTrue(mLauncher.isTablet());
+ Assume.assumeTrue(Flags.enterDesktopByDefaultOnFreeformDisplays());
+ Assume.assumeTrue(DesktopModeStatus.canEnterDesktopMode(getTargetContext()));
+ super.setUp();
+
+ // Default-to-desktop feature requires the display to be freeform mode.
+ setDisplayWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ // Reset the display windowing mode to the device default.
+ setDisplayWindowingMode(WindowConfiguration.WINDOWING_MODE_UNDEFINED);
+
+ mLauncher.recreateTaskbar();
+
+ super.tearDown();
+ }
+
+ @Test
+ @PortraitLandscape
+ @NavigationModeSwitch
+ @TaskbarModeSwitch(mode = PERSISTENT)
+ public void testTaskbarVisibility() {
+ // The taskbar should be visible on home.
+ mDevice.pressHome();
+ waitForResumed("Launcher internal state is still Background");
+ mLauncher.getLaunchedAppState().assertTaskbarVisible();
+
+ // The taskbar should be visible when a freeform task is active.
+ startAppFast(CALCULATOR_APP_PACKAGE);
+ mLauncher.getLaunchedAppState().assertTaskbarVisible();
+ }
+
+ private void setDisplayWindowingMode(int windowingMode) {
+ try {
+ WindowManagerGlobal.getWindowManagerService().setWindowingMode(
+ DEFAULT_DISPLAY, windowingMode);
+ } catch (RemoteException e) {
+ Log.e(TAG, "error setting windowing mode", e);
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsOverviewDesktop.kt b/quickstep/tests/src/com/android/quickstep/TaplTestsOverviewDesktop.kt
index f58c84e..75947ab 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) }
}
@@ -113,25 +122,25 @@
val desktop = moveTaskToDesktop(TEST_ACTIVITY_EXTRA)
var overview = desktop.switchToOverview()
- // Open focused task and go back to Overview to validate whether it has adjacent tasks in
- // its both sides (grid task on left and desktop tasks at its right side)
- val focusedTaskOpened = overview.getTestActivityTask(TEST_ACTIVITY_2).open()
+ // Open first fullscreen task and go back to Overview to validate whether it has adjacent
+ // tasks in its both sides (grid task on left and desktop tasks at its right side)
+ val firstFullscreenTaskOpened = overview.getTestActivityTask(TEST_ACTIVITY_2).open()
- // Fling to desktop task and dismiss the focused task to check repositioning of
+ // Fling to desktop task and dismiss the first fullscreen task to check repositioning of
// grid tasks.
- overview = focusedTaskOpened.switchToOverview().apply { flingBackward() }
+ overview = firstFullscreenTaskOpened.switchToOverview().apply { flingBackward() }
val desktopTask = overview.currentTask
assertWithMessage("The current task is not a Desktop.").that(desktopTask.isDesktop).isTrue()
- // Get focused task (previously opened task) then dismiss this task
- val focusedTaskInOverview = overview.getTestActivityTask(TEST_ACTIVITY_2)
- assertTaskContentDescription(focusedTaskInOverview, TEST_ACTIVITY_2)
- focusedTaskInOverview.dismiss()
+ // Get first fullscreen task (previously opened task) then dismiss this task
+ val firstFullscreenTaskInOverview = overview.getTestActivityTask(TEST_ACTIVITY_2)
+ assertTaskContentDescription(firstFullscreenTaskInOverview, TEST_ACTIVITY_2)
+ firstFullscreenTaskInOverview.dismiss()
- // Dismiss DesktopTask to validate whether the new focused task will take its position
+ // Dismiss DesktopTask to validate whether the new task will take its position
desktopTask.dismiss()
- // Dismiss last focused task
+ // Dismiss last fullscreen task
val lastFocusedTask = overview.currentTask
assertTaskContentDescription(lastFocusedTask, TEST_ACTIVITY_1)
lastFocusedTask.dismiss()
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 961dc58..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;
@@ -584,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..37ac4a0 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;
@@ -32,7 +30,6 @@
import com.android.launcher3.tapl.Taskbar;
import com.android.launcher3.tapl.TaskbarAppIcon;
import com.android.quickstep.util.SplitScreenTestUtils;
-import com.android.wm.shell.Flags;
import org.junit.After;
import org.junit.Before;
@@ -89,25 +86,14 @@
.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
public void testSaveAppPairMenuItemOrActionExistsOnSplitPair() {
- assumeTrue("App pairs feature is currently not enabled, no test needed",
- Flags.enableAppPairs());
-
Overview overview = SplitScreenTestUtils.createAndLaunchASplitPairInOverview(mLauncher);
if (mLauncher.isGridOnlyOverviewEnabled() || !mLauncher.isTablet()) {
@@ -120,9 +106,6 @@
@Test
public void testSaveAppPairMenuItemDoesNotExistOnSingleTask() throws Exception {
- assumeTrue("App pairs feature is currently not enabled, no test needed",
- Flags.enableAppPairs());
-
startAppFast(CALCULATOR_APP_PACKAGE);
assertFalse("Save app pair menu item is erroneously appearing on single task",
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/res/drawable/ic_desktop_add.xml b/res/drawable/ic_desktop_add.xml
index fa5e0de..d31b04b 100644
--- a/res/drawable/ic_desktop_add.xml
+++ b/res/drawable/ic_desktop_add.xml
@@ -14,8 +14,8 @@
~ limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
+ android:width="24dp"
+ android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index fcc442f..ef34ea7 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 d0abd7d..0d1d350 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 78f36dd..a925866 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 f15ef93..7125153 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 a0aef39..7905400 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 9983552..d1ecaeb 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 c11a480..3c579cb 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 27830d7..6d44a97 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 f983b96..b070133 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 79d3614..8b7033f 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 c4c2dbd..c5daecc 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 8b426f1..2ac7233 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 c6c78c5..7979c20 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 19932b3..098c9c1 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 f7b3b2d..a1e822a 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 449de5d..5a92bf4 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 e5d50af..a3d0e0b 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 449de5d..5a92bf4 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 449de5d..5a92bf4 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 a2f4c6e..1d337e0 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 ef612e7..0dbcbe5 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 38384c0..b51feb5 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 ab9cb24..1fcd004 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 6422a50..c45d372 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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>
@@ -191,13 +189,13 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"کاری"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"نمایه کاری"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"برنامههای کاری نشاندار هستند و سرپرست فناوری اطلاعات میتواند آنها را ببیند"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"متوجهام"</string>
+ <string name="work_profile_edu_accept" msgid="6069788082535149071">"متوجهم"</string>
<string name="work_apps_paused_title" msgid="3040901117349444598">"برنامههای کاری موقتاً متوقف شدهاند."</string>
<string name="work_apps_paused_info_body" msgid="1687828929959237477">"از برنامههای کاریتان اعلان دریافت نخواهید کرد"</string>
<string name="work_apps_paused_body" msgid="261634750995824906">"برنامههای کاری نمیتوانند برای شما اعلان ارسال کنند، از باتری استفاده کنند، یا به مکانتان دسترسی داشته باشند"</string>
<string name="work_apps_paused_telephony_unavailable_body" msgid="8358872357502756790">"از برنامههای کاریتان تماس تلفنی، پیام نوشتاری، یا اعلان دریافت نخواهید کرد"</string>
<string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"برنامههای کاری نشاندار هستند و سرپرست فناوری اطلاعات میتواند آنها را ببیند."</string>
- <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"متوجهام"</string>
+ <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"متوجهم"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"توقف موقت برنامههای کاری"</string>
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"ازسرگیری"</string>
<string name="work_scheduler_button_content_description" msgid="917340740986764967">"برنامه زمانی برنامههای کاری"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index a442886..2c43a25 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 dff43d0..c191aae 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 1c6f1f9..e4c40e5 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 4394220..4ded582 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 4a23a7c..4925294 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 bfe1e2c..db2b601 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 832203b..d859f58 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 3c74b4d..9f4ac5a 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 da96e70..036e9c9 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 e2429f3..2ba2997 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 b028a5b..9316f90 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 5567b8e..75e7cd2 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -125,8 +125,7 @@
<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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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 schermata Home"</string>
@@ -164,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 4b0e42c..ec6da15 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -68,14 +68,14 @@
<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>
@@ -110,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>
@@ -119,14 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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>
@@ -204,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 7caa8d3..3d42652 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 7d97100..6a6d30d 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 a22aab0..4f457ee 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 d031e27..6f3fb5c 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 91969b0..5cb996c 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 86ced7f..e5cae63 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 8bc1404..b6b5b66 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 a493800..457c185 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 2583586..b23b97b 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 ccb9459..857b492 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 b7b72f0..450d5d6 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 dc533ec..4d65869 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 3c5d6c0..9e40f08 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 a60ef67..8907bcf 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 571916d..90245af 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 3a5c195..d6410da 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 7cec476..bdce1f4 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 97ad23c..78b566a 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 89afdcc..d771c6f 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 1859ba3..3031259 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 48bad2e..ac5f3be 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 2422acb..754bd76 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 0e62d5f..701bb25 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 1ec575c..dc792f1 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 1741157..03e26e1 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 0ea1595..b2dd767 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 7d17d94..fe27579 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 1b54613..7410051 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 6983baf..7016df5 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 0aea763..eb6660c 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 fe3e12f..e97e39b 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 e95e361..ad5e281 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 ef7d18e..bd0029c 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 c10398d..30dbbb3 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 7cf981c..b6563b0 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 e7f63bb..f5fd93d 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 758f786..4d9a340 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 912738e..cb47400 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 4d810d6..fd3855d 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 603088b..ed002ba 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 f1892b3..f96b6a2 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 fb401c3..1d886b2 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 1833c54..b2d2a1b 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 7b6778e..5a573a1 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 67b9810..6cf783c 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 e6af3f1..58d1188 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -125,8 +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>
- <!-- no translation found for unnamed_folder (2420192029474044442) -->
- <skip />
+ <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>
@@ -164,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 501e650..a02516a 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -408,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..0583d6d 100644
--- a/shared/src/com/android/launcher3/testing/shared/TestProtocol.java
+++ b/shared/src/com/android/launcher3/testing/shared/TestProtocol.java
@@ -169,7 +169,7 @@
public static final String PERMANENT_DIAG_TAG = "TaplTarget";
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/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index 76c0f90..753b2e2 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -75,7 +75,8 @@
TYPE_ADD_TO_HOME_CONFIRMATION,
TYPE_TASKBAR_OVERLAY_PROXY,
TYPE_TASKBAR_PINNING_POPUP,
- TYPE_PIN_IME_POPUP
+ TYPE_PIN_IME_POPUP,
+ TYPE_ONE_GRID_MIGRATION_EDU,
})
@Retention(RetentionPolicy.SOURCE)
public @interface FloatingViewType {}
@@ -104,6 +105,7 @@
public static final int TYPE_TASKBAR_OVERLAY_PROXY = 1 << 20;
public static final int TYPE_TASKBAR_PINNING_POPUP = 1 << 21;
public static final int TYPE_PIN_IME_POPUP = 1 << 22;
+ public static final int TYPE_ONE_GRID_MIGRATION_EDU = 1 << 23;
public static final int TYPE_ALL = TYPE_FOLDER | TYPE_ACTION_POPUP
| TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_WIDGETS_FULL_SHEET
@@ -112,19 +114,19 @@
| TYPE_ICON_SURFACE | TYPE_DRAG_DROP_POPUP | TYPE_PIN_WIDGET_FROM_EXTERNAL_POPUP
| TYPE_TASKBAR_EDUCATION_DIALOG | TYPE_TASKBAR_ALL_APPS | TYPE_OPTIONS_POPUP_DIALOG
| TYPE_ADD_TO_HOME_CONFIRMATION | TYPE_TASKBAR_OVERLAY_PROXY
- | TYPE_TASKBAR_PINNING_POPUP | TYPE_PIN_IME_POPUP;
+ | TYPE_TASKBAR_PINNING_POPUP | TYPE_PIN_IME_POPUP | TYPE_ONE_GRID_MIGRATION_EDU;
// Type of popups which should be kept open during launcher rebind
public static final int TYPE_REBIND_SAFE = TYPE_WIDGETS_FULL_SHEET
| TYPE_WIDGETS_BOTTOM_SHEET | TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE
| TYPE_ALL_APPS_EDU | TYPE_ICON_SURFACE | TYPE_TASKBAR_EDUCATION_DIALOG
| TYPE_TASKBAR_ALL_APPS | TYPE_OPTIONS_POPUP_DIALOG | TYPE_TASKBAR_OVERLAY_PROXY
- | TYPE_PIN_IME_POPUP;
+ | TYPE_PIN_IME_POPUP | TYPE_ONE_GRID_MIGRATION_EDU;
/** Type of popups that should get exclusive accessibility focus. */
public static final int TYPE_ACCESSIBLE = TYPE_ALL & ~TYPE_DISCOVERY_BOUNCE & ~TYPE_LISTENER
& ~TYPE_ALL_APPS_EDU & ~TYPE_TASKBAR_ALL_APPS & ~TYPE_PIN_IME_POPUP
- & ~TYPE_WIDGET_RESIZE_FRAME;
+ & ~TYPE_WIDGET_RESIZE_FRAME & ~TYPE_ONE_GRID_MIGRATION_EDU & ~TYPE_ON_BOARD_POPUP;
// These view all have particular operation associated with swipe down interaction.
public static final int TYPE_STATUS_BAR_SWIPE_DOWN_DISALLOW = TYPE_WIDGETS_BOTTOM_SHEET |
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
index 175d6ec..468cee8 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -261,6 +261,13 @@
return count;
}
+ private void addProfileId(XmlPullParser parser) {
+ Long profileId = mUserTypeToSerial.get(getAttributeValue(parser, ATTR_USER_TYPE));
+ if (profileId != null) {
+ mValues.put(Favorites.PROFILE_ID, profileId);
+ }
+ }
+
/**
* Parses container and screenId attribute from the current tag, and puts it in the out.
* @param out array of size 2.
@@ -305,10 +312,6 @@
convertToDistanceFromEnd(getAttributeValue(parser, ATTR_X), mColumnCount));
mValues.put(Favorites.CELLY,
convertToDistanceFromEnd(getAttributeValue(parser, ATTR_Y), mRowCount));
- Long profileId = mUserTypeToSerial.get(getAttributeValue(parser, ATTR_USER_TYPE));
- if (profileId != null) {
- mValues.put(Favorites.PROFILE_ID, profileId);
- }
TagParser tagParser = tagParserMap.get(parser.getName());
if (tagParser == null) {
@@ -382,7 +385,7 @@
public int parseAndAdd(XmlPullParser parser) {
final String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME);
final String className = getAttributeValue(parser, ATTR_CLASS_NAME);
-
+ addProfileId(parser);
if (!TextUtils.isEmpty(packageName) && !TextUtils.isEmpty(className)) {
ActivityInfo info;
try {
@@ -431,6 +434,7 @@
public int parseAndAdd(XmlPullParser parser) {
final String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME);
final String className = getAttributeValue(parser, ATTR_CLASS_NAME);
+ addProfileId(parser);
if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(className)) {
if (LOGD) Log.d(TAG, "Skipping invalid <favorite> with no component");
return -1;
@@ -452,7 +456,7 @@
public int parseAndAdd(XmlPullParser parser) {
final String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME);
final String shortcutId = getAttributeValue(parser, ATTR_SHORTCUT_ID);
-
+ addProfileId(parser);
try {
LauncherApps launcherApps = mContext.getSystemService(LauncherApps.class);
launcherApps.pinShortcuts(packageName, Collections.singletonList(shortcutId),
@@ -482,13 +486,13 @@
public ComponentName getComponentName(XmlPullParser parser) {
final String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME);
final String className = getAttributeValue(parser, ATTR_CLASS_NAME);
+ addProfileId(parser);
if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(className)) {
return null;
}
return new ComponentName(packageName, className);
}
-
@Override
public int parseAndAdd(XmlPullParser parser)
throws XmlPullParserException, IOException {
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index da73280..315096c 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -75,6 +75,7 @@
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.graphics.IconShape;
import com.android.launcher3.graphics.PreloadIconDrawable;
+import com.android.launcher3.graphics.ThemeManager;
import com.android.launcher3.icons.DotRenderer;
import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
@@ -448,7 +449,9 @@
@VisibleForTesting
@UiThread
public void applyIconAndLabel(ItemInfoWithIcon info) {
- int flags = shouldUseTheme() ? FLAG_THEMED : 0;
+ ThemeManager themeManager = ThemeManager.INSTANCE.get(getContext());
+ int flags = (shouldUseTheme()
+ && themeManager.isMonoThemeEnabled()) ? FLAG_THEMED : 0;
// Remove badge on icons smaller than 48dp.
if (mHideBadge || mDisplay == DISPLAY_SEARCH_RESULT_SMALL) {
flags |= FLAG_NO_BADGE;
@@ -464,8 +467,8 @@
}
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;
}
/**
@@ -1236,6 +1239,7 @@
mIcon = icon;
if (mIcon != null) {
mIcon.setVisible(getWindowVisibility() == VISIBLE && isShown(), false);
+ mIcon.setHoverScaleEnabledForDisplay(mDisplay != DISPLAY_TASKBAR);
}
}
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 714c8b4..988d164 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -532,9 +532,11 @@
for (int i = 0; i < mDelegatedCellDrawings.size(); i++) {
DelegatedCellDrawing cellDrawing = mDelegatedCellDrawings.get(i);
- cellToPoint(cellDrawing.mDelegateCellX, cellDrawing.mDelegateCellY, mTempLocation);
canvas.save();
- canvas.translate(mTempLocation[0], mTempLocation[1]);
+ if (cellDrawing.mDelegateCellX >= 0 && cellDrawing.mDelegateCellY >= 0) {
+ cellToPoint(cellDrawing.mDelegateCellX, cellDrawing.mDelegateCellY, mTempLocation);
+ canvas.translate(mTempLocation[0], mTempLocation[1]);
+ }
cellDrawing.drawUnderItem(canvas);
canvas.restore();
}
@@ -663,9 +665,11 @@
for (int i = 0; i < mDelegatedCellDrawings.size(); i++) {
DelegatedCellDrawing bg = mDelegatedCellDrawings.get(i);
- cellToPoint(bg.mDelegateCellX, bg.mDelegateCellY, mTempLocation);
canvas.save();
- canvas.translate(mTempLocation[0], mTempLocation[1]);
+ if (bg.mDelegateCellX >= 0 && bg.mDelegateCellY >= 0) {
+ cellToPoint(bg.mDelegateCellX, bg.mDelegateCellY, mTempLocation);
+ canvas.translate(mTempLocation[0], mTempLocation[1]);
+ }
bg.drawOverItem(canvas);
canvas.restore();
}
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 9ebae9f..9f47da7 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -26,7 +26,6 @@
import static com.android.launcher3.Utilities.dpiFromPx;
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;
import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
import static com.android.launcher3.testing.shared.ResourceUtils.INVALID_RESOURCE_HANDLE;
import static com.android.launcher3.testing.shared.ResourceUtils.pxFromDp;
@@ -53,6 +52,7 @@
import com.android.launcher3.CellLayout.ContainerType;
import com.android.launcher3.DevicePaddings.DevicePadding;
+import com.android.launcher3.graphics.IconShape;
import com.android.launcher3.icons.DotRenderer;
import com.android.launcher3.icons.IconNormalizer;
import com.android.launcher3.model.data.ItemInfo;
@@ -792,8 +792,7 @@
int leftRightSplitPortraitResId = Resources.getSystem().getIdentifier(
"config_leftRightSplitInPortrait", "bool", "android");
boolean allowLeftRightSplitInPortrait =
- com.android.wm.shell.Flags.enableLeftRightSplitInPortrait()
- && leftRightSplitPortraitResId > 0
+ leftRightSplitPortraitResId > 0
&& res.getBoolean(leftRightSplitPortraitResId);
if (allowLeftRightSplitInPortrait && isTablet) {
isLeftRightSplit = !isLandscape;
@@ -872,7 +871,9 @@
@NonNull Context context, int size, @NonNull SparseArray<DotRenderer> cache) {
DotRenderer renderer = cache.get(size);
if (renderer == null) {
- renderer = new DotRenderer(size, getShapePath(context, DEFAULT_DOT_SIZE),
+ renderer = new DotRenderer(
+ size,
+ IconShape.INSTANCE.get(context).getShapeOverridePath(DEFAULT_DOT_SIZE),
DEFAULT_DOT_SIZE);
cache.put(size, renderer);
}
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 5cca990..753e017 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -685,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]);
}
@@ -694,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)) {
@@ -709,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};
}
/**
@@ -1246,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;
@@ -1258,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 f68c8e0..7df4014 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1657,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())) {
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 5989e4c..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;
@@ -38,18 +32,17 @@
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;
@@ -64,7 +57,6 @@
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;
@@ -108,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);
@@ -156,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);
@@ -255,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/LauncherPrefs.kt b/src/com/android/launcher3/LauncherPrefs.kt
index ad592d8..d8bb84e 100644
--- a/src/com/android/launcher3/LauncherPrefs.kt
+++ b/src/com/android/launcher3/LauncherPrefs.kt
@@ -34,7 +34,6 @@
import com.android.launcher3.states.RotationHelper
import com.android.launcher3.util.DaggerSingletonObject
import com.android.launcher3.util.DisplayController
-import com.android.launcher3.util.Themes
import javax.inject.Inject
/**
@@ -235,13 +234,9 @@
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
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..d93c07f 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;
@@ -659,7 +659,8 @@
// Only fetch badge if the icon is on workspace
if (info.id != ItemInfo.NO_ID && badge == null) {
badge = appState.getIconCache().getShortcutInfoBadge(si)
- .newIcon(context, FLAG_THEMED);
+ .newIcon(context, ThemeManager.INSTANCE.get(context)
+ .isMonoThemeEnabled() ? FLAG_THEMED : 0);
}
}
} else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
@@ -681,7 +682,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 +691,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 bc751d9..86c49d0 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -109,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;
@@ -2241,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) {
@@ -3554,7 +3572,7 @@
int nScreens = getChildCount();
int extraScreenId = mScreenOrder.indexOf(EXTRA_EMPTY_SCREEN_ID);
if (extraScreenId >= 0 && nScreens > 1) {
- if (page == extraScreenId) {
+ if (page == extraScreenId || (isTwoPanelEnabled() && page == extraScreenId + 1)) {
return getContext().getString(R.string.workspace_new_page);
}
nScreens--;
@@ -3566,6 +3584,11 @@
int panelCount = getPanelCount();
int currentPage = (page / panelCount) + 1;
int totalPages = nScreens / panelCount + nScreens % panelCount;
+
+ // When dragging, a blank screen is added. This increases the total page count, but we still
+ // want to describe the original page count where icons are currently pinned
+ if (extraScreenId > 0) totalPages--;
+
return getContext().getString(R.string.workspace_scroll_format, currentPage, totalPages);
}
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/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..44dcc06 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -144,15 +144,6 @@
return ENABLE_TASKBAR_PINNING.get() || Flags.enableTaskbarPinning();
}
- // Aconfig migration complete for ENABLE_APP_PAIRS.
- public static final BooleanFlag ENABLE_APP_PAIRS = getDebugFlag(274189428,
- "ENABLE_APP_PAIRS", DISABLED,
- "Enables the ability to create and save app pairs on the Home screen for easy"
- + " split screen launching.");
- public static boolean enableAppPairs() {
- return ENABLE_APP_PAIRS.get() || com.android.wm.shell.Flags.enableAppPairs();
- }
-
// TODO(Block 20): Clean up flags
// Aconfig migration complete for ENABLE_HOME_TRANSITION_LISTENER.
public static final BooleanFlag ENABLE_HOME_TRANSITION_LISTENER = getDebugFlag(306053414,
@@ -201,15 +192,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 74%
rename from quickstep/dagger/com/android/launcher3/dagger/LauncherAppModule.java
rename to src/com/android/launcher3/dagger/LauncherAppModule.java
index 1711fc1..c58a414 100644
--- a/quickstep/dagger/com/android/launcher3/dagger/LauncherAppModule.java
+++ b/src/com/android/launcher3/dagger/LauncherAppModule.java
@@ -16,9 +16,14 @@
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,
+ StaticObjectModule.class,
+ AppModule.class
+})
+public class LauncherAppModule {
+}
diff --git a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
index 72a97a8..0b7b20f 100644
--- a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
+++ b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
@@ -19,19 +19,22 @@
import android.content.Context;
import com.android.launcher3.LauncherPrefs;
-import com.android.launcher3.contextualeducation.ContextualEduStatsManager;
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.LockedUserState;
import com.android.launcher3.util.MSDLPlayerWrapper;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.PluginManagerWrapper;
import com.android.launcher3.util.ScreenOnTracker;
import com.android.launcher3.util.SettingsCache;
import com.android.launcher3.util.VibratorWrapper;
+import com.android.launcher3.util.WallpaperColorHints;
import com.android.launcher3.util.window.RefreshRateTracker;
import com.android.launcher3.util.window.WindowManagerProxy;
import com.android.launcher3.widget.custom.CustomWidgetManager;
@@ -49,7 +52,6 @@
public interface LauncherBaseAppComponent {
DaggerSingletonTracker getDaggerSingletonTracker();
ApiWrapper getApiWrapper();
- ContextualEduStatsManager getContextualEduStatsManager();
CustomWidgetManager getCustomWidgetManager();
DynamicResource getDynamicResource();
IconShape getIconShape();
@@ -64,6 +66,10 @@
MSDLPlayerWrapper getMSDLPlayerWrapper();
WindowManagerProxy getWmProxy();
LauncherPrefs getLauncherPrefs();
+ ThemeManager getThemeManager();
+ DisplayController getDisplayController();
+ WallpaperColorHints getWallpaperColorHints();
+ LockedUserState getLockedUserState();
/** 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..9c82748 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -60,6 +60,7 @@
import com.android.app.animation.Interpolators;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.graphics.IconShape;
import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.data.ItemInfo;
@@ -264,8 +265,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
@@ -273,7 +273,7 @@
Rect shrunkBounds = new Rect(bounds);
Utilities.scaleRectAboutCenter(shrunkBounds, 0.98f);
adaptiveIcon.setBounds(shrunkBounds);
- final Path mask = adaptiveIcon.getIconMask();
+ final Path mask = IconShape.INSTANCE.get(getContext()).getShapeOverridePath(w);
mTranslateX = new SpringFloatValue(DragView.this,
w * AdaptiveIconDrawable.getExtraInsetFraction());
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/FolderNameProvider.java b/src/com/android/launcher3/folder/FolderNameProvider.java
index be5f8f7..8a1f96d 100644
--- a/src/com/android/launcher3/folder/FolderNameProvider.java
+++ b/src/com/android/launcher3/folder/FolderNameProvider.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.folder;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_FOLDER;
+
import android.annotation.SuppressLint;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
@@ -37,6 +39,7 @@
import com.android.launcher3.model.StringCache;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.CollectionInfo;
+import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.util.IntSparseArrayMap;
import com.android.launcher3.util.Preconditions;
@@ -197,9 +200,18 @@
@Override
public void execute(@NonNull ModelTaskController taskController,
@NonNull BgDataModel dataModel, @NonNull AllAppsList apps) {
- mCollectionInfos = dataModel.collections.clone();
+ mCollectionInfos = getCollectionForSuggestions(dataModel);
mAppInfos = Arrays.asList(apps.copyData());
}
}
+ public static IntSparseArrayMap<CollectionInfo> getCollectionForSuggestions(
+ BgDataModel dataModel) {
+ IntSparseArrayMap<CollectionInfo> result = new IntSparseArrayMap<>();
+ dataModel.itemsIdMap.stream()
+ .filter(item -> item.itemType == ITEM_TYPE_FOLDER)
+ .forEach(item -> result.put(item.id, (FolderInfo) item));
+ return result;
+ }
+
}
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index 22f1164..bebe1a4 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -58,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;
@@ -531,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
*/
diff --git a/src/com/android/launcher3/folder/PreviewItemManager.java b/src/com/android/launcher3/folder/PreviewItemManager.java
index 5ee6a25..4cf618d 100644
--- a/src/com/android/launcher3/folder/PreviewItemManager.java
+++ b/src/com/android/launcher3/folder/PreviewItemManager.java
@@ -53,7 +53,6 @@
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;
@@ -448,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) {
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.kt b/src/com/android/launcher3/graphics/IconShape.kt
index 22d3f3d..2c4d8e4 100644
--- a/src/com/android/launcher3/graphics/IconShape.kt
+++ b/src/com/android/launcher3/graphics/IconShape.kt
@@ -17,88 +17,102 @@
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
-import android.animation.FloatArrayEvaluator
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.Xml
+import android.util.Log
import android.view.View
import android.view.ViewOutlineProvider
-import com.android.launcher3.R
+import androidx.annotation.VisibleForTesting
+import androidx.core.graphics.PathParser
+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 java.io.IOException
import javax.inject.Inject
-import org.xmlpull.v1.XmlPullParser
-import org.xmlpull.v1.XmlPullParserException
/** Abstract representation of the shape of an icon shape */
@LauncherAppSingleton
-class IconShape @Inject constructor(@ApplicationContext context: Context) {
- var shape: ShapeDelegate = Circle()
- private set
+class IconShape
+@Inject
+constructor(
+ @ApplicationContext private val context: Context,
+ private val prefs: LauncherPrefs,
+ private val 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 {
- pickBestShape(context)
+ val changeListener = ThemeChangeListener { shape = pickBestShape(themeManager) }
+ themeManager.addChangeListener(changeListener)
+ lifeCycle.addCloseable { themeManager.removeChangeListener(changeListener) }
+ }
+
+ fun getShapeOverridePath(pathSize: Float = DEFAULT_PATH_SIZE): Path {
+ val path = PathParser.createPathFromPathData(themeManager.iconState.iconMask)
+ if (pathSize != DEFAULT_PATH_SIZE) {
+ val matrix = Matrix()
+ val scale: Float = pathSize / DEFAULT_PATH_SIZE
+ matrix.setScale(scale, scale)
+ path.transform(matrix)
+ }
+ return path
}
/** Initializes the shape which is closest to the [AdaptiveIconDrawable] */
- fun pickBestShape(context: Context) {
- // Pick any large size
- val size = 200
- val full = Region(0, 0, size, size)
- val shapePath = Path()
- val shapeR = Region()
- val iconR = Region()
- val drawable = AdaptiveIconDrawable(ColorDrawable(Color.BLACK), ColorDrawable(Color.BLACK))
- drawable.setBounds(0, 0, size, size)
- iconR.setPath(drawable.iconMask, full)
-
- // Find the shape with minimum area of divergent region.
- var minArea = Int.MAX_VALUE
- var closestShape: ShapeDelegate? = null
- for (shape in getAllShapes(context)) {
- shapePath.reset()
- shape.addToPath(shapePath, 0f, 0f, size / 2f)
- shapeR.setPath(shapePath, full)
- shapeR.op(iconR, Region.Op.XOR)
-
- val area = GraphicsUtils.getArea(shapeR)
- if (area < minArea) {
- minArea = area
- closestShape = shape
+ private fun pickBestShape(themeManager: ThemeManager): ShapeDelegate {
+ val drawable =
+ AdaptiveIconDrawable(null, ColorDrawable(Color.BLACK)).apply {
+ setBounds(0, 0, AREA_CALC_SIZE, AREA_CALC_SIZE)
}
- }
- if (closestShape != null) {
- shape = closestShape
- }
-
- // Initialize shape properties
- normalizationScale = IconNormalizer.normalizeAdaptiveIcon(drawable, size, null)
+ normalizationScale = IconNormalizer.normalizeAdaptiveIcon(drawable, AREA_CALC_SIZE)
+ return pickBestShape(drawable.iconMask, themeManager.iconState.iconMask)
}
interface ShapeDelegate {
- fun enableShapeDetection(): Boolean {
- return false
- }
-
fun drawShape(canvas: Canvas, offsetX: Float, offsetY: Float, radius: Float, paint: Paint)
fun addToPath(path: Path, offsetX: Float, offsetY: Float, radius: Float)
@@ -109,38 +123,11 @@
endRect: Rect,
endRadius: Float,
isReversed: Boolean,
- ): ValueAnimator where T : View?, T : ClipPathView?
+ ): ValueAnimator where T : View, T : ClipPathView
}
- /** Abstract shape where the reveal animation is a derivative of a round rect animation */
- private abstract class SimpleRectShape : ShapeDelegate {
- override fun <T> createRevealAnimator(
- target: T,
- startRect: Rect,
- endRect: Rect,
- endRadius: Float,
- isReversed: Boolean,
- ): ValueAnimator where T : View?, T : ClipPathView? {
- return object :
- RoundedRectRevealOutlineProvider(
- getStartRadius(startRect),
- endRadius,
- startRect,
- endRect,
- ) {
- override fun shouldRemoveElevationDuringAnimation(): Boolean {
- return true
- }
- }
- .createRevealAnimator(target, isReversed)
- }
-
- protected abstract fun getStartRadius(startRect: Rect): Float
- }
-
- /** Abstract shape which draws using [Path] */
- abstract class PathShape : ShapeDelegate {
- private val mTmpPath = Path()
+ @VisibleForTesting
+ class Circle : RoundedSquare(1f) {
override fun drawShape(
canvas: Canvas,
@@ -148,138 +135,16 @@
offsetY: Float,
radius: Float,
paint: Paint,
- ) {
- mTmpPath.reset()
- addToPath(mTmpPath, offsetX, offsetY, radius)
- canvas.drawPath(mTmpPath, paint)
- }
+ ) = canvas.drawCircle(radius + offsetX, radius + offsetY, radius, paint)
- protected abstract fun newUpdateListener(
- startRect: Rect,
- endRect: Rect,
- endRadius: Float,
- outPath: Path,
- ): ValueAnimator.AnimatorUpdateListener
-
- override fun <T> createRevealAnimator(
- target: T,
- startRect: Rect,
- endRect: Rect,
- endRadius: Float,
- isReversed: Boolean,
- ): ValueAnimator where T : View?, T : ClipPathView? {
- val path = Path()
- val listener = newUpdateListener(startRect, endRect, endRadius, path)
-
- val va =
- if (isReversed) ValueAnimator.ofFloat(1f, 0f) else ValueAnimator.ofFloat(0f, 1f)
- va.addListener(
- object : AnimatorListenerAdapter() {
- private var mOldOutlineProvider: ViewOutlineProvider? = null
-
- override fun onAnimationStart(animation: Animator) {
- target?.apply {
- mOldOutlineProvider = outlineProvider
- outlineProvider = null
- translationZ = -target.elevation
- }
- }
-
- override fun onAnimationEnd(animation: Animator) {
- target?.apply {
- translationZ = 0f
- setClipPath(null)
- outlineProvider = mOldOutlineProvider
- }
- }
- }
- )
-
- va.addUpdateListener { anim: ValueAnimator ->
- path.reset()
- listener.onAnimationUpdate(anim)
- target?.setClipPath(path)
- }
-
- return va
- }
- }
-
- open class Circle : PathShape() {
- private val mTempRadii = FloatArray(8)
-
- override fun newUpdateListener(
- startRect: Rect,
- endRect: Rect,
- endRadius: Float,
- outPath: Path,
- ): ValueAnimator.AnimatorUpdateListener {
- val r1 = getStartRadius(startRect)
-
- val startValues =
- floatArrayOf(
- startRect.left.toFloat(),
- startRect.top.toFloat(),
- startRect.right.toFloat(),
- startRect.bottom.toFloat(),
- r1,
- r1,
- )
- val endValues =
- floatArrayOf(
- endRect.left.toFloat(),
- endRect.top.toFloat(),
- endRect.right.toFloat(),
- endRect.bottom.toFloat(),
- endRadius,
- endRadius,
- )
-
- val evaluator = FloatArrayEvaluator(FloatArray(6))
-
- return ValueAnimator.AnimatorUpdateListener { anim: ValueAnimator ->
- val progress = anim.animatedValue as Float
- val 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 fun getRadiiArray(r1: Float, r2: Float): FloatArray {
- mTempRadii[7] = r1
- mTempRadii[6] = mTempRadii[7]
- mTempRadii[3] = mTempRadii[6]
- mTempRadii[2] = mTempRadii[3]
- mTempRadii[1] = mTempRadii[2]
- mTempRadii[0] = mTempRadii[1]
- mTempRadii[5] = r2
- mTempRadii[4] = mTempRadii[5]
- return mTempRadii
- }
-
- override fun addToPath(path: Path, offsetX: Float, offsetY: Float, radius: Float) {
+ override fun addToPath(path: Path, offsetX: Float, offsetY: Float, radius: Float) =
path.addCircle(radius + offsetX, radius + offsetY, radius, Path.Direction.CW)
- }
-
- private fun getStartRadius(startRect: Rect): Float {
- return startRect.width() / 2f
- }
-
- override fun enableShapeDetection(): Boolean {
- return true
- }
}
- private class RoundedSquare(
- /** Ratio of corner radius to half size. */
- private val mRadiusRatio: Float
- ) : SimpleRectShape() {
+ /** 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,
@@ -289,14 +154,14 @@
) {
val cx = radius + offsetX
val cy = radius + offsetY
- val cr = radius * mRadiusRatio
+ 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 * mRadiusRatio
+ val cr = radius * radiusRatio
path.addRoundRect(
cx - radius,
cy - radius,
@@ -308,213 +173,219 @@
)
}
- override fun getStartRadius(startRect: Rect): Float {
- return (startRect.width() / 2f) * mRadiusRatio
+ 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)
}
}
- private class TearDrop(
- /**
- * Radio of short radius to large radius, based on the shape options defined in the config.
- */
- private val mRadiusRatio: Float
- ) : PathShape() {
- private val mTempRadii = FloatArray(8)
-
- override fun addToPath(path: Path, offsetX: Float, offsetY: Float, radius: Float) {
- val r2 = radius * mRadiusRatio
- val cx = radius + offsetX
- val cy = radius + offsetY
-
- path.addRoundRect(
- cx - radius,
- cy - radius,
- cx + radius,
- cy + radius,
- getRadiiArray(radius, r2),
- Path.Direction.CW,
+ /** 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,
)
- }
-
- fun getRadiiArray(r1: Float, r2: Float): FloatArray {
- mTempRadii[7] = r1
- mTempRadii[6] = mTempRadii[7]
- mTempRadii[3] = mTempRadii[6]
- mTempRadii[2] = mTempRadii[3]
- mTempRadii[1] = mTempRadii[2]
- mTempRadii[0] = mTempRadii[1]
- mTempRadii[5] = r2
- mTempRadii[4] = mTempRadii[5]
- return mTempRadii
- }
-
- override fun newUpdateListener(
- startRect: Rect,
- endRect: Rect,
- endRadius: Float,
- outPath: Path,
- ): ValueAnimator.AnimatorUpdateListener {
- val r1 = startRect.width() / 2f
- val r2 = r1 * mRadiusRatio
-
- val startValues =
- floatArrayOf(
- startRect.left.toFloat(),
- startRect.top.toFloat(),
- startRect.right.toFloat(),
- startRect.bottom.toFloat(),
- r1,
- r2,
- )
- val endValues =
- floatArrayOf(
- endRect.left.toFloat(),
- endRect.top.toFloat(),
- endRect.right.toFloat(),
- endRect.bottom.toFloat(),
- endRadius,
- endRadius,
- )
-
- val evaluator = FloatArrayEvaluator(FloatArray(6))
-
- return ValueAnimator.AnimatorUpdateListener { anim: ValueAnimator ->
- val progress = anim.animatedValue as Float
- val values = evaluator.evaluate(progress, startValues, endValues)
- outPath.addRoundRect(
- values[0],
- values[1],
- values[2],
- values[3],
- getRadiiArray(values[4], values[5]),
- Path.Direction.CW,
- )
+ // 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()
- private class Squircle(
- /** Radio of radius to circle radius, based on the shape options defined in the config. */
- private val mRadiusRatio: Float
- ) : PathShape() {
+ 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) {
- val cx = radius + offsetX
- val cy = radius + offsetY
- val control = radius - radius * mRadiusRatio
-
- path.moveTo(cx, cy - radius)
- addLeftCurve(cx, cy, radius, control, path)
- addRightCurve(cx, cy, radius, control, path)
- addLeftCurve(cx, cy, -radius, -control, path)
- addRightCurve(cx, cy, -radius, -control, path)
- path.close()
+ tmpMatrix.setScale(radius / 50, radius / 50)
+ tmpMatrix.postTranslate(offsetX, offsetY)
+ basePath.transform(tmpMatrix, path)
}
- fun addLeftCurve(cx: Float, cy: Float, r: Float, control: Float, path: Path) {
- path.cubicTo(cx - control, cy - r, cx - r, cy - control, cx - r, cy)
- }
-
- fun addRightCurve(cx: Float, cy: Float, r: Float, control: Float, path: Path) {
- path.cubicTo(cx - r, cy + control, cx - control, cy + r, cx, cy + r)
- }
-
- override fun newUpdateListener(
+ override fun <T> createRevealAnimator(
+ target: T,
startRect: Rect,
endRect: Rect,
endRadius: Float,
- outPath: Path,
- ): ValueAnimator.AnimatorUpdateListener {
- val startCX = startRect.exactCenterX()
- val startCY = startRect.exactCenterY()
- val startR = startRect.width() / 2f
- val startControl = startR - startR * mRadiusRatio
- val startHShift = 0f
- val startVShift = 0f
+ 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 endCX = endRect.exactCenterX()
- val endCY = endRect.exactCenterY()
- // Approximate corner circle using bezier curves
- // http://spencermortensen.com/articles/bezier-circle/
- val endControl = endRadius * 0.551915024494f
- val endHShift = endRect.width() / 2f - endRadius
- val endVShift = endRect.height() / 2f - endRadius
+ val va =
+ if (isReversed) ValueAnimator.ofFloat(1f, 0f) else ValueAnimator.ofFloat(0f, 1f)
+ va.addListener(
+ object : AnimatorListenerAdapter() {
+ private var oldOutlineProvider: ViewOutlineProvider? = null
- return ValueAnimator.AnimatorUpdateListener { anim: ValueAnimator ->
- val progress = anim.animatedValue as Float
- val cx = (1 - progress) * startCX + progress * endCX
- val cy = (1 - progress) * startCY + progress * endCY
- val r = (1 - progress) * startR + progress * endRadius
- val control = (1 - progress) * startControl + progress * endControl
- val hShift = (1 - progress) * startHShift + progress * endHShift
- val vShift = (1 - progress) * startVShift + progress * endVShift
+ override fun onAnimationStart(animation: Animator) {
+ target.apply {
+ oldOutlineProvider = outlineProvider
+ outlineProvider = null
+ translationZ = -target.elevation
+ }
+ }
- outPath.moveTo(cx, cy - vShift - r)
- outPath.rLineTo(-hShift, 0f)
+ override fun onAnimationEnd(animation: Animator) {
+ target.apply {
+ translationZ = 0f
+ setClipPath(null)
+ outlineProvider = oldOutlineProvider
+ }
+ }
+ }
+ )
- addLeftCurve(cx - hShift, cy - vShift, r, control, outPath)
- outPath.rLineTo(0f, vShift + vShift)
-
- addRightCurve(cx - hShift, cy + vShift, r, control, outPath)
- outPath.rLineTo(hShift + hShift, 0f)
-
- addLeftCurve(cx + hShift, cy + vShift, -r, -control, outPath)
- outPath.rLineTo(0f, -vShift - vShift)
-
- addRightCurve(cx + hShift, cy - vShift, -r, -control, outPath)
- outPath.close()
+ 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)
- private fun getShapeDefinition(type: String, radius: Float): ShapeDelegate {
- return when (type) {
- "Circle" -> Circle()
- "RoundedSquare" -> RoundedSquare(radius)
- "TearDrop" -> TearDrop(radius)
- "Squircle" -> Squircle(radius)
- else -> throw IllegalArgumentException("Invalid shape type: $type")
+ const val TAG = "IconShape"
+ const val KEY_ICON_SHAPE = "icon_shape"
+ const val DEFAULT_PATH_SIZE = 100f
+ 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
}
}
- private fun getAllShapes(context: Context): List<ShapeDelegate> {
- val result = ArrayList<ShapeDelegate>()
- try {
- context.resources.getXml(R.xml.folder_shapes).use { parser ->
- // Find the root tag
- var type: Int = parser.next()
- while (
- type != XmlPullParser.END_TAG &&
- type != XmlPullParser.END_DOCUMENT &&
- "shapes" != parser.name
- ) {
- type = parser.next()
- }
- val depth = parser.depth
- val radiusAttr = intArrayOf(R.attr.folderIconRadius)
- type = parser.next()
- while (
- (type != XmlPullParser.END_TAG || parser.depth > depth) &&
- type != XmlPullParser.END_DOCUMENT
- ) {
- if (type == XmlPullParser.START_TAG) {
- val attrs = Xml.asAttributeSet(parser)
- val arr = context.obtainStyledAttributes(attrs, radiusAttr)
- val shape = getShapeDefinition(parser.name, arr.getFloat(0, 1f))
- arr.recycle()
- result.add(shape)
- }
- type = parser.next()
- }
- }
- } catch (e: IOException) {
- throw RuntimeException(e)
- } catch (e: XmlPullParserException) {
- throw RuntimeException(e)
+ /** 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)
}
- return result
}
+
+ @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/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index f0e4fc4..a0b73ae 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -24,10 +24,10 @@
import static com.android.launcher3.BubbleTextView.DISPLAY_WORKSPACE;
import static com.android.launcher3.DeviceProfile.DEFAULT_SCALE;
import static com.android.launcher3.Hotseat.ALPHA_CHANNEL_PREVIEW_RENDERER;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
import static com.android.launcher3.Utilities.SHOULD_SHOW_FIRST_PAGE_WIDGET;
-import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
-import static com.android.launcher3.model.ModelUtils.getMissingHotseatRanks;
+import static com.android.launcher3.model.ModelUtils.currentScreenContentFilter;
import android.app.Fragment;
import android.app.WallpaperColors;
@@ -105,7 +105,9 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.stream.Collectors;
+import java.util.stream.IntStream;
/**
* Utility class for generating the preview of Launcher for a given InvariantDeviceProfile.
@@ -456,54 +458,48 @@
private void populate(BgDataModel dataModel,
Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap) {
- // Separate the items that are on the current screen, and the other remaining items.
- ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<>();
- ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<>();
- ArrayList<LauncherAppWidgetInfo> currentAppWidgets = new ArrayList<>();
- ArrayList<LauncherAppWidgetInfo> otherAppWidgets = new ArrayList<>();
+ IntSet missingHotseatRank = new IntSet();
+ IntStream.range(0, mDp.numShownHotseatIcons).forEach(missingHotseatRank::add);
- IntSet currentScreenIds = IntSet.wrap(mWorkspaceScreens.keySet());
- filterCurrentWorkspaceItems(currentScreenIds, dataModel.workspaceItems,
- currentWorkspaceItems, otherWorkspaceItems);
- filterCurrentWorkspaceItems(currentScreenIds, dataModel.appWidgets, currentAppWidgets,
- otherAppWidgets);
- for (ItemInfo itemInfo : currentWorkspaceItems) {
- switch (itemInfo.itemType) {
- case Favorites.ITEM_TYPE_APPLICATION:
- case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
- inflateAndAddIcon((WorkspaceItemInfo) itemInfo);
- break;
- case Favorites.ITEM_TYPE_FOLDER:
- case Favorites.ITEM_TYPE_APP_PAIR:
- inflateAndAddCollectionIcon((CollectionInfo) itemInfo);
- break;
- default:
- break;
- }
- }
- Map<ComponentKey, AppWidgetProviderInfo> widgetsMap = widgetProviderInfoMap;
- for (ItemInfo itemInfo : currentAppWidgets) {
- switch (itemInfo.itemType) {
- case Favorites.ITEM_TYPE_APPWIDGET:
- case Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
- if (widgetsMap == null) {
- widgetsMap = dataModel.widgetsModel.getWidgetsByComponentKey()
- .entrySet()
- .stream()
- .filter(entry -> entry.getValue().widgetInfo != null)
- .collect(Collectors.toMap(
- Map.Entry::getKey,
- entry -> entry.getValue().widgetInfo
- ));
+ Map<ComponentKey, AppWidgetProviderInfo>[] widgetsMap = new Map[] { widgetProviderInfoMap};
+
+ // Separate the items that are on the current screen, and the other remaining items.
+ dataModel.itemsIdMap.stream()
+ .filter(currentScreenContentFilter(IntSet.wrap(mWorkspaceScreens.keySet())))
+ .forEach(itemInfo -> {
+ switch (itemInfo.itemType) {
+ case Favorites.ITEM_TYPE_APPLICATION:
+ case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
+ inflateAndAddIcon((WorkspaceItemInfo) itemInfo);
+ break;
+ case Favorites.ITEM_TYPE_FOLDER:
+ case Favorites.ITEM_TYPE_APP_PAIR:
+ inflateAndAddCollectionIcon((CollectionInfo) itemInfo);
+ break;
+ case Favorites.ITEM_TYPE_APPWIDGET:
+ case Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
+ if (widgetsMap[0] == null) {
+ widgetsMap[0] = dataModel.widgetsModel.getWidgetsByComponentKey()
+ .entrySet()
+ .stream()
+ .filter(entry -> entry.getValue().widgetInfo != null)
+ .collect(Collectors.toMap(
+ Entry::getKey,
+ entry -> entry.getValue().widgetInfo
+ ));
+ }
+ inflateAndAddWidgets((LauncherAppWidgetInfo) itemInfo, widgetsMap[0]);
+ break;
+ default:
+ break;
}
- inflateAndAddWidgets((LauncherAppWidgetInfo) itemInfo, widgetsMap);
- break;
- default:
- break;
- }
- }
- IntArray ranks = getMissingHotseatRanks(currentWorkspaceItems,
- mDp.numShownHotseatIcons);
+
+ if (itemInfo.container == CONTAINER_HOTSEAT) {
+ missingHotseatRank.remove(itemInfo.screenId);
+ }
+ });
+
+ IntArray ranks = missingHotseatRank.getArray();
FixedContainerItems hotseatPredictions =
dataModel.extraItems.get(CONTAINER_HOTSEAT_PREDICTION);
List<ItemInfo> predictions = hotseatPredictions == null
diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
index 9fffcc1..3464e9b 100644
--- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
@@ -40,7 +40,6 @@
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.icons.FastBitmapDrawable;
-import com.android.launcher3.icons.GraphicsUtils;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.util.Themes;
@@ -121,7 +120,8 @@
IconPalette.getPreloadProgressColor(context, info.bitmap.color),
getPreloadColors(context),
Utilities.isDarkTheme(context),
- GraphicsUtils.getShapePath(context, DEFAULT_PATH_SIZE));
+ IconShape.INSTANCE.get(context).getShapeOverridePath(DEFAULT_PATH_SIZE)
+ );
}
public PreloadIconDrawable(
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/CacheableShortcutInfo.kt b/src/com/android/launcher3/icons/CacheableShortcutInfo.kt
index a78da23..225e12f 100644
--- a/src/com/android/launcher3/icons/CacheableShortcutInfo.kt
+++ b/src/com/android/launcher3/icons/CacheableShortcutInfo.kt
@@ -112,7 +112,9 @@
?.let { d ->
li.createBadgedIconBitmap(
d,
- IconOptions().setExtractedColor(Themes.getColorAccent(context)),
+ IconOptions()
+ .setExtractedColor(Themes.getColorAccent(context))
+ .setSourceHint(getSourceHint(info, cache)),
)
} ?: BitmapInfo.LOW_RES_INFO
}
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index 88a60ea..9f99e8f 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -594,7 +594,8 @@
@VisibleForTesting
synchronized boolean isItemInDb(ComponentKey cacheKey) {
- return getEntryFromDBLocked(cacheKey, new CacheEntry(), DEFAULT_LOOKUP_FLAG);
+ return getEntryFromDBLocked(cacheKey, new CacheEntry(), DEFAULT_LOOKUP_FLAG,
+ LauncherActivityCachingLogic.INSTANCE);
}
/**
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/BaseLauncherBinder.java b/src/com/android/launcher3/model/BaseLauncherBinder.java
index c251114..de74ae8 100644
--- a/src/com/android/launcher3/model/BaseLauncherBinder.java
+++ b/src/com/android/launcher3/model/BaseLauncherBinder.java
@@ -19,8 +19,10 @@
import static com.android.launcher3.BuildConfig.WIDGETS_ENABLED;
import static com.android.launcher3.Flags.enableSmartspaceRemovalToggle;
import static com.android.launcher3.Flags.enableWorkspaceInflation;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
import static com.android.launcher3.model.ItemInstallQueue.FLAG_LOADER_RUNNING;
-import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
+import static com.android.launcher3.model.ModelUtils.WIDGET_FILTER;
+import static com.android.launcher3.model.ModelUtils.currentScreenContentFilter;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
@@ -44,11 +46,11 @@
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSet;
+import com.android.launcher3.util.IntSparseArrayMap;
import com.android.launcher3.util.ItemInflater;
import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.util.LooperIdleLock;
@@ -102,14 +104,12 @@
Trace.beginSection("BaseLauncherBinder#bindWorkspace");
try {
// Save a copy of all the bg-thread collections
- ArrayList<ItemInfo> workspaceItems = new ArrayList<>();
- ArrayList<LauncherAppWidgetInfo> appWidgets = new ArrayList<>();
+ IntSparseArrayMap<ItemInfo> itemsIdMap;
final IntArray orderedScreenIds = new IntArray();
ArrayList<FixedContainerItems> extraItems = new ArrayList<>();
final int workspaceItemCount;
synchronized (mBgDataModel) {
- workspaceItems.addAll(mBgDataModel.workspaceItems);
- appWidgets.addAll(mBgDataModel.appWidgets);
+ itemsIdMap = mBgDataModel.itemsIdMap.clone();
orderedScreenIds.addAll(mBgDataModel.collectWorkspaceScreens());
mBgDataModel.extraItems.forEach(extraItems::add);
if (incrementBindId) {
@@ -122,7 +122,7 @@
for (Callbacks cb : mCallbacksList) {
new UnifiedWorkspaceBinder(cb, mUiExecutor, mApp, mBgDataModel, mMyBindingId,
- workspaceItems, appWidgets, extraItems, orderedScreenIds)
+ itemsIdMap, extraItems, orderedScreenIds)
.bind(isBindSync, workspaceItemCount);
}
} finally {
@@ -258,8 +258,7 @@
private final BgDataModel mBgDataModel;
private final int mMyBindingId;
- private final ArrayList<ItemInfo> mWorkspaceItems;
- private final ArrayList<LauncherAppWidgetInfo> mAppWidgets;
+ private final IntSparseArrayMap<ItemInfo> mItemIdMap;
private final IntArray mOrderedScreenIds;
private final ArrayList<FixedContainerItems> mExtraItems;
@@ -268,8 +267,7 @@
LauncherAppState app,
BgDataModel bgDataModel,
int myBindingId,
- ArrayList<ItemInfo> workspaceItems,
- ArrayList<LauncherAppWidgetInfo> appWidgets,
+ IntSparseArrayMap<ItemInfo> itemIdMap,
ArrayList<FixedContainerItems> extraItems,
IntArray orderedScreenIds) {
mCallbacks = callbacks;
@@ -277,8 +275,7 @@
mApp = app;
mBgDataModel = bgDataModel;
mMyBindingId = myBindingId;
- mWorkspaceItems = workspaceItems;
- mAppWidgets = appWidgets;
+ mItemIdMap = itemIdMap;
mExtraItems = extraItems;
mOrderedScreenIds = orderedScreenIds;
}
@@ -294,10 +291,15 @@
ArrayList<ItemInfo> currentAppWidgets = new ArrayList<>();
ArrayList<ItemInfo> otherAppWidgets = new ArrayList<>();
- filterCurrentWorkspaceItems(currentScreenIds, mWorkspaceItems, currentWorkspaceItems,
- otherWorkspaceItems);
- filterCurrentWorkspaceItems(currentScreenIds, mAppWidgets, currentAppWidgets,
- otherAppWidgets);
+ Predicate<ItemInfo> currentScreenCheck = currentScreenContentFilter(currentScreenIds);
+ mItemIdMap.forEach(item -> {
+ if (currentScreenCheck.test(item)) {
+ (WIDGET_FILTER.test(item) ? currentAppWidgets : currentWorkspaceItems)
+ .add(item);
+ } else if (item.container == CONTAINER_DESKTOP) {
+ (WIDGET_FILTER.test(item) ? otherAppWidgets : otherWorkspaceItems).add(item);
+ }
+ });
final InvariantDeviceProfile idp = mApp.getInvariantDeviceProfile();
sortWorkspaceItemsSpatially(idp, currentWorkspaceItems);
sortWorkspaceItemsSpatially(idp, otherWorkspaceItems);
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index b9b1e98..a04cbfb 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -20,6 +20,11 @@
import static com.android.launcher3.BuildConfig.QSB_ON_FIRST_SCREEN;
import static com.android.launcher3.BuildConfig.WIDGETS_ENABLED;
import static com.android.launcher3.Flags.enableSmartspaceRemovalToggle;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_FOLDER;
import static com.android.launcher3.Utilities.SHOULD_SHOW_FIRST_PAGE_WIDGET;
import static com.android.launcher3.shortcuts.ShortcutRequest.PINNED;
@@ -31,7 +36,6 @@
import android.content.pm.ShortcutInfo;
import android.os.UserHandle;
import android.text.TextUtils;
-import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
import android.view.View;
@@ -39,14 +43,11 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.BuildConfig;
import com.android.launcher3.Workspace;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.model.data.AppPairInfo;
import com.android.launcher3.model.data.CollectionInfo;
-import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -93,22 +94,6 @@
public final IntSparseArrayMap<ItemInfo> itemsIdMap = new IntSparseArrayMap<>();
/**
- * List of all the folders and shortcuts directly on the home screen (no widgets
- * or shortcuts within folders).
- */
- public final ArrayList<ItemInfo> workspaceItems = new ArrayList<>();
-
- /**
- * All LauncherAppWidgetInfo created by LauncherModel.
- */
- public final ArrayList<LauncherAppWidgetInfo> appWidgets = new ArrayList<>();
-
- /**
- * Map of id to CollectionInfos of all the folders or app pairs created by LauncherModel
- */
- public final IntSparseArrayMap<CollectionInfo> collections = new IntSparseArrayMap<>();
-
- /**
* Extra container based items
*/
public final IntSparseArrayMap<FixedContainerItems> extraItems = new IntSparseArrayMap<>();
@@ -144,9 +129,6 @@
* Clears all the data
*/
public synchronized void clear() {
- workspaceItems.clear();
- appWidgets.clear();
- collections.clear();
itemsIdMap.clear();
deepShortcutMap.clear();
extraItems.clear();
@@ -158,7 +140,7 @@
public synchronized IntArray collectWorkspaceScreens() {
IntSet screenSet = new IntSet();
for (ItemInfo item: itemsIdMap) {
- if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+ if (item.container == CONTAINER_DESKTOP) {
screenSet.add(item.screenId);
}
}
@@ -173,26 +155,14 @@
public synchronized void dump(String prefix, FileDescriptor fd, PrintWriter writer,
String[] args) {
writer.println(prefix + "Data Model:");
- writer.println(prefix + " ---- workspace items ");
- for (int i = 0; i < workspaceItems.size(); i++) {
- writer.println(prefix + '\t' + workspaceItems.get(i).toString());
- }
- writer.println(prefix + " ---- appwidget items ");
- for (int i = 0; i < appWidgets.size(); i++) {
- writer.println(prefix + '\t' + appWidgets.get(i).toString());
- }
- writer.println(prefix + " ---- collection items ");
- for (int i = 0; i < collections.size(); i++) {
- writer.println(prefix + '\t' + collections.valueAt(i).toString());
+ writer.println(prefix + " ---- items id map ");
+ for (int i = 0; i < itemsIdMap.size(); i++) {
+ writer.println(prefix + '\t' + itemsIdMap.valueAt(i).toString());
}
writer.println(prefix + " ---- extra items ");
for (int i = 0; i < extraItems.size(); i++) {
writer.println(prefix + '\t' + extraItems.valueAt(i).toString());
}
- writer.println(prefix + " ---- items id map ");
- for (int i = 0; i < itemsIdMap.size(); i++) {
- writer.println(prefix + '\t' + itemsIdMap.valueAt(i).toString());
- }
if (args.length > 0 && TextUtils.equals(args[0], "--all")) {
writer.println(prefix + "shortcut counts ");
@@ -207,94 +177,38 @@
removeItem(context, Arrays.asList(items));
}
- public synchronized void removeItem(Context context, Iterable<? extends ItemInfo> items) {
- ArraySet<UserHandle> updatedDeepShortcuts = new ArraySet<>();
- for (ItemInfo item : items) {
- switch (item.itemType) {
- case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
- case LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR:
- collections.remove(item.id);
- if (FeatureFlags.IS_STUDIO_BUILD) {
- for (ItemInfo info : itemsIdMap) {
- if (info.container == item.id) {
- // We are deleting a collection which still contains items that
- // think they are contained by that collection.
- String msg = "deleting a collection (" + item + ") which still "
- + "contains items (" + info + ")";
- Log.e(TAG, msg);
- }
- }
- }
- workspaceItems.remove(item);
- break;
- case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: {
- updatedDeepShortcuts.add(item.user);
- // Fall through.
- }
- case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
- workspaceItems.remove(item);
- break;
- case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
- case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
- appWidgets.remove(item);
- break;
- }
- itemsIdMap.remove(item.id);
+ public synchronized void removeItem(Context context, List<? extends ItemInfo> items) {
+ if (BuildConfig.IS_STUDIO_BUILD) {
+ items.stream()
+ .filter(item -> item.itemType == ITEM_TYPE_FOLDER
+ || item.itemType == ITEM_TYPE_APP_PAIR)
+ .forEach(item -> itemsIdMap.stream()
+ .filter(info -> info.container == item.id)
+ // We are deleting a collection which still contains items that
+ // think they are contained by that collection.
+ .forEach(info -> Log.e(TAG,
+ "deleting a collection (" + item + ") which still contains"
+ + " items (" + info + ")")));
}
- updatedDeepShortcuts.forEach(user -> updateShortcutPinnedState(context, user));
+
+ items.forEach(item -> itemsIdMap.remove(item.id));
+ items.stream().map(info -> info.user).distinct().forEach(
+ user -> updateShortcutPinnedState(context, user));
}
public synchronized void addItem(Context context, ItemInfo item, boolean newItem) {
- addItem(context, item, newItem, null);
- }
-
- public synchronized void addItem(
- Context context, ItemInfo item, boolean newItem, @Nullable LoaderMemoryLogger logger) {
- if (logger != null) {
- logger.addLog(
- Log.DEBUG,
- TAG,
- String.format("Adding item to ID map: %s", item.toString()),
- /* stackTrace= */ null);
- }
itemsIdMap.put(item.id, item);
- switch (item.itemType) {
- case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
- collections.put(item.id, (FolderInfo) item);
- workspaceItems.add(item);
- break;
- case LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR:
- collections.put(item.id, (AppPairInfo) item);
- // Fall through here. App pairs are both containers (like folders) and containable
- // items (can be placed in folders). So we need to add app pairs to the folders
- // array (above) but also verify the existence of their container, like regular
- // apps (below).
- case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
- case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
- if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
- item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
- workspaceItems.add(item);
- } else {
- if (newItem) {
- if (!collections.containsKey(item.container)) {
- // Adding an item to a nonexistent collection.
- String msg = "attempted to add item: " + item + " to a nonexistent app"
- + " collection";
- Log.e(TAG, msg);
- }
- } else {
- findOrMakeFolder(item.container).add(item);
- }
- }
- break;
- case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
- case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
- appWidgets.add((LauncherAppWidgetInfo) item);
- break;
- }
- if (newItem && item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+ if (newItem && item.itemType == ITEM_TYPE_DEEP_SHORTCUT) {
updateShortcutPinnedState(context, item.user);
}
+ if (BuildConfig.IS_DEBUG_DEVICE
+ && newItem
+ && item.container != CONTAINER_DESKTOP
+ && item.container != CONTAINER_HOTSEAT
+ && !(itemsIdMap.get(item.container) instanceof CollectionInfo)) {
+ // Adding an item to a nonexistent collection.
+ Log.e(TAG, "attempted to add item: " + item + " to a nonexistent app collection");
+ }
}
/**
@@ -334,7 +248,7 @@
Map<String, Set<String>> modelMap = Stream.concat(
// Model shortcuts
itemStream.build()
- .filter(wi -> wi.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
+ .filter(wi -> wi.itemType == ITEM_TYPE_DEEP_SHORTCUT)
.map(ShortcutKey::fromItemInfo),
// Pending shortcuts
ItemInstallQueue.INSTANCE.get(context).getPendingShortcuts(user))
@@ -375,24 +289,6 @@
}
/**
- * Return an existing FolderInfo object if we have encountered this ID previously,
- * or make a new one.
- */
- public synchronized CollectionInfo findOrMakeFolder(int id) {
- // See if a placeholder was created for us already
- CollectionInfo collectionInfo = collections.get(id);
- if (collectionInfo == null) {
- // No placeholder -- create a new blank folder instance. At this point, we don't know
- // if the desired container is supposed to be a folder or an app pair. In the case that
- // it is an app pair, the blank folder will be replaced by a blank app pair when the app
- // pair is getting processed, in WorkspaceItemProcessor.processFolderOrAppPair().
- collectionInfo = new FolderInfo();
- collections.put(id, collectionInfo);
- }
- return collectionInfo;
- }
-
- /**
* Clear all the deep shortcut counts for the given package, and re-add the new shortcut counts.
*/
public synchronized void updateDeepShortcutCounts(
@@ -424,16 +320,6 @@
}
/**
- * Returns a list containing all workspace items including widgets.
- */
- public synchronized ArrayList<ItemInfo> getAllWorkspaceItems() {
- ArrayList<ItemInfo> items = new ArrayList<>(workspaceItems.size() + appWidgets.size());
- items.addAll(workspaceItems);
- items.addAll(appWidgets);
- return items;
- }
-
- /**
* Calls the provided {@code op} for all workspaceItems in the in-memory model (both persisted
* items and dynamic/predicted items for the provided {@code userHandle}.
* Note the call is not synchronized over the model, that should be handled by the called.
diff --git a/src/com/android/launcher3/model/FirstScreenBroadcastHelper.kt b/src/com/android/launcher3/model/FirstScreenBroadcastHelper.kt
index aa62c32..6ad52ea 100644
--- a/src/com/android/launcher3/model/FirstScreenBroadcastHelper.kt
+++ b/src/com/android/launcher3/model/FirstScreenBroadcastHelper.kt
@@ -30,7 +30,6 @@
import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.model.data.LauncherAppWidgetInfo
import com.android.launcher3.model.data.WorkspaceItemInfo
-import com.android.launcher3.pm.InstallSessionHelper
import com.android.launcher3.util.Executors
import com.android.launcher3.util.PackageManagerHelper
import com.android.launcher3.util.PackageUserKey
@@ -80,21 +79,22 @@
packageManagerHelper: PackageManagerHelper,
firstScreenItems: List<ItemInfo>,
userKeyToSessionMap: Map<PackageUserKey, SessionInfo>,
- allWidgets: List<LauncherAppWidgetInfo>
+ allWidgets: List<ItemInfo>,
): List<FirstScreenBroadcastModel> {
// installers for installing items
- val pendingItemInstallerMap: Map<String, MutableSet<String>> =
+ val pendingItemInstallerMap: Map<String, Set<String>> =
createPendingItemsMap(userKeyToSessionMap)
+
val installingPackages = pendingItemInstallerMap.values.flatten().toSet()
// installers for installed items on first screen
- val installedItemInstallerMap: Map<String, MutableSet<ItemInfo>> =
+ val installedItemInstallerMap: Map<String, List<ItemInfo>> =
createInstalledItemsMap(firstScreenItems, installingPackages, packageManagerHelper)
// installers for widgets on all screens
- val allInstalledWidgetsMap: Map<String, MutableSet<LauncherAppWidgetInfo>> =
- createAllInstalledWidgetsMap(allWidgets, installingPackages, packageManagerHelper)
+ val allInstalledWidgetsMap: Map<String, List<ItemInfo>> =
+ createInstalledItemsMap(allWidgets, installingPackages, packageManagerHelper)
val allInstallers: Set<String> =
pendingItemInstallerMap.keys +
@@ -131,39 +131,39 @@
context,
0 /* requestCode */,
Intent(),
- PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE
- )
+ PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE,
+ ),
)
.putStringArrayListExtra(
PENDING_COLLECTION_ITEM_EXTRA,
- ArrayList(model.pendingCollectionItems)
+ ArrayList(model.pendingCollectionItems),
)
.putStringArrayListExtra(
PENDING_WORKSPACE_ITEM_EXTRA,
- ArrayList(model.pendingWorkspaceItems)
+ ArrayList(model.pendingWorkspaceItems),
)
.putStringArrayListExtra(
PENDING_HOTSEAT_ITEM_EXTRA,
- ArrayList(model.pendingHotseatItems)
+ ArrayList(model.pendingHotseatItems),
)
.putStringArrayListExtra(
PENDING_WIDGET_ITEM_EXTRA,
- ArrayList(model.pendingWidgetItems)
+ ArrayList(model.pendingWidgetItems),
)
.putStringArrayListExtra(
INSTALLED_WORKSPACE_ITEMS_EXTRA,
- ArrayList(model.installedWorkspaceItems)
+ ArrayList(model.installedWorkspaceItems),
)
.putStringArrayListExtra(
INSTALLED_HOTSEAT_ITEMS_EXTRA,
- ArrayList(model.installedHotseatItems)
+ ArrayList(model.installedHotseatItems),
)
.putStringArrayListExtra(
ALL_INSTALLED_WIDGETS_ITEM_EXTRA,
ArrayList(
model.firstScreenInstalledWidgets +
model.secondaryScreenInstalledWidgets
- )
+ ),
)
context.sendBroadcast(intent)
}
@@ -172,66 +172,46 @@
/** Maps Installer packages to Set of app packages from install sessions */
private fun createPendingItemsMap(
userKeyToSessionMap: Map<PackageUserKey, SessionInfo>
- ): Map<String, MutableSet<String>> {
+ ): Map<String, Set<String>> {
val myUser = Process.myUserHandle()
- val result = mutableMapOf<String, MutableSet<String>>()
- userKeyToSessionMap.forEach { entry ->
- if (!myUser.equals(InstallSessionHelper.getUserHandle(entry.value))) return@forEach
- val installer = entry.value.installerPackageName
- val appPackage = entry.value.appPackageName
- if (installer.isNullOrEmpty() || appPackage.isNullOrEmpty()) return@forEach
- result.getOrPut(installer) { mutableSetOf() }.add(appPackage)
- }
- return result
- }
-
- /**
- * Maps Installer packages to Set of ItemInfo from first screen. Filter out installing packages.
- */
- private fun createInstalledItemsMap(
- firstScreenItems: List<ItemInfo>,
- installingPackages: Set<String>,
- packageManagerHelper: PackageManagerHelper
- ): Map<String, MutableSet<ItemInfo>> {
- val result = mutableMapOf<String, MutableSet<ItemInfo>>()
- firstScreenItems.forEach { item ->
- val appPackage = getPackageName(item) ?: return@forEach
- if (installingPackages.contains(appPackage)) return@forEach
- val installer = packageManagerHelper.getAppInstallerPackage(appPackage)
- if (installer.isNullOrEmpty()) return@forEach
- result.getOrPut(installer) { mutableSetOf() }.add(item)
- }
- return result
- }
-
- /**
- * Maps Installer packages to Set of AppWidget packages installed on all screens. Filter out
- * installing packages.
- */
- private fun createAllInstalledWidgetsMap(
- allWidgets: List<LauncherAppWidgetInfo>,
- installingPackages: Set<String>,
- packageManagerHelper: PackageManagerHelper
- ): Map<String, MutableSet<LauncherAppWidgetInfo>> {
- val result = mutableMapOf<String, MutableSet<LauncherAppWidgetInfo>>()
- allWidgets
- .sortedBy { widget -> widget.screenId }
- .forEach { widget ->
- val appPackage = getPackageName(widget) ?: return@forEach
- if (installingPackages.contains(appPackage)) return@forEach
- val installer = packageManagerHelper.getAppInstallerPackage(appPackage)
- if (installer.isNullOrEmpty()) return@forEach
- result.getOrPut(installer) { mutableSetOf() }.add(widget)
+ return userKeyToSessionMap.values
+ .filter {
+ it.user == myUser &&
+ !it.installerPackageName.isNullOrEmpty() &&
+ !it.appPackageName.isNullOrEmpty()
}
- return result
+ .groupBy(
+ keySelector = { it.installerPackageName },
+ valueTransform = { it.appPackageName },
+ )
+ .mapValues { it.value.filterNotNull().toSet() } as Map<String, Set<String>>
}
+ /** Maps Installer packages to Set of ItemInfos. Filter out installing packages. */
+ private fun createInstalledItemsMap(
+ allItems: Iterable<ItemInfo>,
+ installingPackages: Set<String>,
+ packageManagerHelper: PackageManagerHelper,
+ ): Map<String, List<ItemInfo>> =
+ allItems
+ .sortedBy { it.screenId }
+ .groupByTo(mutableMapOf()) {
+ getPackageName(it)?.let { pkg ->
+ if (installingPackages.contains(pkg)) {
+ null
+ } else {
+ packageManagerHelper.getAppInstallerPackage(pkg)
+ }
+ }
+ }
+ .apply { remove(null) } as Map<String, List<ItemInfo>>
+
/**
* Add first screen Pending Items from Map to [FirstScreenBroadcastModel] for given installer
*/
private fun FirstScreenBroadcastModel.addPendingItems(
installingItems: Set<String>?,
- firstScreenItems: List<ItemInfo>
+ firstScreenItems: List<ItemInfo>,
) {
if (installingItems == null) return
for (info in firstScreenItems) {
@@ -251,7 +231,7 @@
*/
private fun FirstScreenBroadcastModel.addInstalledItems(
installer: String,
- installedItemInstallerMap: Map<String, Set<ItemInfo>>,
+ installedItemInstallerMap: Map<String, List<ItemInfo>>,
) {
installedItemInstallerMap[installer]?.forEach { info ->
val packageName: String = getPackageName(info) ?: return@forEach
@@ -265,7 +245,7 @@
/** Add Widgets on every screen from Map to [FirstScreenBroadcastModel] for given installer */
private fun FirstScreenBroadcastModel.addAllScreenWidgets(
installer: String,
- allInstalledWidgetsMap: Map<String, Set<LauncherAppWidgetInfo>>
+ allInstalledWidgetsMap: Map<String, List<ItemInfo>>,
) {
allInstalledWidgetsMap[installer]?.forEach { widget ->
val packageName: String = getPackageName(widget) ?: return@forEach
@@ -279,7 +259,7 @@
private fun FirstScreenBroadcastModel.addCollectionItems(
info: ItemInfo,
- installingPackages: Set<String>
+ installingPackages: Set<String>,
) {
if (info !is CollectionInfo) return
pendingCollectionItems.addAll(
@@ -336,7 +316,7 @@
Log.d(
TAG,
"Sending First Screen Broadcast for installer=$installerPackage" +
- ", total packages=${getTotalItemCount()}"
+ ", total packages=${getTotalItemCount()}",
)
pendingCollectionItems.forEach {
Log.d(TAG, "$installerPackage:Pending Collection item:$it")
@@ -361,15 +341,7 @@
}
}
- private fun getPackageName(info: ItemInfo): String? {
- var packageName: String? = null
- if (info is LauncherAppWidgetInfo) {
- info.providerName?.let { packageName = info.providerName.packageName }
- } else if (info.targetComponent != null) {
- packageName = info.targetComponent?.packageName
- }
- return packageName
- }
+ private fun getPackageName(info: ItemInfo): String? = info.targetComponent?.packageName
/**
* Clone the provided list on UI thread. This is used for [FolderInfo.getContents] which is
diff --git a/src/com/android/launcher3/model/GridSizeMigrationDBController.java b/src/com/android/launcher3/model/GridSizeMigrationDBController.java
index bfa00bd..8cbe764 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationDBController.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationDBController.java
@@ -120,39 +120,41 @@
@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);
+ } else {
+ copyTable(source, TABLE_NAME, target.getWritableDatabase(), TMP_TABLE, context);
+ }
- if (oneGridSpecs()) {
- DbReader destReader = new DbReader(
- target.getWritableDatabase(), TABLE_NAME, context);
- boolean shouldShiftCells = shouldShiftCells(destReader, srcDeviceState.getRows());
- if (shouldShiftCells) {
+ long migrationStartTime = System.currentTimeMillis();
+ try (SQLiteTransaction t = new SQLiteTransaction(target.getWritableDatabase())) {
+
+ if (shouldMigrateToStrictlyTallerGrid) {
+ // We want to add the extra row(s) to the top of the screen, so we shift the grid
+ // down.
+ if (oneGridSpecs()) {
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;
}
- // Save current configuration, so that the migration does not run again.
- destDeviceState.writeToPrefs(context);
- return true;
- }
- copyTable(source, TABLE_NAME, target.getWritableDatabase(), TMP_TABLE, context);
-
- long migrationStartTime = System.currentTimeMillis();
- try (SQLiteTransaction t = new SQLiteTransaction(target.getWritableDatabase())) {
DbReader srcReader = new DbReader(t.getDb(), TMP_TABLE, context);
DbReader destReader = new DbReader(t.getDb(), TABLE_NAME, context);
@@ -164,7 +166,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 +173,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);
}
}
@@ -443,22 +446,6 @@
}
}
- private static boolean shouldShiftCells(final DbReader destReader, final int srcGridRowCount) {
- List<DbEntry> workspaceItems = destReader.loadAllWorkspaceEntries();
- int firstPageItemsRowPosSum = workspaceItems.stream()
- .filter(entry -> entry.screenId == 0)
- .mapToInt(entry -> entry.cellY).sum();
- int firstPageWorkspaceItemsCount = (int) workspaceItems.stream()
- .filter(entry -> entry.screenId == 0).count();
- if (firstPageWorkspaceItemsCount == 0) {
- return false;
- }
- float srcGridMidPoint = srcGridRowCount / 2f;
- float firstPageItemPosAvg = (float) firstPageItemsRowPosSum / firstPageWorkspaceItemsCount;
- return (firstPageItemPosAvg >= srcGridMidPoint);
- }
-
-
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
public static class DbReader {
diff --git a/src/com/android/launcher3/model/GridSizeMigrationLogic.kt b/src/com/android/launcher3/model/GridSizeMigrationLogic.kt
index 6f86ae0..dfda8e4 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,34 @@
"$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 ->
+ // We want to add the extra row(s) to the top of the screen, so we shift the grid
+ // down.
+ if (shouldMigrateToStrtictlyTallerGrid) {
+ Log.d(TAG, "Migrating to strictly taller grid")
+ if (oneGridSpecs()) {
+ 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,20 +125,10 @@
// Save current configuration, so that the migration does not run again.
destDeviceState.writeToPrefs(context)
- }
- }
- private fun shouldShiftCells(destReader: DbReader, srcGridRowCount: Int): Boolean {
- val workspaceItems = destReader.loadAllWorkspaceEntries()
- val firstPageItemsRowPosSum =
- workspaceItems.sumOf { entry -> if (entry.screenId == 0) entry.cellY else 0 }
- val firstPageWorkspaceItemsCount = workspaceItems.count { entry -> entry.screenId == 0 }
- if (firstPageWorkspaceItemsCount == 0) {
- return false
+ // Notify if we've migrated successfully
+ modelDelegate.gridMigrationComplete(srcDeviceState, destDeviceState)
}
- val srcGridMidPoint = srcGridRowCount / 2f
- val firstPageItemPosAvg = firstPageItemsRowPosSum / firstPageWorkspaceItemsCount.toFloat()
- return (firstPageItemPosAvg >= srcGridMidPoint)
}
/** Handles hotseat migration. */
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 536d4c9..1623881 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -16,6 +16,11 @@
package com.android.launcher3.model;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
import static com.android.launcher3.Utilities.SHOULD_SHOW_FIRST_PAGE_WIDGET;
import static com.android.launcher3.icons.cache.CacheLookupFlag.DEFAULT_LOOKUP_FLAG;
@@ -48,6 +53,8 @@
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.CollectionInfo;
+import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.IconRequestInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -84,6 +91,11 @@
private final IntArray mRestoredRows = new IntArray();
private final IntSparseArrayMap<GridOccupancy> mOccupied = new IntSparseArrayMap<>();
+ // CollectionInfo objects, which have not yet been loaded from the DB, but are expected to
+ // found eventually as the loading progresses
+ private final IntSparseArrayMap<CollectionInfo> mPendingCollectionInfo =
+ new IntSparseArrayMap<>();
+
private final int mIconIndex;
public final int mTitleIndex;
@@ -479,8 +491,26 @@
info.cellY = getInt(mCellYIndex);
}
- public void checkAndAddItem(ItemInfo info, BgDataModel dataModel) {
- checkAndAddItem(info, dataModel, null);
+ /**
+ * Return an existing FolderInfo object if we have encountered this ID previously,
+ * or make a new one.
+ */
+ public CollectionInfo findOrMakeFolder(int id, BgDataModel dataModel) {
+ // See if a placeholder was created for us already
+ ItemInfo info = dataModel.itemsIdMap.get(id);
+ if (info instanceof CollectionInfo c) return c;
+
+ CollectionInfo pending = mPendingCollectionInfo.get(id);
+ if (pending != null) return pending;
+
+ // No placeholder -- create a new blank folder instance. At this point, we don't know
+ // if the desired container is supposed to be a folder or an app pair. In the case that
+ // it is an app pair, the blank folder will be replaced by a blank app pair when the app
+ // pair is getting processed, in WorkspaceItemProcessor.processFolderOrAppPair().
+ pending = new FolderInfo();
+ pending.id = id;
+ mPendingCollectionInfo.put(id, pending);
+ return pending;
}
/**
@@ -495,7 +525,21 @@
ShortcutKey.fromItemInfo(info);
}
if (checkItemPlacement(info, dataModel.isFirstPagePinnedItemEnabled)) {
- dataModel.addItem(mContext, info, false, logger);
+ if (logger != null) {
+ logger.addLog(
+ Log.DEBUG,
+ TAG,
+ String.format("Adding item to ID map: %s", info),
+ /* stackTrace= */ null);
+ }
+ dataModel.addItem(mContext, info, false);
+ if ((info.itemType == ITEM_TYPE_APP_PAIR
+ || info.itemType == ITEM_TYPE_DEEP_SHORTCUT
+ || info.itemType == ITEM_TYPE_APPLICATION)
+ && info.container != CONTAINER_DESKTOP
+ && info.container != CONTAINER_HOTSEAT) {
+ findOrMakeFolder(info.container, dataModel).add(info);
+ }
if (mRestoreEventLogger != null) {
mRestoreEventLogger.logSingleFavoritesItemRestored(itemType);
}
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 4e57944..fee9696 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -31,7 +31,8 @@
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_CHANGE_PERMISSION;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_WORK_PROFILE_QUIET_MODE_ENABLED;
-import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
+import static com.android.launcher3.model.ModelUtils.WIDGET_FILTER;
+import static com.android.launcher3.model.ModelUtils.currentScreenContentFilter;
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static com.android.launcher3.util.PackageManagerHelper.hasShortcutsPermission;
@@ -82,7 +83,6 @@
import com.android.launcher3.logging.FileLog;
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.IconRequestInfo;
import com.android.launcher3.model.data.ItemInfo;
@@ -210,10 +210,10 @@
final int firstScreen = allScreens.get(0);
IntSet firstScreens = IntSet.wrap(firstScreen);
- ArrayList<ItemInfo> allItems = mBgDataModel.getAllWorkspaceItems();
- ArrayList<ItemInfo> firstScreenItems = new ArrayList<>();
- filterCurrentWorkspaceItems(firstScreens, allItems, firstScreenItems,
- new ArrayList<>() /* otherScreenItems are ignored */);
+ List<ItemInfo> firstScreenItems =
+ mBgDataModel.itemsIdMap.stream()
+ .filter(currentScreenContentFilter(firstScreens))
+ .toList();
final int disableArchivingLauncherBroadcast = Settings.Secure.getInt(
mApp.getContext().getContentResolver(),
"disable_launcher_broadcast_installed_apps",
@@ -227,7 +227,7 @@
mPmHelper,
firstScreenItems,
mInstallingPkgsCached,
- mBgDataModel.appWidgets
+ mBgDataModel.itemsIdMap.stream().filter(WIDGET_FILTER).toList()
);
logASplit("Sending first screen broadcast with additional archiving Extras");
FirstScreenBroadcastHelper.sendBroadcastsForModels(mApp.getContext(), broadcastModels);
@@ -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();
@@ -523,14 +523,13 @@
* requests high-res icons for the items that are part of an app pair.
*/
private void processAppPairItems() {
- for (CollectionInfo collection : mBgDataModel.collections) {
- if (!(collection instanceof AppPairInfo appPair)) {
- continue;
- }
-
- appPair.getContents().sort(Folder.ITEM_POS_COMPARATOR);
- appPair.fetchHiResIconsIfNeeded(mIconCache);
- }
+ mBgDataModel.itemsIdMap.stream()
+ .filter(item -> item instanceof AppPairInfo)
+ .forEach(item -> {
+ AppPairInfo appPair = (AppPairInfo) item;
+ appPair.getContents().sort(Folder.ITEM_POS_COMPARATOR);
+ appPair.fetchHiResIconsIfNeeded(mIconCache);
+ });
}
/**
@@ -586,8 +585,8 @@
// Sort the folder items, update ranks, and make sure all preview items are high res.
List<FolderGridOrganizer> verifiers = mApp.getInvariantDeviceProfile().supportedProfiles
.stream().map(FolderGridOrganizer::createFolderGridOrganizer).toList();
- for (CollectionInfo collection : mBgDataModel.collections) {
- if (!(collection instanceof FolderInfo folder)) {
+ for (ItemInfo itemInfo : mBgDataModel.itemsIdMap) {
+ if (!(itemInfo instanceof FolderInfo folder)) {
continue;
}
@@ -657,8 +656,6 @@
IntArray deletedFolderIds = mApp.getModel().getModelDbController().deleteEmptyFolders();
synchronized (mBgDataModel) {
for (int folderId : deletedFolderIds) {
- mBgDataModel.workspaceItems.remove(mBgDataModel.collections.get(folderId));
- mBgDataModel.collections.remove(folderId);
mBgDataModel.itemsIdMap.remove(folderId);
}
}
@@ -676,8 +673,6 @@
synchronized (mBgDataModel) {
for (int id : deleted) {
- mBgDataModel.workspaceItems.remove(mBgDataModel.collections.get(id));
- mBgDataModel.collections.remove(id);
mBgDataModel.itemsIdMap.remove(id);
}
}
@@ -819,18 +814,19 @@
private void loadFolderNames() {
FolderNameProvider provider = FolderNameProvider.newInstance(mApp.getContext(),
- mBgAllAppsList.data, mBgDataModel.collections);
+ mBgAllAppsList.data, FolderNameProvider.getCollectionForSuggestions(mBgDataModel));
synchronized (mBgDataModel) {
- for (int i = 0; i < mBgDataModel.collections.size(); i++) {
- FolderNameInfos suggestionInfos = new FolderNameInfos();
- CollectionInfo info = mBgDataModel.collections.valueAt(i);
- if (info instanceof FolderInfo fi && fi.suggestedFolderNames == null) {
- provider.getSuggestedFolderName(mApp.getContext(), fi.getAppContents(),
- suggestionInfos);
- fi.suggestedFolderNames = suggestionInfos;
- }
- }
+ mBgDataModel.itemsIdMap.stream()
+ .filter(item ->
+ item instanceof FolderInfo fi && fi.suggestedFolderNames == null)
+ .forEach(info -> {
+ FolderInfo fi = (FolderInfo) info;
+ FolderNameInfos suggestionInfos = new FolderNameInfos();
+ provider.getSuggestedFolderName(mApp.getContext(), fi.getAppContents(),
+ suggestionInfos);
+ fi.suggestedFolderNames = suggestionInfos;
+ });
}
}
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/ModelUtils.java b/src/com/android/launcher3/model/ModelUtils.java
index 9e72e28..da79982 100644
--- a/src/com/android/launcher3/model/ModelUtils.java
+++ b/src/com/android/launcher3/model/ModelUtils.java
@@ -15,15 +15,15 @@
*/
package com.android.launcher3.model;
-import com.android.launcher3.LauncherSettings;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
+
import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSet;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-import java.util.stream.IntStream;
+import java.util.function.Predicate;
/**
* Utils class for {@link com.android.launcher3.LauncherModel}.
@@ -31,54 +31,17 @@
public class ModelUtils {
/**
- * Filters the set of items who are directly or indirectly (via another container) on the
- * specified screen.
+ * Returns a filter for items on hotseat or current screens
*/
- public static <T extends ItemInfo> void filterCurrentWorkspaceItems(
- final IntSet currentScreenIds,
- List<? extends T> allWorkspaceItems,
- List<T> currentScreenItems,
- List<T> otherScreenItems) {
- // Purge any null ItemInfos
- allWorkspaceItems.removeIf(Objects::isNull);
- // Order the set of items by their containers first, this allows use to walk through the
- // list sequentially, build up a list of containers that are in the specified screen,
- // as well as all items in those containers.
- IntSet itemsOnScreen = new IntSet();
- Collections.sort(allWorkspaceItems,
- (lhs, rhs) -> Integer.compare(lhs.container, rhs.container));
- for (T info : allWorkspaceItems) {
- if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
- if (currentScreenIds.contains(info.screenId)) {
- currentScreenItems.add(info);
- itemsOnScreen.add(info.id);
- } else {
- otherScreenItems.add(info);
- }
- } else if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
- currentScreenItems.add(info);
- itemsOnScreen.add(info.id);
- } else {
- if (itemsOnScreen.contains(info.container)) {
- currentScreenItems.add(info);
- itemsOnScreen.add(info.id);
- } else {
- otherScreenItems.add(info);
- }
- }
- }
+ public static Predicate<ItemInfo> currentScreenContentFilter(IntSet currentScreenIds) {
+ return item -> item.container == CONTAINER_HOTSEAT
+ || (item.container == CONTAINER_DESKTOP
+ && currentScreenIds.contains(item.screenId));
}
/**
- * Iterates though current workspace items and returns available hotseat ranks for prediction.
+ * Returns a filter for widget items
*/
- public static IntArray getMissingHotseatRanks(List<ItemInfo> items, int len) {
- IntSet seen = new IntSet();
- items.stream().filter(
- info -> info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT)
- .forEach(i -> seen.add(i.screenId));
- IntArray result = new IntArray(len);
- IntStream.range(0, len).filter(i -> !seen.contains(i)).forEach(result::add);
- return result;
- }
+ public static final Predicate<ItemInfo> WIDGET_FILTER = item ->
+ item.itemType == ITEM_TYPE_APPWIDGET || item.itemType == ITEM_TYPE_CUSTOM_APPWIDGET;
}
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
index b477cb1..0332775 100644
--- a/src/com/android/launcher3/model/ModelWriter.java
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -459,37 +459,14 @@
if (item.container != Favorites.CONTAINER_DESKTOP &&
item.container != Favorites.CONTAINER_HOTSEAT) {
// Item is in a collection, make sure this collection exists
- if (!mBgDataModel.collections.containsKey(item.container)) {
+ if (!(mBgDataModel.itemsIdMap.get(item.container) instanceof CollectionInfo)) {
// An items container is being set to a that of an item which is not in
- // the list of Folders.
+ // the list of collections.
String msg = "item: " + item + " container being set to: " +
item.container + ", not in the list of collections";
Log.e(TAG, msg);
}
}
-
- // Items are added/removed from the corresponding FolderInfo elsewhere, such
- // as in Workspace.onDrop. Here, we just add/remove them from the list of items
- // that are on the desktop, as appropriate
- ItemInfo modelItem = mBgDataModel.itemsIdMap.get(itemId);
- if (modelItem != null &&
- (modelItem.container == Favorites.CONTAINER_DESKTOP ||
- modelItem.container == Favorites.CONTAINER_HOTSEAT)) {
- switch (modelItem.itemType) {
- case Favorites.ITEM_TYPE_APPLICATION:
- case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
- case Favorites.ITEM_TYPE_FOLDER:
- case Favorites.ITEM_TYPE_APP_PAIR:
- if (!mBgDataModel.workspaceItems.contains(modelItem)) {
- mBgDataModel.workspaceItems.add(modelItem);
- }
- break;
- default:
- break;
- }
- } else {
- mBgDataModel.workspaceItems.remove(modelItem);
- }
mVerifier.verifyModel();
}
}
diff --git a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
index d238213..4103937 100644
--- a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
+++ b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.model;
+import static com.android.launcher3.model.ModelUtils.WIDGET_FILTER;
+
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -85,12 +87,16 @@
}
});
- for (LauncherAppWidgetInfo widget : dataModel.appWidgets) {
- if (widget.providerName.getPackageName().equals(mInstallInfo.packageName)) {
- widget.installProgress = mInstallInfo.progress;
- updates.add(widget);
- }
- }
+ dataModel.itemsIdMap.stream()
+ .filter(WIDGET_FILTER)
+ .filter(item -> mInstallInfo.user.equals(item.user))
+ .map(item -> (LauncherAppWidgetInfo) item)
+ .filter(widget -> widget.providerName.getPackageName()
+ .equals(mInstallInfo.packageName))
+ .forEach(widget -> {
+ widget.installProgress = mInstallInfo.progress;
+ updates.add(widget);
+ });
if (!updates.isEmpty()) {
taskController.scheduleCallbackTask(
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index d619965..1153f48 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -18,7 +18,9 @@
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_WORK_PROFILE_QUIET_MODE_ENABLED;
+import static com.android.launcher3.model.ModelUtils.WIDGET_FILTER;
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_ARCHIVED;
+import static com.android.launcher3.model.data.LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
import static com.android.launcher3.model.data.WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON;
import static com.android.launcher3.model.data.WorkspaceItemInfo.FLAG_RESTORED_ICON;
@@ -347,24 +349,25 @@
}
});
- for (LauncherAppWidgetInfo widgetInfo : dataModel.appWidgets) {
- if (mUser.equals(widgetInfo.user)
- && widgetInfo.hasRestoreFlag(
- LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
- && packageSet.contains(widgetInfo.providerName.getPackageName())) {
- widgetInfo.restoreStatus &=
- ~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY
- & ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
+ dataModel.itemsIdMap.stream()
+ .filter(WIDGET_FILTER)
+ .filter(item -> mUser.equals(item.user))
+ .map(item -> (LauncherAppWidgetInfo) item)
+ .filter(widget -> widget.hasRestoreFlag(FLAG_PROVIDER_NOT_READY)
+ && packageSet.contains(widget.providerName.getPackageName()))
+ .forEach(widgetInfo -> {
+ widgetInfo.restoreStatus &=
+ ~FLAG_PROVIDER_NOT_READY
+ & ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
- // adding this flag ensures that launcher shows 'click to setup'
- // if the widget has a config activity. In case there is no config
- // activity, it will be marked as 'restored' during bind.
- widgetInfo.restoreStatus |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
+ // adding this flag ensures that launcher shows 'click to setup'
+ // if the widget has a config activity. In case there is no config
+ // activity, it will be marked as 'restored' during bind.
+ widgetInfo.restoreStatus |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
- widgets.add(widgetInfo);
- taskController.getModelWriter().updateItemInDatabase(widgetInfo);
- }
- }
+ widgets.add(widgetInfo);
+ taskController.getModelWriter().updateItemInDatabase(widgetInfo);
+ });
}
taskController.bindUpdatedWorkspaceItems(updatedWorkspaceItems);
diff --git a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
index 0272bd9..de1df2e 100644
--- a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
+++ b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
@@ -32,7 +32,6 @@
import com.android.launcher3.LauncherAppState
import com.android.launcher3.LauncherSettings.Favorites
import com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RestoreError
-import com.android.launcher3.config.FeatureFlags
import com.android.launcher3.icons.CacheableShortcutInfo
import com.android.launcher3.icons.cache.CacheLookupFlag.Companion.DEFAULT_LOOKUP_FLAG
import com.android.launcher3.logging.FileLog
@@ -405,24 +404,14 @@
* stored in the BgDataModel.
*/
private fun processFolderOrAppPair() {
- var collection = bgDataModel.findOrMakeFolder(c.id)
+ var collection = c.findOrMakeFolder(c.id, bgDataModel)
// If we generated a placeholder Folder before this point, it may need to be replaced with
// an app pair.
if (c.itemType == Favorites.ITEM_TYPE_APP_PAIR && collection is FolderInfo) {
- if (!FeatureFlags.enableAppPairs()) {
- // If app pairs are not enabled, stop loading.
- Log.e(TAG, "app pairs flag is off, did not load app pair")
- return
- }
-
- val folderInfo: FolderInfo = collection
val newAppPair = AppPairInfo()
// Move the placeholder's contents over to the new app pair.
- folderInfo.getContents().forEach(newAppPair::add)
+ collection.getContents().forEach(newAppPair::add)
collection = newAppPair
- // Remove the placeholder and add the app pair into the data model.
- bgDataModel.collections.remove(c.id)
- bgDataModel.collections.put(c.id, collection)
}
c.applyCommonProperties(collection)
@@ -576,7 +565,7 @@
logWidgetInfo(app.invariantDeviceProfile, lapi)
}
}
- c.checkAndAddItem(appWidgetInfo, bgDataModel)
+ c.checkAndAddItem(appWidgetInfo, bgDataModel, memoryLogger)
}
companion object {
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/model/data/ItemInfoWithIcon.java b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
index 772ea7f..7fb0152 100644
--- a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
+++ b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
@@ -16,6 +16,8 @@
package com.android.launcher3.model.data;
+import static com.android.launcher3.icons.BitmapInfo.FLAG_THEMED;
+
import android.content.Context;
import android.content.Intent;
import android.os.Process;
@@ -23,6 +25,7 @@
import androidx.annotation.Nullable;
import com.android.launcher3.Flags;
+import com.android.launcher3.graphics.ThemeManager;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.BitmapInfo.DrawableCreationFlags;
import com.android.launcher3.icons.FastBitmapDrawable;
@@ -320,6 +323,9 @@
* Returns a FastBitmapDrawable with the icon and context theme applied
*/
public FastBitmapDrawable newIcon(Context context, @DrawableCreationFlags int creationFlags) {
+ if (!ThemeManager.INSTANCE.get(context).isMonoThemeEnabled()) {
+ creationFlags &= ~FLAG_THEMED;
+ }
FastBitmapDrawable drawable = bitmap.newIcon(context, creationFlags);
drawable.setIsDisabled(isDisabled());
return drawable;
diff --git a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
index 0a5dd62..9a7c347 100644
--- a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
+++ b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
@@ -36,6 +36,7 @@
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.ApiWrapper;
import com.android.launcher3.util.ContentWriter;
+import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
import java.util.Arrays;
@@ -178,7 +179,7 @@
public void updateFromDeepShortcutInfo(@NonNull final ShortcutInfo shortcutInfo,
@NonNull final Context context) {
- if (com.android.wm.shell.Flags.enableBubbleAnything()) {
+ if (BubbleAnythingFlagHelper.enableCreateAnyBubble()) {
mShortcutInfo = shortcutInfo;
}
// {@link ShortcutInfo#getActivity} can change during an update. Recreate the intent
diff --git a/src/com/android/launcher3/notification/NotificationListener.java b/src/com/android/launcher3/notification/NotificationListener.java
index 836ea4a..864fed0 100644
--- a/src/com/android/launcher3/notification/NotificationListener.java
+++ b/src/com/android/launcher3/notification/NotificationListener.java
@@ -170,6 +170,9 @@
for (NotificationsChangedListener listener : sNotificationsChangedListeners) {
listener.onNotificationPosted(msg.first, msg.second);
}
+ Log.i(TAG, "received notification posted event - " + msg.first);
+ } else {
+ Log.i(TAG, "received notification posted event, but there are no listeners");
}
break;
case MSG_NOTIFICATION_REMOVED:
@@ -178,6 +181,9 @@
for (NotificationsChangedListener listener : sNotificationsChangedListeners) {
listener.onNotificationRemoved(msg.first, msg.second);
}
+ Log.i(TAG, "received notification removed event - " + msg.first);
+ } else {
+ Log.i(TAG, "received notification removed event, but there are no listeners");
}
break;
case MSG_NOTIFICATION_FULL_REFRESH:
@@ -186,6 +192,11 @@
listener.onNotificationFullRefresh(
(List<StatusBarNotification>) message.obj);
}
+ ((List<StatusBarNotification>) message.obj).forEach(sbn -> Log.i(TAG,
+ "Handling notification state refresh for " + sbn.getPackageName() + "#"
+ + sbn.getUserId()));
+ } else {
+ Log.i(TAG, "received notification refresh event, but there are no listeners");
}
break;
}
@@ -205,6 +216,7 @@
@Override
public void onListenerConnected() {
super.onListenerConnected();
+ Log.i(TAG, "onListenerConnected");
sIsConnected = true;
// Register an observer to rebind the notification listener when dots are re-enabled.
@@ -230,6 +242,7 @@
@Override
public void onListenerDisconnected() {
super.onListenerDisconnected();
+ Log.i(TAG, "onListenerDisconnected");
sIsConnected = false;
mSettingsCache.unregister(NOTIFICATION_BADGING_URI, mNotificationSettingsChangedListener);
onNotificationFullRefresh();
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index 329d9df..7e08c6e 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -431,7 +431,7 @@
&& !(itemInfo instanceof WorkspaceItemInfo)) {
return null;
}
- return new BubbleShortcut(activity, itemInfo, originalView);
+ return new BubbleShortcut<>(activity, itemInfo, originalView);
};
public interface BubbleActivityStarter {
@@ -439,7 +439,7 @@
void showShortcutBubble(ShortcutInfo info);
/** Tell SysUI to show the provided intent in a bubble. */
- void showAppBubble(Intent intent);
+ void showAppBubble(Intent intent, UserHandle user);
}
public static class BubbleShortcut<T extends ActivityContext> extends SystemShortcut<T> {
@@ -476,7 +476,7 @@
if (intent.getPackage() == null) {
intent.setPackage(mItemInfo.getTargetPackage());
}
- mStarter.showAppBubble(intent);
+ mStarter.showAppBubble(intent, mItemInfo.user);
} else {
Log.w(TAG, "unable to bubble, no intent: " + mItemInfo);
}
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..943a913 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -15,12 +15,12 @@
*/
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 +243,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:
@@ -325,8 +325,9 @@
return response;
}
- case TestProtocol.REQUEST_FLAG_ENABLE_APP_PAIRS: {
- response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD, enableAppPairs());
+ case TestProtocol.REQUEST_IS_RECENTS_WINDOW_ENABLED: {
+ response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD,
+ enableLauncherOverviewInWindow() || enableFallbackOverviewInWindow());
return response;
}
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 d8a2a3d..9472f5f 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -53,6 +53,9 @@
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;
@@ -67,11 +70,14 @@
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";
@@ -82,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;
@@ -101,6 +107,7 @@
private static final String TARGET_OVERLAY_PACKAGE = "android";
private final Context mContext;
+ private final WindowManagerProxy mWMProxy;
// Null for SDK < S
private final Context mWindowContext;
@@ -117,13 +124,31 @@
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;
+ 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 = context.getSystemService(DisplayManager.class)
@@ -134,31 +159,17 @@
// 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);
+ });
}
/**
@@ -208,20 +219,6 @@
}
@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);
- }
- mWindowContext.unregisterComponentCallbacks(this);
- mReceiver.unregisterReceiverSafely(mContext);
- WindowManagerProxy.INSTANCE.get(mContext).unregisterDesktopVisibilityListener(this);
- }
-
- @Override
public void onDesktopVisibilityChanged(boolean visible) {
notifyConfigChange();
}
@@ -259,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();
}
@@ -286,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/IntSparseArrayMap.java b/src/com/android/launcher3/util/IntSparseArrayMap.java
index 9d5391b..70f74e3 100644
--- a/src/com/android/launcher3/util/IntSparseArrayMap.java
+++ b/src/com/android/launcher3/util/IntSparseArrayMap.java
@@ -19,6 +19,8 @@
import android.util.SparseArray;
import java.util.Iterator;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
/**
* Extension of {@link SparseArray} with some utility methods.
@@ -43,6 +45,10 @@
return new ValueIterator();
}
+ public Stream<E> stream() {
+ return StreamSupport.stream(spliterator(), false);
+ }
+
@Thunk class ValueIterator implements Iterator<E> {
private int mNextIndex = 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..8559f3b
--- /dev/null
+++ b/src/com/android/launcher3/util/LayoutImportExportHelper.kt
@@ -0,0 +1,136 @@
+/*
+ * 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.itemsIdMap.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)
+ }
+
+ 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,
+ )
+ }
+ }
+}
diff --git a/src/com/android/launcher3/util/LockedUserState.kt b/src/com/android/launcher3/util/LockedUserState.kt
index c8d86d4..a6a6ceb 100644
--- a/src/com/android/launcher3/util/LockedUserState.kt
+++ b/src/com/android/launcher3/util/LockedUserState.kt
@@ -20,10 +20,17 @@
import android.os.Process
import android.os.UserManager
import androidx.annotation.VisibleForTesting
+import com.android.launcher3.dagger.ApplicationContext
+import com.android.launcher3.dagger.LauncherAppComponent
+import com.android.launcher3.dagger.LauncherAppSingleton
import com.android.launcher3.util.Executors.MAIN_EXECUTOR
import com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR
+import javax.inject.Inject
-class LockedUserState(private val mContext: Context) : SafeCloseable {
+@LauncherAppSingleton
+class LockedUserState
+@Inject
+constructor(@ApplicationContext private val context: Context, lifeCycle: DaggerSingletonTracker) {
val isUserUnlockedAtLauncherStartup: Boolean
var isUserUnlocked = false
private set(value) {
@@ -36,7 +43,7 @@
private val mUserUnlockedActions: RunnableList = RunnableList()
@VisibleForTesting
- val mUserUnlockedReceiver =
+ val userUnlockedReceiver =
SimpleBroadcastReceiver(UI_HELPER_EXECUTOR) {
if (Intent.ACTION_USER_UNLOCKED == it.action) {
isUserUnlocked = true
@@ -53,8 +60,8 @@
isUserUnlocked = checkIsUserUnlocked()
isUserUnlockedAtLauncherStartup = isUserUnlocked
if (!isUserUnlocked) {
- mUserUnlockedReceiver.register(
- mContext,
+ userUnlockedReceiver.register(
+ context,
{
// If user is unlocked while registering broadcast receiver, we should update
// [isUserUnlocked], which will call [notifyUserUnlocked] in setter
@@ -62,22 +69,18 @@
MAIN_EXECUTOR.execute { isUserUnlocked = true }
}
},
- Intent.ACTION_USER_UNLOCKED
+ Intent.ACTION_USER_UNLOCKED,
)
}
+ lifeCycle.addCloseable { userUnlockedReceiver.unregisterReceiverSafely(context) }
}
private fun checkIsUserUnlocked() =
- mContext.getSystemService(UserManager::class.java)!!.isUserUnlocked(Process.myUserHandle())
+ context.getSystemService(UserManager::class.java)!!.isUserUnlocked(Process.myUserHandle())
private fun notifyUserUnlocked() {
mUserUnlockedActions.executeAllAndDestroy()
- mUserUnlockedReceiver.unregisterReceiverSafely(mContext)
- }
-
- /** Stops the receiver from listening for ACTION_USER_UNLOCK broadcasts. */
- override fun close() {
- mUserUnlockedReceiver.unregisterReceiverSafely(mContext)
+ userUnlockedReceiver.unregisterReceiverSafely(context)
}
/**
@@ -88,9 +91,7 @@
mUserUnlockedActions.add(action)
}
- /**
- * Removes a previously queued `Runnable` to be run when the user is unlocked.
- */
+ /** Removes a previously queued `Runnable` to be run when the user is unlocked. */
fun removeOnUserUnlockedRunnable(action: Runnable) {
mUserUnlockedActions.remove(action)
}
@@ -98,7 +99,7 @@
companion object {
@VisibleForTesting
@JvmField
- val INSTANCE = MainThreadInitializedObject { LockedUserState(it) }
+ val INSTANCE = DaggerSingletonObject(LauncherAppComponent::getLockedUserState)
@JvmStatic fun get(context: Context): LockedUserState = INSTANCE.get(context)
}
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/SettingsCache.java b/src/com/android/launcher3/util/SettingsCache.java
index 8fe6e93..fa183c8 100644
--- a/src/com/android/launcher3/util/SettingsCache.java
+++ b/src/com/android/launcher3/util/SettingsCache.java
@@ -34,11 +34,11 @@
import com.android.launcher3.dagger.LauncherAppSingleton;
import com.android.launcher3.dagger.LauncherBaseAppComponent;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.function.Function;
import javax.inject.Inject;
@@ -57,7 +57,7 @@
* Cache will also be updated if a key queried is missing (even if it has no listeners registered).
*/
@LauncherAppSingleton
-public class SettingsCache extends ContentObserver implements SafeCloseable {
+public class SettingsCache extends ContentObserver {
/** Hidden field Settings.Secure.NOTIFICATION_BADGING */
public static final Uri NOTIFICATION_BADGING_URI =
@@ -79,11 +79,17 @@
private static final String SYSTEM_URI_PREFIX = Settings.System.CONTENT_URI.toString();
private static final String GLOBAL_URI_PREFIX = Settings.Global.CONTENT_URI.toString();
+ private final Function<Uri, CopyOnWriteArrayList<OnChangeListener>> mListenerMapper = uri -> {
+ registerUriAsync(uri);
+ return new CopyOnWriteArrayList<>();
+ };
+
/**
* Caches the last seen value for registered keys.
*/
- private Map<Uri, Boolean> mKeyCache = new ConcurrentHashMap<>();
- private final Map<Uri, CopyOnWriteArrayList<OnChangeListener>> mListenerMap = new HashMap<>();
+ private final Map<Uri, Boolean> mKeyCache = new ConcurrentHashMap<>();
+ private final Map<Uri, CopyOnWriteArrayList<OnChangeListener>> mListenerMap =
+ new ConcurrentHashMap<>();
protected final ContentResolver mResolver;
/**
@@ -96,12 +102,8 @@
SettingsCache(@ApplicationContext Context context, DaggerSingletonTracker tracker) {
super(new Handler(Looper.getMainLooper()));
mResolver = context.getContentResolver();
- tracker.addCloseable(this);
- }
-
- @Override
- public void close() {
- UI_HELPER_EXECUTOR.execute(() -> mResolver.unregisterContentObserver(this));
+ tracker.addCloseable(() ->
+ UI_HELPER_EXECUTOR.execute(() -> mResolver.unregisterContentObserver(this)));
}
@Override
@@ -109,11 +111,12 @@
// We use default of 1, but if we're getting an onChange call, can assume a non-default
// value will exist
boolean newVal = updateValue(uri, 1 /* Effectively Unused */);
- if (!mListenerMap.containsKey(uri)) {
+ List<OnChangeListener> listeners = mListenerMap.get(uri);
+ if (listeners == null) {
return;
}
- for (OnChangeListener listener : mListenerMap.get(uri)) {
+ for (OnChangeListener listener : listeners) {
listener.onSettingsChanged(newVal);
}
}
@@ -138,22 +141,17 @@
}
}
+ private void registerUriAsync(Uri uri) {
+ UI_HELPER_EXECUTOR.execute(() -> mResolver.registerContentObserver(uri, false, this));
+ }
+
/**
* Does not de-dupe if you add same listeners for the same key multiple times.
* Unregister once complete using {@link #unregister(Uri, OnChangeListener)}
*/
@UiThread
public void register(Uri uri, OnChangeListener changeListener) {
- Preconditions.assertUIThread();
- if (mListenerMap.containsKey(uri)) {
- mListenerMap.get(uri).add(changeListener);
- } else {
- CopyOnWriteArrayList<OnChangeListener> l = new CopyOnWriteArrayList<>();
- l.add(changeListener);
- mListenerMap.put(uri, l);
- UI_HELPER_EXECUTOR.execute(
- () -> mResolver.registerContentObserver(uri, false, this));
- }
+ mListenerMap.computeIfAbsent(uri, mListenerMapper).add(changeListener);
}
private boolean updateValue(Uri keyUri, int defaultValue) {
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/ViewPool.java b/src/com/android/launcher3/util/ViewPool.java
index 2fa8bf4..1627057 100644
--- a/src/com/android/launcher3/util/ViewPool.java
+++ b/src/com/android/launcher3/util/ViewPool.java
@@ -17,6 +17,7 @@
import android.content.Context;
import android.os.Handler;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -33,6 +34,7 @@
* During initialization, views are inflated on the background thread.
*/
public class ViewPool<T extends View & Reusable> {
+ private static final String TAG = ViewPool.class.getSimpleName();
private final Object[] mPool;
@@ -42,6 +44,9 @@
private int mCurrentSize = 0;
+ @Nullable
+ private Thread mViewPoolInitThread;
+
public ViewPool(Context context, @Nullable ViewGroup parent,
int layoutId, int maxSize, int initialSize) {
this(LayoutInflater.from(context).cloneInContext(context),
@@ -72,12 +77,15 @@
// Inflate views on a non looper thread. This allows us to catch errors like calling
// "new Handler()" in constructor easily.
- new Thread(() -> {
+ mViewPoolInitThread = new Thread(() -> {
for (int i = 0; i < initialSize; i++) {
T view = inflateNewView(inflater);
handler.post(() -> addToPool(view));
}
- }, "ViewPool-init").start();
+ Log.d(TAG, "initPool complete");
+ mViewPoolInitThread = null;
+ }, "ViewPool-init");
+ mViewPoolInitThread.start();
}
@UiThread
@@ -114,6 +122,12 @@
return (T) inflater.inflate(mLayoutId, mParent, false);
}
+ public void killOngoingInitializations() throws InterruptedException {
+ if (mViewPoolInitThread != null) {
+ mViewPoolInitThread.join();
+ }
+ }
+
/**
* Interface to indicate that a view is reusable
*/
diff --git a/src/com/android/launcher3/util/WallpaperColorHints.kt b/src/com/android/launcher3/util/WallpaperColorHints.kt
index 11d4c25..29fe31a 100644
--- a/src/com/android/launcher3/util/WallpaperColorHints.kt
+++ b/src/com/android/launcher3/util/WallpaperColorHints.kt
@@ -23,14 +23,21 @@
import android.content.Context
import androidx.annotation.MainThread
import androidx.annotation.VisibleForTesting
+import com.android.launcher3.dagger.ApplicationContext
+import com.android.launcher3.dagger.LauncherAppComponent
+import com.android.launcher3.dagger.LauncherAppSingleton
import com.android.launcher3.util.Executors.MAIN_EXECUTOR
import com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR
+import javax.inject.Inject
/**
* This class caches the system's wallpaper color hints for use by other classes as a performance
* enhancer. It also centralizes all the WallpaperManager color hint code in one location.
*/
-class WallpaperColorHints(private val context: Context) : SafeCloseable {
+@LauncherAppSingleton
+class WallpaperColorHints
+@Inject
+constructor(@ApplicationContext private val context: Context, tracker: DaggerSingletonTracker) {
var hints: Int = 0
private set
@@ -38,7 +45,6 @@
get() = context.getSystemService(WallpaperManager::class.java)!!
private val onColorHintsChangedListeners = mutableListOf<OnColorHintListener>()
- private val onClose: SafeCloseable
init {
hints = wallpaperManager.getWallpaperColors(FLAG_SYSTEM)?.colorHints ?: 0
@@ -51,7 +57,7 @@
MAIN_EXECUTOR.handler,
)
}
- onClose = SafeCloseable {
+ tracker.addCloseable {
UI_HELPER_EXECUTOR.execute {
wallpaperManager.removeOnColorsChangedListener(onColorsChangedListener)
}
@@ -69,8 +75,6 @@
}
}
- override fun close() = onClose.close()
-
fun registerOnColorHintsChangedListener(listener: OnColorHintListener) {
onColorHintsChangedListeners.add(listener)
}
@@ -82,7 +86,7 @@
companion object {
@VisibleForTesting
@JvmField
- val INSTANCE = MainThreadInitializedObject { WallpaperColorHints(it) }
+ val INSTANCE = DaggerSingletonObject(LauncherAppComponent::getWallpaperColorHints)
@JvmStatic fun get(context: Context): WallpaperColorHints = INSTANCE.get(context)
}
diff --git a/src/com/android/launcher3/views/ArrowTipView.java b/src/com/android/launcher3/views/ArrowTipView.java
index bb4f040..abb0081 100644
--- a/src/com/android/launcher3/views/ArrowTipView.java
+++ b/src/com/android/launcher3/views/ArrowTipView.java
@@ -73,7 +73,7 @@
}
};
- private final ActivityContext mActivityContext;
+ protected final ActivityContext mActivityContext;
private final Handler mHandler = new Handler();
private boolean mIsPointingUp;
private Runnable mOnClosed;
@@ -103,16 +103,26 @@
R.dimen.arrow_toast_arrow_width);
mArrowMinOffset = context.getResources().getDimensionPixelSize(
R.dimen.dynamic_grid_cell_border_spacing);
- TypedArray ta = context.obtainStyledAttributes(R.styleable.ArrowTipView);
+ Context localContext = context;
+ TypedArray ta = localContext.obtainStyledAttributes(R.styleable.ArrowTipView);
// Set style to default to avoid inflation issues with missing attributes.
if (!ta.hasValue(R.styleable.ArrowTipView_arrowTipBackground)
|| !ta.hasValue(R.styleable.ArrowTipView_arrowTipTextColor)) {
- context = new ContextThemeWrapper(context, R.style.ArrowTipStyle);
+ localContext = new ContextThemeWrapper(localContext, R.style.ArrowTipStyle);
}
- mArrowViewPaintColor = ta.getColor(R.styleable.ArrowTipView_arrowTipBackground,
+ mArrowViewPaintColor = applyArrowPaintColor(ta, localContext);
+ init(localContext, layoutId);
+ }
+
+ protected int applyArrowPaintColor(TypedArray typedArray, Context context) {
+ int arrowPaintColor = typedArray.getColor(R.styleable.ArrowTipView_arrowTipBackground,
context.getColor(R.color.arrow_tip_view_bg));
- ta.recycle();
- init(context, layoutId);
+ typedArray.recycle();
+ return arrowPaintColor;
+ }
+
+ protected int getArrowId() {
+ return R.id.arrow;
}
@Override
@@ -154,7 +164,7 @@
inflate(context, layoutId, this);
setOrientation(LinearLayout.VERTICAL);
- mArrowView = findViewById(R.id.arrow);
+ mArrowView = findViewById(getArrowId());
updateArrowTipInView(mIsPointingUp);
setAlpha(0);
@@ -343,6 +353,34 @@
parent.addView(this);
requestLayout();
}
+ return showAtLocation(arrowXCoord, yCoordDownPointingTip, yCoordUpPointingTip,
+ minViewMargin, parentViewWidth, parentViewHeight, shouldAutoClose);
+ }
+
+ /**
+ * Show the ArrowTipView (tooltip) custom aligned. The tooltip is vertically flipped if it
+ * cannot fit on screen in the requested orientation.
+ *
+ * @param arrowXCoord The X coordinate for the arrow on the tooltip. The arrow is usually in the
+ * center of tooltip unless the tooltip goes beyond screen margin.
+ * @param yCoordDownPointingTip The Y coordinate of the pointed tip end of the tooltip when the
+ * tooltip is placed pointing downwards.
+ * @param yCoordUpPointingTip The Y coordinate of the pointed tip end of the tooltip when the
+ * tooltip is placed pointing upwards.
+ * @param minViewMargin The view margin in pixels from the tip end to the y coordinate.
+ * @param parentViewWidth The width in pixels of the parent view.
+ * @param parentViewHeight The height in pixels of the parent view.
+ * @param shouldAutoClose If Tooltip should be auto close.
+ * @return The tool tip view. {@code null} if the tip can not be shown.
+ */
+ protected ArrowTipView showAtLocation(
+ @Px int arrowXCoord,
+ @Px int yCoordDownPointingTip,
+ @Px int yCoordUpPointingTip,
+ @Px int minViewMargin,
+ @Px int parentViewWidth,
+ @Px int parentViewHeight,
+ boolean shouldAutoClose) {
post(() -> {
// Adjust the tooltip horizontally.
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/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index d850fc6..ab0f9a7 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -577,14 +577,11 @@
public void exitSearchMode() {
if (!mIsInSearchMode) return;
onSearchResults(new ArrayList<>());
- WidgetsRecyclerView searchRecyclerView = mAdapters.get(
- AdapterHolder.SEARCH).mWidgetsRecyclerView;
// Remove all views when exiting the search mode; this prevents animating from stale results
// to new ones the next time we enter search mode. By the time recycler view is hidden,
// layout may not have happened to clear up existing results. So, instead of waiting for it
// to happen, we clear the views here.
- searchRecyclerView.swapAdapter(
- searchRecyclerView.getAdapter(), /*removeAndRecycleExistingViews=*/ true);
+ mAdapters.get(AdapterHolder.SEARCH).reset();
setViewVisibilityBasedOnSearch(/*isInSearchMode=*/ false);
if (mHasWorkProfile) {
mViewPager.snapToPage(AdapterHolder.PRIMARY);
@@ -613,13 +610,12 @@
mNoWidgetsView.setVisibility(GONE);
} else {
mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView.setVisibility(GONE);
- mAdapters.get(getCurrentAdapterHolderType()).mWidgetsRecyclerView.setVisibility(
- VISIBLE);
- if (mRecommendedWidgetsCount > 0) {
- // Display recommendations immediately, if present, so that other parts of sticky
- // header (e.g. personal / work tabs) don't flash in interim.
- mWidgetRecommendationsContainer.setVisibility(VISIBLE);
- }
+ AdapterHolder currentAdapterHolder = mAdapters.get(getCurrentAdapterHolderType());
+ // Remove all views when exiting the search mode; this prevents animating / flashing old
+ // list position / state.
+ currentAdapterHolder.reset();
+ currentAdapterHolder.mWidgetsRecyclerView.setVisibility(VISIBLE);
+ post(this::onRecommendedWidgetsBound);
// Visibility of recycler views and headers are handled in methods below.
onWidgetsBound();
}
@@ -1126,6 +1122,21 @@
mWidgetsListItemAnimator = new WidgetsListItemAnimator();
}
+ /**
+ * Swaps the adapter to existing adapter to prevent the recycler view from using stale view
+ * to animate in the new visibility update.
+ *
+ * <p>For instance, when clearing search text and re-entering search with new list shouldn't
+ * use stale results to animate in new results. Alternative is setting list animators to
+ * null, but, we need animations with the default item animator.
+ */
+ private void reset() {
+ mWidgetsRecyclerView.swapAdapter(
+ mWidgetsListAdapter,
+ /*removeAndRecycleExistingViews=*/ true
+ );
+ }
+
private int getEmptySpaceHeight() {
return mStickyHeaderLayout != null ? mStickyHeaderLayout.getHeaderHeight() : 0;
}
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/src_no_quickstep/com/android/launcher3/dagger/Modules.kt b/src_no_quickstep/com/android/launcher3/dagger/Modules.kt
new file mode 100644
index 0000000..c3bf7c5
--- /dev/null
+++ b/src_no_quickstep/com/android/launcher3/dagger/Modules.kt
@@ -0,0 +1,32 @@
+/*
+ * 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 dagger.Module
+
+private object Modules {}
+
+@Module abstract class WindowManagerProxyModule {}
+
+@Module abstract class ApiWrapperModule {}
+
+@Module abstract class PluginManagerWrapperModule {}
+
+@Module object StaticObjectModule {}
+
+// Module containing bindings for the final derivative app
+@Module abstract class AppModule {}
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/AndroidManifest.xml b/tests/AndroidManifest.xml
index 5cf96c8..862b862 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -29,6 +29,8 @@
android:functionalTest="false"
android:handleProfiling="false"
android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.launcher3" >
+ android:targetPackage="com.android.launcher3" >
+ <meta-data android:name="listener"
+ android:value="com.android.launcher3.util.GlobalTestRunListener"/>
</instrumentation>
</manifest>
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/assets/databases/GridMigrationTest/result5x5to5x8.db b/tests/assets/databases/GridMigrationTest/result5x5to5x8.db
index 311a112..d750774 100644
--- a/tests/assets/databases/GridMigrationTest/result5x5to5x8.db
+++ b/tests/assets/databases/GridMigrationTest/result5x5to5x8.db
Binary files differ
diff --git a/tests/assets/databases/GridMigrationTest/result5x5to5x8WithShift.db b/tests/assets/databases/GridMigrationTest/result5x5to5x8WithShift.db
deleted file mode 100644
index d750774..0000000
--- a/tests/assets/databases/GridMigrationTest/result5x5to5x8WithShift.db
+++ /dev/null
Binary files differ
diff --git a/tests/assets/databases/GridMigrationTest/test_launcher_2.db b/tests/assets/databases/GridMigrationTest/test_launcher_2.db
deleted file mode 100644
index b538e26..0000000
--- a/tests/assets/databases/GridMigrationTest/test_launcher_2.db
+++ /dev/null
Binary files differ
diff --git a/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt b/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
index 6676766..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)
@@ -312,8 +312,8 @@
DaggerAbsDPTestSandboxComponent.builder()
.bindWMProxy(windowManagerProxy)
.bindLauncherPrefs(launcherPrefs)
+ .bindDisplayController(displayController)
)
- context.putObject(DisplayController.INSTANCE, displayController)
whenever(launcherPrefs.get(LauncherPrefs.TASKBAR_PINNING)).thenReturn(false)
whenever(launcherPrefs.get(LauncherPrefs.TASKBAR_PINNING_IN_DESKTOP_MODE)).thenReturn(true)
@@ -364,20 +364,18 @@
}
}
-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/src/com/android/launcher3/allapps/FloatingMaskViewTest.kt b/tests/multivalentTests/src/com/android/launcher3/allapps/FloatingMaskViewTest.kt
similarity index 83%
rename from tests/src/com/android/launcher3/allapps/FloatingMaskViewTest.kt
rename to tests/multivalentTests/src/com/android/launcher3/allapps/FloatingMaskViewTest.kt
index cf03adc..7ddd859 100644
--- a/tests/src/com/android/launcher3/allapps/FloatingMaskViewTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/allapps/FloatingMaskViewTest.kt
@@ -20,28 +20,33 @@
import android.view.ViewGroup.MarginLayoutParams
import android.widget.ImageView
import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.launcher3.util.ActivityContextWrapper
import com.google.common.truth.Truth
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.MockitoAnnotations
+@RunWith(AndroidJUnit4::class)
class FloatingMaskViewTest {
- @Mock
- private val mockAllAppsRecyclerView: AllAppsRecyclerView? = null
+ @Mock private val mockAllAppsRecyclerView: AllAppsRecyclerView? = null
- @Mock
- private val mockBottomBox: ImageView? = null
+ @Mock private val mockBottomBox: ImageView? = null
private var mVut: FloatingMaskView? = null
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
val context: Context = ActivityContextWrapper(ApplicationProvider.getApplicationContext())
mVut = FloatingMaskView(context)
- mVut!!.layoutParams = MarginLayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
- ViewGroup.LayoutParams.WRAP_CONTENT)
+ mVut!!.layoutParams =
+ MarginLayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ )
}
@Test
diff --git a/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java b/tests/multivalentTests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
similarity index 99%
rename from tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
rename to tests/multivalentTests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
index 430e496..d757d10 100644
--- a/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
@@ -42,7 +42,7 @@
import android.os.UserHandle;
import android.os.UserManager;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.pm.UserCache;
diff --git a/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewTest.java b/tests/multivalentTests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewTest.java
similarity index 98%
rename from tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewTest.java
rename to tests/multivalentTests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewTest.java
index 398f9c5..23b00c2 100644
--- a/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewTest.java
@@ -52,8 +52,8 @@
import android.widget.RelativeLayout;
import android.widget.TextView;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.launcher3.R;
import com.android.launcher3.logging.StatsLogManager;
@@ -300,7 +300,7 @@
&& info.user.equals(MAIN_HANDLE));
int rows = (int) (ALL_APPS_HEIGHT - PS_HEADER_HEIGHT - HEADER_PROTECTION_HEIGHT);
- int position = rows * NUM_APP_COLS - (NUM_APP_COLS-1) + 1;
+ int position = rows * NUM_APP_COLS - (NUM_APP_COLS - 1) + 1;
assertEquals(NUM_PRIVATE_SPACE_APPS + MAIN_USER_APP_COUNT
+ CONTAINER_HEADER_ELEMENT_COUNT + VIEW_AT_END_OF_APP_LIST,
@@ -335,7 +335,7 @@
&& info.user.equals(MAIN_HANDLE));
int rows = (int) (ALL_APPS_HEIGHT - PS_HEADER_HEIGHT - HEADER_PROTECTION_HEIGHT) - 1;
- int position = rows * NUM_APP_COLS - (NUM_APP_COLS-1) + 1;
+ int position = rows * NUM_APP_COLS - (NUM_APP_COLS - 1) + 1;
assertEquals(NUM_PRIVATE_SPACE_APPS + MAIN_USER_APP_COUNT
+ CONTAINER_HEADER_ELEMENT_COUNT + VIEW_AT_END_OF_APP_LIST,
@@ -370,7 +370,7 @@
&& info.user.equals(MAIN_HANDLE));
int rows = (int) (ALL_APPS_HEIGHT - BIGGER_PS_HEADER_HEIGHT - HEADER_PROTECTION_HEIGHT);
- int position = rows * NUM_APP_COLS - (NUM_APP_COLS-1) + 1;
+ int position = rows * NUM_APP_COLS - (NUM_APP_COLS - 1) + 1;
assertEquals(NUM_PRIVATE_SPACE_APPS + MAIN_USER_APP_COUNT
+ CONTAINER_HEADER_ELEMENT_COUNT + VIEW_AT_END_OF_APP_LIST,
diff --git a/tests/src/com/android/launcher3/allapps/WorkUtilityViewTest.java b/tests/multivalentTests/src/com/android/launcher3/allapps/WorkUtilityViewTest.java
similarity index 100%
rename from tests/src/com/android/launcher3/allapps/WorkUtilityViewTest.java
rename to tests/multivalentTests/src/com/android/launcher3/allapps/WorkUtilityViewTest.java
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/folder/PreviewItemManagerTest.kt b/tests/multivalentTests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt
index 548cf5b..15accbd 100644
--- a/tests/multivalentTests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt
@@ -17,27 +17,30 @@
package com.android.launcher3.folder
import android.R
-import android.graphics.Bitmap
import android.os.Process
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.launcher3.LauncherAppState
-import com.android.launcher3.LauncherPrefs.Companion.THEMED_ICONS
-import com.android.launcher3.LauncherPrefs.Companion.get
+import com.android.launcher3.LauncherPrefs
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_FOLDER
+import com.android.launcher3.dagger.LauncherAppComponent
+import com.android.launcher3.dagger.LauncherAppSingleton
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.ItemInfoWithIcon.FLAG_ARCHIVED
import com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE
import com.android.launcher3.model.data.WorkspaceItemInfo
import com.android.launcher3.util.ActivityContextWrapper
+import com.android.launcher3.util.AllModulesForTest
import com.android.launcher3.util.Executors
+import com.android.launcher3.util.FakePrefsModule
import com.android.launcher3.util.FlagOp
import com.android.launcher3.util.LauncherLayoutBuilder
import com.android.launcher3.util.LauncherModelHelper
@@ -45,10 +48,19 @@
import com.android.launcher3.util.TestUtil
import com.android.launcher3.util.UserIconInfo
import com.google.common.truth.Truth.assertThat
+import dagger.Component
+import kotlin.annotation.AnnotationRetention.RUNTIME
+import kotlin.annotation.AnnotationTarget.FUNCTION
+import kotlin.annotation.AnnotationTarget.PROPERTY_GETTER
+import kotlin.annotation.AnnotationTarget.PROPERTY_SETTER
import org.junit.After
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
+import org.junit.rules.TestRule
+import org.junit.runner.Description
import org.junit.runner.RunWith
+import org.junit.runners.model.Statement
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.doReturn
@@ -62,6 +74,8 @@
@RunWith(AndroidJUnit4::class)
class PreviewItemManagerTest {
+ @get:Rule val theseStateRule = ThemeStateRule()
+
private lateinit var previewItemManager: PreviewItemManager
private lateinit var context: SandboxModelContext
private lateinit var folderItems: ArrayList<WorkspaceItemInfo>
@@ -69,12 +83,14 @@
private lateinit var folderIcon: FolderIcon
private lateinit var iconCache: IconCache
- private var defaultThemedIcons = false
-
@Before
fun setup() {
modelHelper = LauncherModelHelper()
context = modelHelper.sandboxContext
+ context.initDaggerComponent(DaggerPreviewItemManagerTestComponent.builder())
+ theseStateRule.themeState?.let {
+ LauncherPrefs.get(context).putSync(ThemeManager.THEMED_ICONS.to(it))
+ }
folderIcon = FolderIcon(ActivityContextWrapper(context))
val app = spy(LauncherAppState.getInstance(context))
@@ -97,27 +113,16 @@
)
.loadModelSync()
+ folderIcon.mInfo =
+ modelHelper.bgDataModel.itemsIdMap.find { it.itemType == ITEM_TYPE_FOLDER }
+ as FolderInfo
// 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)
-
- // Set first icon to be themed.
- folderItems[0].bitmap.themedBitmap =
- MonoThemedBitmap(
- folderItems[0].bitmap.icon,
- Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888),
- )
+ folderItems = folderIcon.mInfo.getAppContents()
// Set second icon to be non-themed.
folderItems[1].bitmap.themedBitmap = null
// Set third icon to be themed with badge.
- folderItems[2].bitmap.themedBitmap =
- MonoThemedBitmap(
- folderItems[2].bitmap.icon,
- Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888),
- )
folderItems[2].bitmap =
folderItems[2].bitmap.withFlags(profileFlagOp(UserIconInfo.TYPE_WORK))
@@ -125,20 +130,17 @@
folderItems[3].bitmap =
folderItems[3].bitmap.withFlags(profileFlagOp(UserIconInfo.TYPE_WORK))
folderItems[3].bitmap.themedBitmap = null
-
- defaultThemedIcons = get(context).get(THEMED_ICONS)
}
@After
@Throws(Exception::class)
fun tearDown() {
- get(context).put(THEMED_ICONS, defaultThemedIcons)
modelHelper.destroy()
}
@Test
+ @MonoThemeEnabled(true)
fun checkThemedIconWithThemingOn_iconShouldBeThemed() {
- get(context).put(THEMED_ICONS, true)
val drawingParams = PreviewItemDrawingParams(0f, 0f, 0f)
previewItemManager.setDrawable(drawingParams, folderItems[0])
@@ -147,8 +149,8 @@
}
@Test
+ @MonoThemeEnabled(false)
fun checkThemedIconWithThemingOff_iconShouldNotBeThemed() {
- get(context).put(THEMED_ICONS, false)
val drawingParams = PreviewItemDrawingParams(0f, 0f, 0f)
previewItemManager.setDrawable(drawingParams, folderItems[0])
@@ -157,8 +159,8 @@
}
@Test
+ @MonoThemeEnabled(true)
fun checkUnthemedIconWithThemingOn_iconShouldNotBeThemed() {
- get(context).put(THEMED_ICONS, true)
val drawingParams = PreviewItemDrawingParams(0f, 0f, 0f)
previewItemManager.setDrawable(drawingParams, folderItems[1])
@@ -167,8 +169,8 @@
}
@Test
+ @MonoThemeEnabled(false)
fun checkUnthemedIconWithThemingOff_iconShouldNotBeThemed() {
- get(context).put(THEMED_ICONS, false)
val drawingParams = PreviewItemDrawingParams(0f, 0f, 0f)
previewItemManager.setDrawable(drawingParams, folderItems[1])
@@ -177,8 +179,8 @@
}
@Test
+ @MonoThemeEnabled(true)
fun checkThemedIconWithBadgeWithThemingOn_iconAndBadgeShouldBeThemed() {
- get(context).put(THEMED_ICONS, true)
val drawingParams = PreviewItemDrawingParams(0f, 0f, 0f)
previewItemManager.setDrawable(drawingParams, folderItems[2])
@@ -190,8 +192,8 @@
}
@Test
+ @MonoThemeEnabled(true)
fun checkUnthemedIconWithBadgeWithThemingOn_badgeShouldBeThemed() {
- get(context).put(THEMED_ICONS, true)
val drawingParams = PreviewItemDrawingParams(0f, 0f, 0f)
previewItemManager.setDrawable(drawingParams, folderItems[3])
@@ -203,8 +205,8 @@
}
@Test
+ @MonoThemeEnabled(false)
fun checkUnthemedIconWithBadgeWithThemingOff_iconAndBadgeShouldNotBeThemed() {
- get(context).put(THEMED_ICONS, false)
val drawingParams = PreviewItemDrawingParams(0f, 0f, 0f)
previewItemManager.setDrawable(drawingParams, folderItems[3])
@@ -276,3 +278,28 @@
private fun profileFlagOp(type: Int) =
UserIconInfo(Process.myUserHandle(), type).applyBitmapInfoFlags(FlagOp.NO_OP)
}
+
+class ThemeStateRule : TestRule {
+
+ var themeState: Boolean? = null
+
+ override fun apply(base: Statement, description: Description): Statement {
+ themeState = description.getAnnotation(MonoThemeEnabled::class.java)?.value
+ return base
+ }
+}
+
+// Annotation for tests that need to be run with quickstep enabled and disabled.
+@Retention(RUNTIME)
+@Target(FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER)
+annotation class MonoThemeEnabled(val value: Boolean = false)
+
+@LauncherAppSingleton
+@Component(modules = [AllModulesForTest::class, FakePrefsModule::class])
+interface PreviewItemManagerTestComponent : LauncherAppComponent {
+
+ @Component.Builder
+ interface Builder : LauncherAppComponent.Builder {
+ override fun build(): PreviewItemManagerTestComponent
+ }
+}
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/icons/mono/MonoIconThemeControllerTest.kt b/tests/multivalentTests/src/com/android/launcher3/icons/mono/MonoIconThemeControllerTest.kt
index 4af564e..12c14fb 100644
--- a/tests/multivalentTests/src/com/android/launcher3/icons/mono/MonoIconThemeControllerTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/icons/mono/MonoIconThemeControllerTest.kt
@@ -16,9 +16,11 @@
package com.android.launcher3.icons.mono
+import android.content.ComponentName
import android.graphics.Color
import android.graphics.drawable.AdaptiveIconDrawable
import android.graphics.drawable.ColorDrawable
+import android.os.Process
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
@@ -29,6 +31,9 @@
import com.android.launcher3.Flags
import com.android.launcher3.icons.BaseIconFactory
import com.android.launcher3.icons.BitmapInfo
+import com.android.launcher3.icons.SourceHint
+import com.android.launcher3.icons.cache.LauncherActivityCachingLogic
+import com.android.launcher3.util.ComponentKey
import com.android.launcher3.util.LauncherMultivalentJUnit.Companion.isRunningInRobolectric
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
@@ -45,6 +50,12 @@
private val iconFactory = BaseIconFactory(context, DisplayMetrics.DENSITY_MEDIUM, 30)
+ private val sourceHint =
+ SourceHint(
+ key = ComponentKey(ComponentName("a", "a"), Process.myUserHandle()),
+ logic = LauncherActivityCachingLogic,
+ )
+
@Test
fun `createThemedBitmap when mono drawable is present`() {
val icon = AdaptiveIconDrawable(ColorDrawable(Color.BLACK), null, ColorDrawable(Color.RED))
@@ -81,7 +92,8 @@
val themeBitmap =
MonoIconThemeController().createThemedBitmap(icon, iconInfo, iconFactory)!!
assertNotNull(
- MonoIconThemeController().decode(themeBitmap.serialize(), iconInfo, iconFactory)
+ MonoIconThemeController()
+ .decode(themeBitmap.serialize(), iconInfo, iconFactory, sourceHint)
)
}
@@ -90,7 +102,10 @@
ensureBitmapSerializationSupported()
val icon = AdaptiveIconDrawable(ColorDrawable(Color.BLACK), null, ColorDrawable(Color.RED))
val iconInfo = iconFactory.createBadgedIconBitmap(icon)
- assertNull(MonoIconThemeController().decode(byteArrayOf(1, 1, 1, 1), iconInfo, iconFactory))
+ assertNull(
+ MonoIconThemeController()
+ .decode(byteArrayOf(1, 1, 1, 1), iconInfo, iconFactory, sourceHint)
+ )
}
@Test
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java b/tests/multivalentTests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
index 1e2431f..0ae4d00 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
@@ -16,6 +16,8 @@
package com.android.launcher3.model;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY;
import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE;
@@ -42,6 +44,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.List;
+
/**
* Tests for layout parser for remote layout
*/
@@ -63,14 +67,23 @@
mModelHelper.destroy();
}
+ private List<ItemInfo> getWorkspaceItems() {
+ return mModelHelper
+ .getBgDataModel()
+ .itemsIdMap
+ .stream()
+ .filter(i -> i.container == CONTAINER_DESKTOP || i.container == CONTAINER_HOTSEAT)
+ .toList();
+ }
+
@Test
public void testCustomProfileLoaded_with_icon_on_hotseat() throws Exception {
writeLayoutAndLoad(new LauncherLayoutBuilder().atHotseat(0)
.putApp(TEST_PACKAGE, TEST_ACTIVITY));
// Verify one item in hotseat
- assertEquals(1, mModelHelper.getBgDataModel().workspaceItems.size());
- ItemInfo info = mModelHelper.getBgDataModel().workspaceItems.get(0);
+ assertEquals(1, getWorkspaceItems().size());
+ ItemInfo info = getWorkspaceItems().get(0);
assertEquals(LauncherSettings.Favorites.CONTAINER_HOTSEAT, info.container);
assertEquals(LauncherSettings.Favorites.ITEM_TYPE_APPLICATION, info.itemType);
}
@@ -84,8 +97,8 @@
.build());
// Verify folder
- assertEquals(1, mModelHelper.getBgDataModel().workspaceItems.size());
- ItemInfo info = mModelHelper.getBgDataModel().workspaceItems.get(0);
+ assertEquals(1, getWorkspaceItems().size());
+ ItemInfo info = getWorkspaceItems().get(0);
assertEquals(LauncherSettings.Favorites.ITEM_TYPE_FOLDER, info.itemType);
assertEquals(3, ((FolderInfo) info).getContents().size());
}
@@ -99,8 +112,8 @@
.build());
// Verify folder
- assertEquals(1, mModelHelper.getBgDataModel().workspaceItems.size());
- ItemInfo info = mModelHelper.getBgDataModel().workspaceItems.get(0);
+ assertEquals(1, getWorkspaceItems().size());
+ ItemInfo info = getWorkspaceItems().get(0);
assertEquals(LauncherSettings.Favorites.ITEM_TYPE_FOLDER, info.itemType);
assertEquals(3, ((FolderInfo) info).getContents().size());
assertEquals("CustomFolder", info.title.toString());
@@ -124,8 +137,8 @@
.putWidget(pendingAppPkg, "PlaceholderWidget", 2, 2));
// Verify widget
- assertEquals(1, mModelHelper.getBgDataModel().appWidgets.size());
- ItemInfo info = mModelHelper.getBgDataModel().appWidgets.get(0);
+ assertEquals(1, getWorkspaceItems().size());
+ ItemInfo info = getWorkspaceItems().get(0);
assertEquals(LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET, info.itemType);
assertEquals(2, info.spanX);
assertEquals(2, info.spanY);
@@ -138,8 +151,8 @@
.putShortcut(TEST_PACKAGE, "shortcut2"));
// Verify one item in hotseat
- assertEquals(1, mModelHelper.getBgDataModel().workspaceItems.size());
- ItemInfo info = mModelHelper.getBgDataModel().workspaceItems.get(0);
+ assertEquals(1, getWorkspaceItems().size());
+ ItemInfo info = getWorkspaceItems().get(0);
assertEquals(LauncherSettings.Favorites.CONTAINER_HOTSEAT, info.container);
assertEquals(LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT, info.itemType);
}
@@ -154,8 +167,8 @@
.build());
// Verify folder
- assertEquals(1, mModelHelper.getBgDataModel().workspaceItems.size());
- FolderInfo info = (FolderInfo) mModelHelper.getBgDataModel().workspaceItems.get(0);
+ assertEquals(1, getWorkspaceItems().size());
+ FolderInfo info = (FolderInfo) getWorkspaceItems().get(0);
assertEquals(3, info.getContents().size());
// Verify last icon
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/FolderIconLoadTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/FolderIconLoadTest.kt
index e8f778f..f357487 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/FolderIconLoadTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/model/FolderIconLoadTest.kt
@@ -18,8 +18,10 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.launcher3.LauncherAppState
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_FOLDER
import com.android.launcher3.icons.BitmapInfo
import com.android.launcher3.icons.waitForUpdateHandlerToFinish
+import com.android.launcher3.model.data.FolderInfo
import com.android.launcher3.model.data.WorkspaceItemInfo
import com.android.launcher3.util.Executors
import com.android.launcher3.util.LauncherLayoutBuilder
@@ -149,11 +151,13 @@
// Reload again with correct icon state
app.model.forceReload()
modelHelper.loadModelSync()
- val collections = modelHelper.getBgDataModel().collections
-
- assertThat(collections.size()).isEqualTo(1)
- assertThat(collections.valueAt(0).getAppContents().size).isEqualTo(itemCount)
- return collections.valueAt(0).getAppContents()
+ val collections =
+ modelHelper.bgDataModel.itemsIdMap
+ .filter { it.itemType == ITEM_TYPE_FOLDER }
+ .map { it as FolderInfo }
+ assertThat(collections.size).isEqualTo(1)
+ assertThat(collections[0].getAppContents().size).isEqualTo(itemCount)
+ return collections[0].getAppContents()
}
private fun verifyHighRes(items: ArrayList<WorkspaceItemInfo>, vararg indices: Int) {
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt
index d699eee..da87dfc 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt
@@ -494,8 +494,7 @@
@Test
fun `When processing Folder then create FolderInfo and mark restored`() {
val actualFolderInfo = FolderInfo()
- mockBgDataModel =
- mock<BgDataModel>().apply { whenever(findOrMakeFolder(1)).thenReturn(actualFolderInfo) }
+ mockBgDataModel = mock<BgDataModel>()
mockCursor =
mock<LoaderCursor>().apply {
user = UserHandle(0)
@@ -509,6 +508,7 @@
whenever(getColumnIndex(Favorites.TITLE)).thenReturn(4)
whenever(getString(4)).thenReturn("title")
whenever(options).thenReturn(5)
+ whenever(findOrMakeFolder(eq(1), any())).thenReturn(actualFolderInfo)
}
val expectedFolderInfo =
FolderInfo().apply {
@@ -600,7 +600,8 @@
// Then
val widgetInfoCaptor = ArgumentCaptor.forClass(LauncherAppWidgetInfo::class.java)
- verify(mockCursor).checkAndAddItem(widgetInfoCaptor.capture(), eq(mockBgDataModel))
+ verify(mockCursor)
+ .checkAndAddItem(widgetInfoCaptor.capture(), eq(mockBgDataModel), anyOrNull())
val actualWidgetInfo = widgetInfoCaptor.value
with(actualWidgetInfo) {
assertThat(providerName).isEqualTo(expectedWidgetInfo.providerName)
@@ -655,7 +656,7 @@
itemProcessorUnderTest.processItem()
// Then
- verify(mockCursor).checkAndAddItem(any(), any())
+ verify(mockCursor).checkAndAddItem(any(), any(), anyOrNull())
}
@Test
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..b66a9d3
--- /dev/null
+++ b/tests/multivalentTests/src/com/android/launcher3/util/DaggerGraphs.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.AppModule
+import com.android.launcher3.dagger.StaticObjectModule
+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,
+ StaticObjectModule::class,
+ AppModule::class,
+ ]
+)
+class AllModulesForTest
+
+/** All modules except the WMProxy */
+@Module(includes = [ApiWrapperModule::class, StaticObjectModule::class, AppModule::class])
+class AllModulesMinusWMProxy
+
+/** All modules except the ApiWrapper */
+@Module(includes = [WindowManagerProxyModule::class, StaticObjectModule::class, AppModule::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 e6e156f..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
@@ -43,6 +42,7 @@
import dagger.Component
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
+import kotlin.math.min
import org.junit.After
import org.junit.Before
import org.junit.Test
@@ -57,7 +57,6 @@
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.mockito.stubbing.Answer
-import kotlin.math.min
/** Unit tests for {@link DisplayController} */
@SmallTest
@@ -139,7 +138,7 @@
whenever(context.resources).thenReturn(resources)
// Initialize DisplayController
- displayController = DisplayController(context)
+ displayController = DisplayController.INSTANCE.get(context)
displayController.addChangeListener(displayInfoChangeListener)
}
@@ -235,14 +234,12 @@
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
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/GlobalTestRunListener.java b/tests/multivalentTests/src/com/android/launcher3/util/GlobalTestRunListener.java
new file mode 100644
index 0000000..667f540
--- /dev/null
+++ b/tests/multivalentTests/src/com/android/launcher3/util/GlobalTestRunListener.java
@@ -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.launcher3.util;
+
+import org.junit.runner.Description;
+import org.junit.runner.notification.RunListener;
+import org.mockito.Mockito;
+
+public class GlobalTestRunListener extends RunListener {
+ /**
+ * See {@link RunListener#testFinished} which executes per atomic test.
+ * {@link RunListener#testSuiteFinished} which executes per test suite. Test suite = test class
+ * in this context.
+ * {@link RunListener#testRunFinished} which executes after everything (all test suites) is
+ * completed.
+ */
+ @Override
+ public void testSuiteFinished(Description description) throws Exception {
+ // This method runs after every test class and will clear mocks after every test class
+ // execution is completed.
+ Mockito.framework().clearInlineMocks();
+ super.testSuiteFinished(description);
+ }
+}
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/LockedUserStateTest.kt b/tests/multivalentTests/src/com/android/launcher3/util/LockedUserStateTest.kt
index 2711d7a..99f5a5b 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/LockedUserStateTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/util/LockedUserStateTest.kt
@@ -22,7 +22,10 @@
import android.os.UserManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.launcher3.util.Executors.MAIN_EXECUTOR
+import com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR
import com.google.common.truth.Truth.assertThat
+import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -38,17 +41,24 @@
private val userManager: UserManager = mock()
private val context: Context = mock()
+ private val lifeCycle: DaggerSingletonTracker = mock()
@Before
fun setup() {
whenever(context.getSystemService(UserManager::class.java)).thenReturn(userManager)
}
+ @After
+ fun tearDown() {
+ UI_HELPER_EXECUTOR.submit {}.get()
+ MAIN_EXECUTOR.submit {}.get()
+ }
+
@Test
fun runOnUserUnlocked_runs_action_immediately_if_already_unlocked() {
whenever(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(true)
val action: Runnable = mock()
- LockedUserState(context).runOnUserUnlocked(action)
+ LockedUserState(context, lifeCycle).runOnUserUnlocked(action)
verify(action).run()
}
@@ -56,23 +66,23 @@
fun runOnUserUnlocked_waits_to_run_action_until_user_is_unlocked() {
whenever(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(false)
val action: Runnable = mock()
- val state = LockedUserState(context)
+ val state = LockedUserState(context, lifeCycle)
state.runOnUserUnlocked(action)
// b/343530737
verifyNoMoreInteractions(action)
- state.mUserUnlockedReceiver.onReceive(context, Intent(Intent.ACTION_USER_UNLOCKED))
+ state.userUnlockedReceiver.onReceive(context, Intent(Intent.ACTION_USER_UNLOCKED))
verify(action).run()
}
@Test
fun isUserUnlocked_returns_true_when_user_is_unlocked() {
whenever(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(true)
- assertThat(LockedUserState(context).isUserUnlocked).isTrue()
+ assertThat(LockedUserState(context, lifeCycle).isUserUnlocked).isTrue()
}
@Test
fun isUserUnlocked_returns_false_when_user_is_locked() {
whenever(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(false)
- assertThat(LockedUserState(context).isUserUnlocked).isFalse()
+ assertThat(LockedUserState(context, lifeCycle).isUserUnlocked).isFalse()
}
}
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/multivalentTests/src/com/android/launcher3/util/SandboxApplication.kt b/tests/multivalentTests/src/com/android/launcher3/util/SandboxApplication.kt
index efe7637..0da8891 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/SandboxApplication.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/util/SandboxApplication.kt
@@ -48,6 +48,7 @@
class SandboxApplication private constructor(private val base: SandboxApplicationWrapper) :
SandboxModelContext(base), TestRule {
+ @JvmOverloads
constructor(
base: Context = ApplicationProvider.getApplicationContext()
) : this(SandboxApplicationWrapper(base))
diff --git a/tests/src/com/android/launcher3/backuprestore/BackupAndRestoreDBSelectionTest.kt b/tests/src/com/android/launcher3/backuprestore/BackupAndRestoreDBSelectionTest.kt
index b4ee090..1e54603 100644
--- a/tests/src/com/android/launcher3/backuprestore/BackupAndRestoreDBSelectionTest.kt
+++ b/tests/src/com/android/launcher3/backuprestore/BackupAndRestoreDBSelectionTest.kt
@@ -25,15 +25,18 @@
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
import com.android.launcher3.util.rule.BackAndRestoreRule
import com.android.launcher3.util.rule.setFlags
import org.junit.Before
+import org.junit.Ignore
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 +52,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)
@@ -65,12 +70,13 @@
}
@Test
+ @Ignore("b/385147987")
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..380c208 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
@@ -80,13 +82,13 @@
@RunWith(AndroidJUnit4::class)
class GridMigrationTest {
private val DB_FILE = "test_launcher.db"
- // This DB is used for testing the heuristic where we add an extra row at the bottom.
- private val DB_FILE_NO_SHIFT = "test_launcher_2.db"
@JvmField
@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 +104,7 @@
dst.dbHelper,
src.dbHelper.readableDatabase,
true,
+ modelDelegate,
)
} else {
GridSizeMigrationDBController.migrateGridIfNeeded(
@@ -111,6 +114,7 @@
dst.dbHelper,
src.dbHelper.readableDatabase,
true,
+ modelDelegate,
)
}
}
@@ -149,11 +153,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 +167,7 @@
}
validateDb(dst)
compare(dst, target, src)
+ verify(modelDelegate).gridMigrationComplete(src.gridState, dst.gridState)
}
// Copying the src db for all tests.
@@ -222,42 +228,6 @@
@JvmField
@Rule
- val result5x5to5x8WithShift =
- TestToPhoneFileCopier(
- src = "databases/GridMigrationTest/result5x5to5x8WithShift.db",
- dest = "databases/result5x5to5x8WithShift.db",
- removeOnFinish = true,
- )
-
- @Test
- fun `5x5 to 5x8 with cells shifting down`() =
- runTest(
- src = GridMigrationData(DB_FILE, DeviceGridState(5, 5, 5, TYPE_PHONE, DB_FILE)),
- dst =
- GridMigrationData(
- null, // in memory db, to download a new db change null
- // for
- // the filename of the db name to store it. Do not use existing names.
- DeviceGridState(5, 8, 5, TYPE_PHONE, ""),
- ),
- target =
- GridMigrationData(
- "result5x5to5x8WithShift.db",
- DeviceGridState(5, 8, 5, TYPE_PHONE, ""),
- ),
- )
-
- @JvmField
- @Rule
- val fileCopierNoShift =
- TestToPhoneFileCopier(
- src = "databases/GridMigrationTest/$DB_FILE_NO_SHIFT",
- dest = "databases/$DB_FILE_NO_SHIFT",
- removeOnFinish = true,
- )
-
- @JvmField
- @Rule
val result5x5to5x8 =
TestToPhoneFileCopier(
src = "databases/GridMigrationTest/result5x5to5x8.db",
@@ -266,16 +236,13 @@
)
@Test
- fun `5x5 to 5x8 without cell shift`() =
+ fun `5x5 to 5x8`() =
runTest(
- src =
- GridMigrationData(
- DB_FILE_NO_SHIFT,
- DeviceGridState(5, 5, 5, TYPE_PHONE, DB_FILE_NO_SHIFT),
- ),
+ src = GridMigrationData(DB_FILE, DeviceGridState(5, 5, 5, TYPE_PHONE, DB_FILE)),
dst =
GridMigrationData(
- null, // in memory db, to download a new db change null for
+ null, // in memory db, to download a new db change null
+ // for
// the filename of the db name to store it. Do not use existing names.
DeviceGridState(5, 8, 5, TYPE_PHONE, ""),
),
diff --git a/tests/src/com/android/launcher3/model/LoaderTaskTest.kt b/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
index d2229c4..f04688d 100644
--- a/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
+++ b/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
@@ -19,6 +19,10 @@
import com.android.launcher3.LauncherPrefs
import com.android.launcher3.LauncherPrefs.Companion.IS_FIRST_LOAD_AFTER_RESTORE
import com.android.launcher3.LauncherPrefs.Companion.RESTORE_DEVICE
+import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP
+import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_FOLDER
import com.android.launcher3.icons.IconCache
import com.android.launcher3.icons.cache.CachingLogic
import com.android.launcher3.icons.cache.IconCacheUpdateHandler
@@ -155,9 +159,24 @@
widgetsFilterDataProvider,
)
.runSyncOnBackgroundThread()
- Truth.assertThat(workspaceItems.size).isAtLeast(25)
- Truth.assertThat(appWidgets.size).isAtLeast(7)
- Truth.assertThat(collections.size()).isAtLeast(8)
+ Truth.assertThat(
+ itemsIdMap
+ .filter {
+ it.container == CONTAINER_DESKTOP || it.container == CONTAINER_HOTSEAT
+ }
+ .size
+ )
+ .isAtLeast(32)
+ Truth.assertThat(itemsIdMap.filter { ModelUtils.WIDGET_FILTER.test(it) }.size)
+ .isAtLeast(7)
+ Truth.assertThat(
+ itemsIdMap
+ .filter {
+ it.itemType == ITEM_TYPE_FOLDER || it.itemType == ITEM_TYPE_APP_PAIR
+ }
+ .size
+ )
+ .isAtLeast(8)
Truth.assertThat(itemsIdMap.size()).isAtLeast(40)
Truth.assertThat(widgetsModel.defaultWidgetsFilter).isNotNull()
}
diff --git a/tests/src/com/android/launcher3/model/WorkspaceItemProcessorExtraTest.kt b/tests/src/com/android/launcher3/model/WorkspaceItemProcessorExtraTest.kt
index d553f47..8db049c 100644
--- a/tests/src/com/android/launcher3/model/WorkspaceItemProcessorExtraTest.kt
+++ b/tests/src/com/android/launcher3/model/WorkspaceItemProcessorExtraTest.kt
@@ -58,6 +58,7 @@
import org.mockito.Mockito.RETURNS_DEEP_STUBS
import org.mockito.Mockito.verify
import org.mockito.kotlin.any
+import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
@@ -220,7 +221,8 @@
)
.commit()
val widgetInfoCaptor = ArgumentCaptor.forClass(LauncherAppWidgetInfo::class.java)
- verify(mockCursor).checkAndAddItem(widgetInfoCaptor.capture(), eq(mockBgDataModel))
+ verify(mockCursor)
+ .checkAndAddItem(widgetInfoCaptor.capture(), eq(mockBgDataModel), anyOrNull())
val actualWidgetInfo = widgetInfoCaptor.value
with(actualWidgetInfo) {
assertThat(providerName).isEqualTo(expectedWidgetInfo.providerName)
@@ -271,7 +273,7 @@
itemProcessorUnderTest.processItem()
// Then
- verify(mockCursor).checkAndAddItem(any(), any())
+ verify(mockCursor).checkAndAddItem(any(), any(), anyOrNull())
}
private fun createWorkspaceItemProcessorUnderTest(
diff --git a/tests/src/com/android/launcher3/pm/InstallSessionHelperTest.kt b/tests/src/com/android/launcher3/pm/InstallSessionHelperTest.kt
index 94b2c7e..707c2c1 100644
--- a/tests/src/com/android/launcher3/pm/InstallSessionHelperTest.kt
+++ b/tests/src/com/android/launcher3/pm/InstallSessionHelperTest.kt
@@ -52,7 +52,7 @@
private val sandboxContext = spy(launcherModelHelper.sandboxContext)
private val packageManager = sandboxContext.packageManager
private val expectedAppPackage = "expectedAppPackage"
- private val expectedInstallerPackage = "expectedInstallerPackage"
+ private val expectedInstallerPackage = sandboxContext.packageName
private val mockPackageInstaller: PackageInstaller = mock()
private lateinit var installSessionHelper: InstallSessionHelper
@@ -61,7 +61,6 @@
@Before
fun setup() {
whenever(packageManager.packageInstaller).thenReturn(mockPackageInstaller)
- whenever(sandboxContext.packageName).thenReturn(expectedInstallerPackage)
launcherApps = sandboxContext.spyService(LauncherApps::class.java)
installSessionHelper = InstallSessionHelper(sandboxContext)
}
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/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/AppIconMenu.java b/tests/tapl/com/android/launcher3/tapl/AppIconMenu.java
index bbcc6a8..033cfb0 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIconMenu.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIconMenu.java
@@ -63,5 +63,12 @@
return new SplitScreenMenuItem(mLauncher, menuItem);
}
+ /** Returns the Bubble menu item. */
+ public BubbleMenuItem getBubbleMenuItem() {
+ final UiObject2 menuItem = mLauncher.waitForObjectInContainer(mDeepShortcutsContainer,
+ AppIcon.getMenuItemSelector("Bubble", mLauncher));
+ return new BubbleMenuItem(mLauncher, menuItem);
+ }
+
protected abstract AppIconMenuItem createMenuItem(UiObject2 menuItem);
}
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index b15afc1..ef72a0f 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -440,7 +440,7 @@
"Not expecting an actions bar: device is tablet and task is not centered");
return false;
}
- if (task.isGrouped() && (!mLauncher.isAppPairsEnabled() || !isTablet)) {
+ if (task.isGrouped() && !isTablet) {
testLogD(TAG, "Not expecting an actions bar: device is phone and task is split");
// Overview actions aren't visible for split screen tasks, except for save app pair
// button on tablets.
diff --git a/tests/tapl/com/android/launcher3/tapl/BubbleMenuItem.kt b/tests/tapl/com/android/launcher3/tapl/BubbleMenuItem.kt
new file mode 100644
index 0000000..77391f1
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/BubbleMenuItem.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.tapl
+
+import androidx.test.uiautomator.UiObject2
+
+/**
+ * A class representing the Bubble menu item in the app long-press menu, which moves the app into a
+ * bubble.
+ */
+class BubbleMenuItem(
+ private val launcher: LauncherInstrumentation,
+ private val uiObject: UiObject2,
+) {
+
+ fun click() {
+ launcher.addContextLayer("want to create bubble from app long-press menu").use {
+ LauncherInstrumentation.log(
+ "clicking on bubble menu item ${uiObject.visibleCenter} in ${
+ launcher.getVisibleBounds(
+ uiObject
+ )
+ }"
+ )
+ launcher.clickLauncherObject(uiObject)
+ }
+ }
+}
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..edca6dc 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);
}
@@ -2010,11 +2015,6 @@
TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
- boolean isAppPairsEnabled() {
- return getTestInfo(TestProtocol.REQUEST_FLAG_ENABLE_APP_PAIRS).getBoolean(
- TestProtocol.TEST_INFO_RESPONSE_FIELD);
- }
-
public void sendPointer(long downTime, long currentTime, int action, Point point,
GestureScope gestureScope) {
sendPointer(downTime, currentTime, action, point, gestureScope,
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
index 8512d73..2431ef5 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;
@@ -158,6 +157,7 @@
}
boolean taskWasFocused = mLauncher.isTablet()
+ && !isDesktop()
&& getVisibleHeight() == mLauncher.getOverviewTaskSize().height();
List<Integer> originalTasksCenterX =
getCurrentTasksCenterXList().stream().sorted().toList();
@@ -281,10 +281,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 +298,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