Merge "Migrate desktop_form_factor to Launcher3" into main
diff --git a/Android.bp b/Android.bp
index a4a058f..2c4fb37 100644
--- a/Android.bp
+++ b/Android.bp
@@ -25,6 +25,7 @@
name: "launcher-non-platform-apis-defaults",
static_libs: [
"android.os.flags-aconfig-java",
+ "android.multiuser.flags-aconfig-java",
"android.appwidget.flags-aconfig-java",
"com.android.window.flags.window-aconfig-java",
],
@@ -408,6 +409,9 @@
lint: {
baseline_filename: "lint-baseline.xml",
},
+ flags_packages: [
+ "com_android_launcher3_flags",
+ ],
}
//
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 6948133..768ba65 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,8 +1,10 @@
[Builtin Hooks]
ktfmt = true
+bpfmt = true
[Builtin Hooks Options]
ktfmt = --kotlinlang-style
+bpfmt = -d
[Tool Paths]
ktfmt = ${REPO_ROOT}/external/ktfmt/ktfmt.sh
diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig
index cc746eb..cd8b891 100644
--- a/aconfig/launcher.aconfig
+++ b/aconfig/launcher.aconfig
@@ -235,7 +235,7 @@
flag {
name: "enable_fallback_overview_in_window"
- namespace: "launcher"
+ namespace: "lse_desktop_experience"
description: "Enables fallback recents opening inside of a window instead of an activity."
bug: "292269949"
}
@@ -311,6 +311,13 @@
}
flag {
+ name: "all_apps_blur"
+ namespace: "launcher"
+ description: "Content behind the all apps panel in Launcher will be blurred."
+ bug: "400827727"
+}
+
+flag {
name: "multiline_search_bar"
namespace: "launcher"
description: "Search bar can wrap to multi-line"
@@ -450,14 +457,14 @@
flag {
name: "enable_recents_window_proto_log"
- namespace: "launcher"
+ namespace: "lse_desktop_experience"
description: "Enables tracking recents window logs in ProtoLog"
bug: "292269949"
}
flag {
name: "enable_state_manager_proto_log"
- namespace: "launcher"
+ namespace: "lse_desktop_experience"
description: "Enables tracking state manager logs in ProtoLog"
bug: "292269949"
}
@@ -488,7 +495,7 @@
flag {
name: "enable_launcher_overview_in_window"
- namespace: "launcher"
+ namespace: "lse_desktop_experience"
description: "Enables launcher recents opening inside of a window instead of being hosted in launcher activity."
bug: "292269949"
}
@@ -518,13 +525,6 @@
}
flag {
- name: "taskbar_recents_layout_transition"
- namespace: "launcher"
- description: "Enable Taskbar LayoutTransition for Recent Apps"
- bug: "343521765"
-}
-
-flag {
name: "enable_pinning_app_with_context_menu"
namespace: "launcher"
description: "Add options to pin/unpin to taskbar to app context menus."
@@ -566,6 +566,14 @@
}
flag {
+ name: "google_sans_flex_font"
+ namespace: "launcher"
+ description: "Adds refresh for font family. Needs to be fixed to be used in resources."
+ bug: "395145453"
+ is_fixed_read_only: true
+}
+
+flag {
name: "restore_archived_shortcuts"
namespace: "launcher"
description: "Makes sure pre-archived pinned shortcuts also get restored"
@@ -636,3 +644,50 @@
description: "Enables gesture navigation handling on connected displays"
bug: "382130680"
}
+
+flag {
+ name: "enable_taskbar_behind_shade"
+ namespace: "lse_desktop_experience"
+ description: "Keeps taskbar behind notification shade when its pulled down"
+ bug: "343194358"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "enable_scalability_for_desktop_experience"
+ namespace: "launcher"
+ description: "Enable more grid scale options on the launcher for desktop experience"
+ bug: "375491272"
+}
+
+flag {
+ name: "enable_gesture_nav_horizontal_touch_slop"
+ namespace: "launcher"
+ description: "Enables horizontal touch slop checking in non-vertical fling navigation gestures"
+ bug: "394364217"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "sync_app_launch_with_taskbar_stash"
+ namespace: "launcher"
+ description: "Syncs the two animations (app launch, taskbar stash) so they play at the same time."
+ bug: "319162553"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "remove_apps_refresh_on_right_click"
+ namespace: "launcher"
+ description: "Remove predicted apps refresh on right click"
+ bug: "343650193"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/aconfig/launcher_overview.aconfig b/aconfig/launcher_overview.aconfig
index 5749c51..d27a214 100644
--- a/aconfig/launcher_overview.aconfig
+++ b/aconfig/launcher_overview.aconfig
@@ -5,14 +5,14 @@
name: "enable_grid_only_overview"
namespace: "launcher_overview"
description: "Enable a grid-only overview without a focused task."
- bug: "257950105"
+ bug: "360204325"
}
flag {
name: "enable_overview_icon_menu"
namespace: "launcher_overview"
description: "Enable updated overview icon and menu within task."
- bug: "257950105"
+ bug: "360205084"
}
flag {
@@ -110,3 +110,13 @@
description: "Enable wallpaper background for desktop tasks in overview."
bug: "363257721"
}
+
+flag {
+ name: "enable_show_enabled_shortcuts_in_accessibility_menu"
+ namespace: "launcher_overview"
+ description: "Enables showing the same shortcuts in the Task menu as well as the accessibility actions menu"
+ bug: "383662632"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/quickstep/res/color/app_chip_menu_item_color_fg.xml b/quickstep/res/color/app_chip_menu_item_color_fg.xml
new file mode 100644
index 0000000..fa1dc34
--- /dev/null
+++ b/quickstep/res/color/app_chip_menu_item_color_fg.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:alpha="0.15" android:color="@color/materialColorOnSurface" android:state_enabled="true" android:state_pressed="true" />
+ <item android:alpha="0.11" android:color="@color/materialColorOnSurface" android:state_enabled="true" android:state_hovered="true" />
+ <item android:color="@android:color/transparent" />
+</selector>
\ No newline at end of file
diff --git a/quickstep/res/color/app_chip_state_color_fg.xml b/quickstep/res/color/app_chip_state_color_fg.xml
new file mode 100644
index 0000000..58dfee0
--- /dev/null
+++ b/quickstep/res/color/app_chip_state_color_fg.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:alpha="0.15" android:color="@color/materialColorOnSurface" android:state_enabled="true" android:state_pressed="true" />
+ <item android:alpha="0.11" android:color="@color/materialColorOnSurface" android:state_enabled="true" android:state_hovered="true" />
+ <item android:color="@android:color/transparent" />
+</selector>
\ No newline at end of file
diff --git a/quickstep/res/color/keyboard_quick_switch_scroll_button_bg.xml b/quickstep/res/color/keyboard_quick_switch_scroll_button_bg.xml
new file mode 100644
index 0000000..1592055
--- /dev/null
+++ b/quickstep/res/color/keyboard_quick_switch_scroll_button_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.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false"
+ android:color="@color/materialColorPrimary"
+ android:alpha="0.38"/>
+ <item android:color="@color/materialColorPrimary"/>
+</selector>
\ No newline at end of file
diff --git a/quickstep/res/color/keyboard_quick_switch_scroll_button_fg.xml b/quickstep/res/color/keyboard_quick_switch_scroll_button_fg.xml
new file mode 100644
index 0000000..051c18f
--- /dev/null
+++ b/quickstep/res/color/keyboard_quick_switch_scroll_button_fg.xml
@@ -0,0 +1,27 @@
+<?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.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="true"
+ android:state_pressed="true"
+ android:color="@color/materialColorOnPrimary"
+ android:alpha="0.15"/>
+ <item android:state_enabled="true"
+ android:state_hovered="true"
+ android:color="@color/materialColorOnPrimary"
+ android:alpha="0.11" />
+ <item android:color="@android:color/transparent" />
+</selector>
\ No newline at end of file
diff --git a/quickstep/res/color/keyboard_quick_switch_scroll_button_icon.xml b/quickstep/res/color/keyboard_quick_switch_scroll_button_icon.xml
new file mode 100644
index 0000000..74df84b
--- /dev/null
+++ b/quickstep/res/color/keyboard_quick_switch_scroll_button_icon.xml
@@ -0,0 +1,23 @@
+<?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.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false"
+ android:color="@color/materialColorOnPrimary"
+ android:alpha="0.38"/>
+ <item android:color="@color/materialColorOnPrimary"/>
+</selector>
diff --git a/quickstep/res/drawable/app_chip_fg.xml b/quickstep/res/drawable/app_chip_fg.xml
new file mode 100644
index 0000000..7b19c9e
--- /dev/null
+++ b/quickstep/res/drawable/app_chip_fg.xml
@@ -0,0 +1,18 @@
+<?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/app_chip_state_color_fg" />
+</shape>
\ No newline at end of file
diff --git a/quickstep/res/drawable/app_chip_menu_bg.xml b/quickstep/res/drawable/app_chip_menu_bg.xml
new file mode 100644
index 0000000..499056e
--- /dev/null
+++ b/quickstep/res/drawable/app_chip_menu_bg.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="@color/materialColorSurfaceBright"/>
+ <corners android:radius="@dimen/task_menu_corner_radius"/>
+</shape>
diff --git a/quickstep/res/drawable/app_chip_menu_item_bg.xml b/quickstep/res/drawable/app_chip_menu_item_bg.xml
new file mode 100644
index 0000000..39e88d2
--- /dev/null
+++ b/quickstep/res/drawable/app_chip_menu_item_bg.xml
@@ -0,0 +1,19 @@
+<?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" />
+</shape>
diff --git a/quickstep/res/drawable/app_chip_menu_item_fg.xml b/quickstep/res/drawable/app_chip_menu_item_fg.xml
new file mode 100644
index 0000000..96d067d
--- /dev/null
+++ b/quickstep/res/drawable/app_chip_menu_item_fg.xml
@@ -0,0 +1,20 @@
+<?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/app_chip_menu_item_color_fg" />
+ <corners android:radius="@dimen/task_menu_item_corner_radius" />
+</shape>
diff --git a/quickstep/res/drawable/bg_keyboard_quick_switch_scroll_button.xml b/quickstep/res/drawable/bg_keyboard_quick_switch_scroll_button.xml
new file mode 100644
index 0000000..7067f13
--- /dev/null
+++ b/quickstep/res/drawable/bg_keyboard_quick_switch_scroll_button.xml
@@ -0,0 +1,20 @@
+<?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" android:shape="rectangle">
+ <solid android:color="@color/keyboard_quick_switch_scroll_button_bg" />
+ <corners android:radius="@dimen/keyboard_quick_switch_scroll_button_corner_radius" />
+</shape>
diff --git a/quickstep/res/drawable/fg_keyboard_quick_switch_scroll_button.xml b/quickstep/res/drawable/fg_keyboard_quick_switch_scroll_button.xml
new file mode 100644
index 0000000..dd63f54
--- /dev/null
+++ b/quickstep/res/drawable/fg_keyboard_quick_switch_scroll_button.xml
@@ -0,0 +1,20 @@
+<?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" android:shape="rectangle">
+ <solid android:color="@color/keyboard_quick_switch_scroll_button_fg" />
+ <corners android:radius="@dimen/keyboard_quick_switch_scroll_button_corner_radius" />
+</shape>
diff --git a/quickstep/res/layout-land/keyboard_quick_switch_taskview.xml b/quickstep/res/layout-land/keyboard_quick_switch_taskview.xml
index b44510d..b45a04a 100644
--- a/quickstep/res/layout-land/keyboard_quick_switch_taskview.xml
+++ b/quickstep/res/layout-land/keyboard_quick_switch_taskview.xml
@@ -21,14 +21,14 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:importantForAccessibility="yes"
- android:background="@drawable/keyboard_quick_switch_task_view_background"
- android:clipToOutline="true"
- launcher:focusBorderColor="@color/materialColorOutline">
+ launcher:focusBorderColor="@color/materialColorSecondary">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/content"
android:layout_width="@dimen/keyboard_quick_switch_taskview_width"
android:layout_height="@dimen/keyboard_quick_switch_taskview_height"
+ android:background="@drawable/keyboard_quick_switch_task_view_background"
+ android:clipToOutline="true"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
diff --git a/quickstep/res/layout-land/keyboard_quick_switch_taskview_square.xml b/quickstep/res/layout-land/keyboard_quick_switch_taskview_square.xml
index 56b1adf..672b715 100644
--- a/quickstep/res/layout-land/keyboard_quick_switch_taskview_square.xml
+++ b/quickstep/res/layout-land/keyboard_quick_switch_taskview_square.xml
@@ -21,14 +21,14 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:importantForAccessibility="yes"
- android:background="@drawable/keyboard_quick_switch_task_view_background"
- android:clipToOutline="true"
- launcher:focusBorderColor="@androidprv:color/materialColorOutline">
+ launcher:focusBorderColor="@color/materialColorSecondary">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/content"
android:layout_width="0dp"
android:layout_height="@dimen/keyboard_quick_switch_taskview_height"
+ android:background="@drawable/keyboard_quick_switch_task_view_background"
+ android:clipToOutline="true"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintTop_toTopOf="parent"
diff --git a/quickstep/res/layout/gesture_tutorial_fragment.xml b/quickstep/res/layout/gesture_tutorial_fragment.xml
index 64ad1f7..6e7ff86 100644
--- a/quickstep/res/layout/gesture_tutorial_fragment.xml
+++ b/quickstep/res/layout/gesture_tutorial_fragment.xml
@@ -153,16 +153,6 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/gesture_tutorial_fragment_feedback_title"/>
- <com.android.quickstep.interaction.TutorialStepIndicator
- android:id="@+id/gesture_tutorial_fragment_feedback_tutorial_step"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
-
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toTopOf="@id/gesture_tutorial_fragment_action_button"
- app:layout_constraintBottom_toBottomOf="@id/gesture_tutorial_fragment_action_button"/>
-
<Button
android:id="@+id/gesture_tutorial_fragment_action_button"
style="@style/TextAppearance.GestureTutorial.ButtonLabel"
diff --git a/quickstep/res/layout/icon_app_chip_view.xml b/quickstep/res/layout/icon_app_chip_view.xml
index 0972be1..09fb509 100644
--- a/quickstep/res/layout/icon_app_chip_view.xml
+++ b/quickstep/res/layout/icon_app_chip_view.xml
@@ -22,10 +22,12 @@
android:layout_width="@dimen/task_thumbnail_icon_menu_expanded_width"
android:layout_height="@dimen/task_thumbnail_icon_menu_expanded_height"
android:clipToOutline="true"
- android:focusable="false"
+ android:focusable="true"
+ android:focusableInTouchMode="false"
android:importantForAccessibility="no"
android:autoMirrored="true"
android:elevation="@dimen/task_thumbnail_icon_menu_elevation"
+ android:foreground="@drawable/app_chip_fg"
android:background="@color/materialColorSurfaceBright">
<!-- ignoring warning because the user of the anchor is a Rect where RTL is not needed -->
@@ -77,4 +79,5 @@
android:background="@drawable/icon_menu_arrow_background"
android:src="@drawable/ic_chevron_down"
android:importantForAccessibility="no" />
+
</com.android.quickstep.views.IconAppChipView>
\ No newline at end of file
diff --git a/quickstep/res/layout/keyboard_quick_switch_desktop_taskview.xml b/quickstep/res/layout/keyboard_quick_switch_desktop_taskview.xml
index db47ff0..d281732 100644
--- a/quickstep/res/layout/keyboard_quick_switch_desktop_taskview.xml
+++ b/quickstep/res/layout/keyboard_quick_switch_desktop_taskview.xml
@@ -20,9 +20,8 @@
xmlns:launcher="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:clipToOutline="true"
android:importantForAccessibility="yes"
- launcher:focusBorderColor="@androidprv:color/materialColorOutline"
+ launcher:focusBorderColor="@color/materialColorSecondary"
launcher:focusBorderRadius="@dimen/keyboard_quick_switch_text_button_radius">
<androidx.constraintlayout.widget.ConstraintLayout
@@ -31,6 +30,7 @@
android:layout_height="@dimen/keyboard_quick_switch_taskview_height"
android:background="@drawable/keyboard_quick_switch_text_button_background"
android:backgroundTint="@androidprv:color/materialColorSurfaceContainer"
+ android:clipToOutline="true"
android:paddingHorizontal="@dimen/keyboard_quick_switch_text_button_horizontal_padding"
app:layout_constraintTop_toTopOf="parent"
diff --git a/quickstep/res/layout/keyboard_quick_switch_overview_taskview.xml b/quickstep/res/layout/keyboard_quick_switch_overview_taskview.xml
index 5a3ee83..3321306 100644
--- a/quickstep/res/layout/keyboard_quick_switch_overview_taskview.xml
+++ b/quickstep/res/layout/keyboard_quick_switch_overview_taskview.xml
@@ -20,9 +20,8 @@
xmlns:launcher="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:clipToOutline="true"
android:importantForAccessibility="yes"
- launcher:focusBorderColor="@androidprv:color/materialColorOutline"
+ launcher:focusBorderColor="@color/materialColorSecondary"
launcher:focusBorderRadius="@dimen/keyboard_quick_switch_text_button_radius">
<androidx.constraintlayout.widget.ConstraintLayout
@@ -31,6 +30,7 @@
android:layout_height="@dimen/keyboard_quick_switch_taskview_height"
android:background="@drawable/keyboard_quick_switch_text_button_background"
android:backgroundTint="@androidprv:color/materialColorSurfaceBright"
+ android:clipToOutline="true"
android:paddingHorizontal="@dimen/keyboard_quick_switch_text_button_horizontal_padding"
app:layout_constraintTop_toTopOf="parent"
diff --git a/quickstep/res/layout/keyboard_quick_switch_taskview.xml b/quickstep/res/layout/keyboard_quick_switch_taskview.xml
index 37bb027..218a3f2 100644
--- a/quickstep/res/layout/keyboard_quick_switch_taskview.xml
+++ b/quickstep/res/layout/keyboard_quick_switch_taskview.xml
@@ -21,14 +21,14 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:importantForAccessibility="yes"
- android:background="@drawable/keyboard_quick_switch_task_view_background"
- android:clipToOutline="true"
- launcher:focusBorderColor="@color/materialColorOutline">
+ launcher:focusBorderColor="@color/materialColorSecondary">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/content"
android:layout_width="@dimen/keyboard_quick_switch_taskview_width"
android:layout_height="@dimen/keyboard_quick_switch_taskview_height"
+ android:background="@drawable/keyboard_quick_switch_task_view_background"
+ android:clipToOutline="true"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
diff --git a/quickstep/res/layout/keyboard_quick_switch_taskview_square.xml b/quickstep/res/layout/keyboard_quick_switch_taskview_square.xml
index 33d8c16..d3f5bf1 100644
--- a/quickstep/res/layout/keyboard_quick_switch_taskview_square.xml
+++ b/quickstep/res/layout/keyboard_quick_switch_taskview_square.xml
@@ -21,14 +21,14 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:importantForAccessibility="yes"
- android:background="@drawable/keyboard_quick_switch_task_view_background"
- android:clipToOutline="true"
- launcher:focusBorderColor="@androidprv:color/materialColorOutline">
+ launcher:focusBorderColor="@color/materialColorSecondary">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/content"
android:layout_width="0dp"
android:layout_height="@dimen/keyboard_quick_switch_taskview_height"
+ android:background="@drawable/keyboard_quick_switch_task_view_background"
+ android:clipToOutline="true"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintTop_toTopOf="parent"
diff --git a/quickstep/res/layout/keyboard_quick_switch_view.xml b/quickstep/res/layout/keyboard_quick_switch_view.xml
index 345b97c..2dea79c 100644
--- a/quickstep/res/layout/keyboard_quick_switch_view.xml
+++ b/quickstep/res/layout/keyboard_quick_switch_view.xml
@@ -18,6 +18,8 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/keyboard_quick_switch_view"
+ android:contentDescription="@string/quick_switch_content_description"
+ android:accessibilityPaneTitle="@string/quick_switch_pane_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/keyboard_quick_switch_margin_top"
@@ -67,6 +69,24 @@
</androidx.constraintlayout.widget.ConstraintLayout>
+ <ImageButton
+ android:id="@+id/scroll_button_start"
+ android:src="@drawable/ic_chevron_start"
+ android:contentDescription="@string/quick_switch_scroll_arrow_left"
+ android:background="@drawable/bg_keyboard_quick_switch_scroll_button"
+ android:foreground="@drawable/fg_keyboard_quick_switch_scroll_button"
+ android:tint="@color/keyboard_quick_switch_scroll_button_icon"
+ android:layout_width="@dimen/keyboard_quick_switch_scroll_button_width"
+ android:layout_height="@dimen/keyboard_quick_switch_scroll_button_height"
+ android:paddingHorizontal="@dimen/keyboard_quick_switch_scroll_button_horizontal_padding"
+ android:paddingVertical="@dimen/keyboard_quick_switch_scroll_button_vertical_padding"
+ android:layout_marginStart="@dimen/keyboard_quick_switch_view_spacing"
+ android:visibility="gone"
+
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"/>
+
<HorizontalScrollView
android:id="@+id/scroll_view"
android:layout_width="wrap_content"
@@ -76,9 +96,12 @@
android:alpha="0"
android:visibility="gone"
+ app:layout_constraintHorizontal_bias="0"
+ app:layout_constrainedWidth="true"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintStart_toStartOf="parent">
+ app:layout_constraintStart_toEndOf="@id/scroll_button_start"
+ app:layout_constraintEnd_toStartOf="@id/scroll_button_end">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/content"
@@ -89,4 +112,22 @@
</HorizontalScrollView>
+ <ImageButton
+ android:id="@+id/scroll_button_end"
+ android:src="@drawable/ic_chevron_end"
+ android:contentDescription="@string/quick_switch_scroll_arrow_right"
+ android:background="@drawable/bg_keyboard_quick_switch_scroll_button"
+ android:foreground="@drawable/fg_keyboard_quick_switch_scroll_button"
+ android:tint="@color/keyboard_quick_switch_scroll_button_icon"
+ android:layout_width="@dimen/keyboard_quick_switch_scroll_button_width"
+ android:layout_height="@dimen/keyboard_quick_switch_scroll_button_height"
+ android:paddingHorizontal="@dimen/keyboard_quick_switch_scroll_button_horizontal_padding"
+ android:paddingVertical="@dimen/keyboard_quick_switch_scroll_button_vertical_padding"
+ android:layout_marginEnd="@dimen/keyboard_quick_switch_view_spacing"
+ android:visibility="gone"
+
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintEnd_toEndOf="parent" />
+
</com.android.launcher3.taskbar.KeyboardQuickSwitchView>
diff --git a/quickstep/res/layout/redesigned_gesture_tutorial_fragment.xml b/quickstep/res/layout/redesigned_gesture_tutorial_fragment.xml
index 8ca59c4..55fe2b8 100644
--- a/quickstep/res/layout/redesigned_gesture_tutorial_fragment.xml
+++ b/quickstep/res/layout/redesigned_gesture_tutorial_fragment.xml
@@ -165,16 +165,6 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/gesture_tutorial_fragment_feedback_title" />
- <com.android.quickstep.interaction.TutorialStepIndicator
- android:id="@+id/gesture_tutorial_fragment_feedback_tutorial_step"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
-
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent" />
-
<Button
android:id="@+id/gesture_tutorial_fragment_close_button"
style="@style/TextAppearance.GestureTutorial.Feedback.Subtext"
@@ -225,7 +215,6 @@
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"
diff --git a/quickstep/res/layout/task.xml b/quickstep/res/layout/task.xml
index 3aac1b6..4abfbbe 100644
--- a/quickstep/res/layout/task.xml
+++ b/quickstep/res/layout/task.xml
@@ -29,8 +29,8 @@
launcher:hoverBorderColor="@color/materialColorPrimary">
<ViewStub
- android:id="@+id/snapshot"
- android:inflatedId="@id/snapshot"
+ android:id="@+id/task_content_view"
+ android:inflatedId="@id/task_content_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
diff --git a/quickstep/res/layout/task_content_view.xml b/quickstep/res/layout/task_content_view.xml
new file mode 100644
index 0000000..9055ccd
--- /dev/null
+++ b/quickstep/res/layout/task_content_view.xml
@@ -0,0 +1,20 @@
+<?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.task.thumbnail.TaskContentView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/task_content_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical" />
\ No newline at end of file
diff --git a/quickstep/res/layout/task_grouped.xml b/quickstep/res/layout/task_grouped.xml
index 3e6f5ed..a7c4856 100644
--- a/quickstep/res/layout/task_grouped.xml
+++ b/quickstep/res/layout/task_grouped.xml
@@ -34,14 +34,14 @@
launcher:hoverBorderColor="@color/materialColorPrimary">
<ViewStub
- android:id="@+id/snapshot"
- android:inflatedId="@id/snapshot"
+ android:id="@+id/task_content_view"
+ android:inflatedId="@id/task_content_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ViewStub
- android:id="@+id/bottomright_snapshot"
- android:inflatedId="@id/bottomright_snapshot"
+ android:id="@+id/bottomright_task_content_view"
+ android:inflatedId="@id/bottomright_task_content_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
diff --git a/quickstep/res/layout/task_header_view.xml b/quickstep/res/layout/task_header_view.xml
new file mode 100644
index 0000000..849153f
--- /dev/null
+++ b/quickstep/res/layout/task_header_view.xml
@@ -0,0 +1,62 @@
+<?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.TaskHeaderView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/task_header_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="@dimen/task_thumbnail_header_padding_top_bottom"
+ android:paddingBottom="@dimen/task_thumbnail_header_padding_top_bottom"
+ android:paddingStart="@dimen/task_thumbnail_header_padding_start_end"
+ android:paddingEnd="@dimen/task_thumbnail_header_padding_start_end"
+ android:background="@drawable/task_thumbnail_header_bg">
+ <ImageView
+ android:id="@+id/header_app_icon"
+ 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:layout_marginEnd="@dimen/task_thumbnail_header_margin_between_views"
+ android:contentDescription="@string/header_app_icon_description"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+ <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:maxLines="1"
+ android:text="@string/header_default_app_title"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toEndOf="@id/header_app_icon"
+ app:layout_constraintTop_toTopOf="parent" />
+ <ImageButton
+ android:id="@+id/header_close_button"
+ 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:layout_marginEnd="@dimen/task_thumbnail_header_margin_between_views"
+ android:background="@null"
+ android:contentDescription="@string/header_close_icon_description"
+ android:src="@drawable/task_header_close_button"
+ android:tint="@android:color/darker_gray"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="1"
+ app:layout_constraintStart_toEndOf="@id/header_app_title"
+ app:layout_constraintTop_toTopOf="parent" />
+</com.android.quickstep.views.TaskHeaderView>
diff --git a/quickstep/res/layout/task_menu.xml b/quickstep/res/layout/task_menu.xml
index b6d8786..abc728a 100644
--- a/quickstep/res/layout/task_menu.xml
+++ b/quickstep/res/layout/task_menu.xml
@@ -43,8 +43,7 @@
android:id="@+id/menu_option_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:showDividers="middle" />
+ android:orientation="horizontal" />
</ScrollView>
diff --git a/quickstep/res/layout/task_thumbnail.xml b/quickstep/res/layout/task_thumbnail.xml
index 3b96615..8280e13 100644
--- a/quickstep/res/layout/task_thumbnail.xml
+++ b/quickstep/res/layout/task_thumbnail.xml
@@ -17,7 +17,8 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/snapshot"
android:layout_width="match_parent"
- android:layout_height="match_parent" >
+ android:layout_height="0dp"
+ android:layout_weight="1" >
<com.android.quickstep.views.FixedSizeImageView
android:id="@+id/task_thumbnail"
diff --git a/quickstep/res/layout/task_thumbnail_view_header.xml b/quickstep/res/layout/task_thumbnail_view_header.xml
deleted file mode 100644
index 70e4a42..0000000
--- a/quickstep/res/layout/task_thumbnail_view_header.xml
+++ /dev/null
@@ -1,72 +0,0 @@
-<?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:id="@+id/task_thumbnail_view_header"
- 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"
- android:background="@null"
- 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 c922238..83e6f79 100644
--- a/quickstep/res/values-af/strings.xml
+++ b/quickstep/res/values-af/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Taakbalkoorloop"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Skuif na links bo"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Skuif na regs onder"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Maak app as ’n borrel oop"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{meer app}other{meer apps}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Werkskerm"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> en <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Borrel"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Oorvloei"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> vanaf <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Speld aan taakbalk vas"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Ontspeld van taakbalk"</string>
</resources>
diff --git a/quickstep/res/values-am/strings.xml b/quickstep/res/values-am/strings.xml
index 97b0ac7..d596734 100644
--- a/quickstep/res/values-am/strings.xml
+++ b/quickstep/res/values-am/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"የተግባር አሞሌ ትርፍ ፍሰት"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ወደ ላይ/ግራ ይውሰዱ"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ወደ ታች/ቀኝ ይውሰዱ"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"መተግበሪያን እንደ ዓረፋ ይክፈቱ"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{ተጨማሪ መተግበሪያ}one{ተጨማሪ መተግበሪያ}other{ተጨማሪ መተግበሪያዎች}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"አረፋ"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ትርፍ ፍሰት"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ከ<xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"ተግባር አሞሌ ላይ ፒን አድርግ"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"ከተግባር አሞሌ ንቀል"</string>
</resources>
diff --git a/quickstep/res/values-ar/strings.xml b/quickstep/res/values-ar/strings.xml
index 5b61fa7..ec37983 100644
--- a/quickstep/res/values-ar/strings.xml
+++ b/quickstep/res/values-ar/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"القائمة الكاملة لشريط التطبيقات"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"الانتقال إلى يمين الشاشة أو أعلاها"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"الانتقال إلى يسار الشاشة أو أسفلها"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"فتح التطبيق كفقاعة"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{تطبيق واحد آخر}zero{تطبيق آخر}two{تطبيقان آخران}few{تطبيقات أخرى}many{تطبيقًا آخر}other{تطبيق آخر}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"فقاعة"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"القائمة الكاملة"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"\"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>\" من \"<xliff:g id="APP_NAME">%2$s</xliff:g>\""</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"التثبيت على شريط التطبيقات"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"إزالة التثبيت من شريط التطبيقات"</string>
</resources>
diff --git a/quickstep/res/values-as/strings.xml b/quickstep/res/values-as/strings.xml
index d7cb836..9cadc10 100644
--- a/quickstep/res/values-as/strings.xml
+++ b/quickstep/res/values-as/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"টাস্কবাৰ অ’ভাৰফ্ল"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ওপৰৰ বাঁওফাললৈ নিয়ক"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"তলৰ সোঁফাললৈ নিয়ক"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"বাবল হিচাপে এপ্টো খোলক"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{অধিক এপ্}one{অধিক এপ্}other{অধিক এপ্}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"বাবল"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"অ’ভাৰফ্ল’"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g>ৰ পৰা <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"টাস্কবাৰত পিন কৰক"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"টাস্কবাৰৰ পৰা আনপিন কৰক"</string>
</resources>
diff --git a/quickstep/res/values-az/strings.xml b/quickstep/res/values-az/strings.xml
index dab4070..bf34d27 100644
--- a/quickstep/res/values-az/strings.xml
+++ b/quickstep/res/values-az/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Tapşırıqlar Paneli üzrə əlavə menyu"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Yuxarı/sola köçürün"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Aşağı/sağa köçürün"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Tətbiqi yumrucuq kimi açın"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{əlavə tətbiq}other{əlavə tətbiq}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Masaüstü"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> və <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Yumrucuq"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Kənara çıxma"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>, <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"İşləmə panelinə bərkidin"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"İşləmə panelindən çıxarın"</string>
</resources>
diff --git a/quickstep/res/values-b+sr+Latn/strings.xml b/quickstep/res/values-b+sr+Latn/strings.xml
index 58a9c6b..14724e9 100644
--- a/quickstep/res/values-b+sr+Latn/strings.xml
+++ b/quickstep/res/values-b+sr+Latn/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Preklopna traka zadataka"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Premesti gore levo"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Premesti dole desno"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Otvori aplikaciju kao oblačić"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{dodatna aplikacija}one{dodatna aplikacija}few{dodatne aplikacije}other{dodatnih aplikacija}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Računar"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> i <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Oblačić"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Preklopni"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> – <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Zakači za traku zad."</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Otkači sa trake zad."</string>
</resources>
diff --git a/quickstep/res/values-be/strings.xml b/quickstep/res/values-be/strings.xml
index 5ea5761..5f55b41 100644
--- a/quickstep/res/values-be/strings.xml
+++ b/quickstep/res/values-be/strings.xml
@@ -90,8 +90,8 @@
<string name="allset_title" msgid="5021126669778966707">"Гатова!"</string>
<string name="allset_hint" msgid="459504134589971527">"Каб перайсці на галоўны экран, правядзіце пальцам уверх"</string>
<string name="allset_button_hint" msgid="2395219947744706291">"Каб перайсці на галоўны экран, націсніце кнопку галоўнага экрана"</string>
- <string name="allset_description_generic" msgid="5385500062202019855">"Вы можаце пачаць карыстанне прыладай \"<xliff:g id="DEVICE">%1$s</xliff:g>\""</string>
- <string name="default_device_name" msgid="6660656727127422487">"прылада"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Вы можаце пачаць карыстацца <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"прыладай"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Налады навігацыі ў сістэме"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Абагуліць"</string>
<string name="action_screenshot" msgid="8171125848358142917">"Здымак экрана"</string>
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Меню з пашырэннем панэлі задач"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Перамясціць уверх/улева"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Перамясціць уніз/управа"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Адкрыць праграму ва ўсплывальным акне"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{даступная праграма}one{даступная праграма}few{даступныя праграмы}many{даступных праграм}other{даступнай праграмы}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Бурбалкі"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Меню з пашырэннем"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>, крыніца: <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Замацаваць на панэлі"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Адмацаваць ад панэлі"</string>
</resources>
diff --git a/quickstep/res/values-bg/strings.xml b/quickstep/res/values-bg/strings.xml
index 5c01d29..871501f 100644
--- a/quickstep/res/values-bg/strings.xml
+++ b/quickstep/res/values-bg/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Меню при препълване на лентата на задачите"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Преместване горе/вляво"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Преместване долу/вдясно"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Отваряне на приложението като балонче"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{допълнително приложение}other{допълнителни приложения}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Балонче"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Препълване"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> от <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Лент. на зад.: фикс."</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Лент. на зад.: осв."</string>
</resources>
diff --git a/quickstep/res/values-bn/strings.xml b/quickstep/res/values-bn/strings.xml
index 01954cf..a1e30bb 100644
--- a/quickstep/res/values-bn/strings.xml
+++ b/quickstep/res/values-bn/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"টাস্কবার ওভারফ্লো"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"উপরে/বাঁদিকে সরান"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"নিচে/ডানদিকে সরান"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"বাবল হিসেবে অ্যাপ খুলুন"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{আরও অ্যাপ}one{আরও অ্যাপ}other{আরও অ্যাপ}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"বাবল"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ওভারফ্লো"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g> থেকে <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"টাস্কবারে পিন করুন"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"টাস্কবার থেকে আনপিন করুন"</string>
</resources>
diff --git a/quickstep/res/values-bs/strings.xml b/quickstep/res/values-bs/strings.xml
index 18c178b..57aa3c3 100644
--- a/quickstep/res/values-bs/strings.xml
+++ b/quickstep/res/values-bs/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Preklopni meni trake zadataka"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Premjesti gore lijevo"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Premjesti dolje desno"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Otvori aplikaciju kao oblačić"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{dodatna aplikacija}one{dodatna aplikacija}few{dodatne aplikacije}other{dodatnih aplikacija}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Računar"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> i <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Oblačić"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Preklopni meni"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> iz aplikacije <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Zakači na traku zadataka"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Otkači s trake zadataka"</string>
</resources>
diff --git a/quickstep/res/values-ca/strings.xml b/quickstep/res/values-ca/strings.xml
index 23cd439..e729ff5 100644
--- a/quickstep/res/values-ca/strings.xml
+++ b/quickstep/res/values-ca/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Menú addicional de la barra de tasques"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mou a la part superior o a l\'esquerra"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mou a la part inferior o a la dreta"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Obre l\'aplicació com a bombolla"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{aplicació més}other{aplicacions més}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Escriptori"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> i <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Bombolla"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Desbordament"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Fixa a barra tasques"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Deixa de fixar"</string>
</resources>
diff --git a/quickstep/res/values-cs/strings.xml b/quickstep/res/values-cs/strings.xml
index d809cbe..5106ae3 100644
--- a/quickstep/res/values-cs/strings.xml
+++ b/quickstep/res/values-cs/strings.xml
@@ -139,9 +139,16 @@
<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>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Přesunout doprava dolů"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Otevřít aplikaci jako bublinu"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{další aplikace}few{další aplikace}many{další aplikace}other{dalších aplikací}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Počítač"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> a <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Bublina"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Rozbalovací nabídka"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> z aplikace <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Připnout na panel"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Odepnout z panelu"</string>
</resources>
diff --git a/quickstep/res/values-da/strings.xml b/quickstep/res/values-da/strings.xml
index 4bae930..1328da9 100644
--- a/quickstep/res/values-da/strings.xml
+++ b/quickstep/res/values-da/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Prikmenu på proceslinjen"</string>
<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="open_app_as_a_bubble" msgid="6642626287247807473">"Åbn appen som en boble"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<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">"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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Boble"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Overløb"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> fra <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Fastgør til proceslinje"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Frigør fra proceslinje"</string>
</resources>
diff --git a/quickstep/res/values-de/strings.xml b/quickstep/res/values-de/strings.xml
index 7986a55..2074d5e 100644
--- a/quickstep/res/values-de/strings.xml
+++ b/quickstep/res/values-de/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Dreipunkt-Menü der Taskleiste"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Nach oben / Nach links verschieben"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Nach unten / Nach rechts verschieben"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"App als Bubble öffnen"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{weitere App}other{weitere Apps}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Desktopmodus"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> und <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Bubble"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Weitere Optionen"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"„<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>“ aus <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"An Taskleiste pinnen"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Von Taskleiste lösen"</string>
</resources>
diff --git a/quickstep/res/values-el/strings.xml b/quickstep/res/values-el/strings.xml
index 438dcf8..8951e20 100644
--- a/quickstep/res/values-el/strings.xml
+++ b/quickstep/res/values-el/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Υπερχείλιση γραμμής εργαλείων"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Μετακίνηση επάνω/αριστερά"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Μετακίνηση κάτω/δεξιά"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Άνοιγμα εφαρμογής σε συννεφάκι"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{ακόμη εφαρμογή}other{ακόμη εφαρμογές}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Συννεφάκι"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Υπερχείλιση"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> από <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Καρφ. σε γρ. εργαλ."</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Ξεκαρφ. από γρ. εργαλ."</string>
</resources>
diff --git a/quickstep/res/values-en-rAU/strings.xml b/quickstep/res/values-en-rAU/strings.xml
index 954db96..18e5984 100644
--- a/quickstep/res/values-en-rAU/strings.xml
+++ b/quickstep/res/values-en-rAU/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Taskbar overflow"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Move to top/left"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Move to bottom/right"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Open app as a bubble"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{more app}other{more apps}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Desktop"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> and <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Bubble"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Overflow"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> from <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Pin to taskbar"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Unpin from taskbar"</string>
</resources>
diff --git a/quickstep/res/values-en-rCA/strings.xml b/quickstep/res/values-en-rCA/strings.xml
index 4efd369..8f52c66 100644
--- a/quickstep/res/values-en-rCA/strings.xml
+++ b/quickstep/res/values-en-rCA/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Taskbar Overflow"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Move to top/left"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Move to bottom/right"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Open app as a bubble"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{more app}other{more apps}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Desktop"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> and <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Bubble"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Overflow"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> from <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Pin to taskbar"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Unpin from taskbar"</string>
</resources>
diff --git a/quickstep/res/values-en-rGB/strings.xml b/quickstep/res/values-en-rGB/strings.xml
index 954db96..18e5984 100644
--- a/quickstep/res/values-en-rGB/strings.xml
+++ b/quickstep/res/values-en-rGB/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Taskbar overflow"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Move to top/left"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Move to bottom/right"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Open app as a bubble"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{more app}other{more apps}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Desktop"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> and <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Bubble"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Overflow"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> from <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Pin to taskbar"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Unpin from taskbar"</string>
</resources>
diff --git a/quickstep/res/values-en-rIN/strings.xml b/quickstep/res/values-en-rIN/strings.xml
index 954db96..18e5984 100644
--- a/quickstep/res/values-en-rIN/strings.xml
+++ b/quickstep/res/values-en-rIN/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Taskbar overflow"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Move to top/left"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Move to bottom/right"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Open app as a bubble"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{more app}other{more apps}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Desktop"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> and <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Bubble"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Overflow"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> from <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Pin to taskbar"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Unpin from taskbar"</string>
</resources>
diff --git a/quickstep/res/values-es-rUS/strings.xml b/quickstep/res/values-es-rUS/strings.xml
index 6cd544c..d909af1 100644
--- a/quickstep/res/values-es-rUS/strings.xml
+++ b/quickstep/res/values-es-rUS/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Barra de tareas ampliada"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mover a la parte superior o izquierda"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mover a la parte inferior o derecha"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Abrir app como burbuja"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<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">"Escritorio"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Burbuja"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Ampliada"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Fijar a la barra"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"No fijar a la barra"</string>
</resources>
diff --git a/quickstep/res/values-es/strings.xml b/quickstep/res/values-es/strings.xml
index 7f7b433..016e579 100644
--- a/quickstep/res/values-es/strings.xml
+++ b/quickstep/res/values-es/strings.xml
@@ -92,7 +92,7 @@
<string name="allset_button_hint" msgid="2395219947744706291">"Toca el botón de inicio para ir a la pantalla de inicio"</string>
<string name="allset_description_generic" msgid="5385500062202019855">"Ya puedes empezar a usar tu <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
<string name="default_device_name" msgid="6660656727127422487">"dispositivo"</string>
- <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Ajustes de navegación del sistema"</annotation></string>
+ <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Opciones de navegación"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Compartir"</string>
<string name="action_screenshot" msgid="8171125848358142917">"Hacer captura"</string>
<string name="action_split" msgid="2098009717623550676">"Dividir"</string>
@@ -139,9 +139,16 @@
<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="open_app_as_a_bubble" msgid="6642626287247807473">"Abrir aplicación como burbuja"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Burbuja"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Menú adicional"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Fijar a la barra"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Desfijar de la barra"</string>
</resources>
diff --git a/quickstep/res/values-et/strings.xml b/quickstep/res/values-et/strings.xml
index d4e041a..ecf59af 100644
--- a/quickstep/res/values-et/strings.xml
+++ b/quickstep/res/values-et/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Tegumiriba ületäide"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Teisalda üles/vasakule"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Teisalda alla/paremale"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Rakenduse avamine mullina"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{rakendus veel}other{rakendust veel}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Lauaarvuti"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> ja <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Mull"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Ületäide"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> – <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Tegumiribale kinnitamine"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Tegumiribalt vabastamine"</string>
</resources>
diff --git a/quickstep/res/values-eu/strings.xml b/quickstep/res/values-eu/strings.xml
index 0a912d8..90748c0 100644
--- a/quickstep/res/values-eu/strings.xml
+++ b/quickstep/res/values-eu/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Zereginen barraren luzapena"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Eraman gora, ezkerretara"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Eraman behera, eskuinetara"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Ireki aplikazioa burbuila gisa"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{aplikazio gehiago}other{aplikazio gehiago}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Ordenagailua"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> eta <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Burbuila"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Luzapena"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> (<xliff:g id="APP_NAME">%2$s</xliff:g>)"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Ainguratu zereginen barran"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Kendu aingura zereginen barratik"</string>
</resources>
diff --git a/quickstep/res/values-fa/strings.xml b/quickstep/res/values-fa/strings.xml
index 7dcdb7f..ee9ed1d 100644
--- a/quickstep/res/values-fa/strings.xml
+++ b/quickstep/res/values-fa/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"سرریز نوار وظیفه"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"انتقال به بالا/ چپ"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"انتقال به پایین/ راست"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"باز کردن برنامه بهصورت حبابک"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{برنامه دیگر}one{برنامه دیگر}other{برنامه دیگر}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"حبابک"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"سرریز"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> از <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"سنجاق به نوار وظیفه"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"برداشتن از نواروظیفه"</string>
</resources>
diff --git a/quickstep/res/values-fi/strings.xml b/quickstep/res/values-fi/strings.xml
index adeaa34..f681e0d 100644
--- a/quickstep/res/values-fi/strings.xml
+++ b/quickstep/res/values-fi/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Tehtäväpalkin ylivuotu"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Siirrä ylös tai vasemmalle"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Siirrä alas tai oikealle"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Avaa sovellus kuplana"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{muu sovellus}other{muuta sovellusta}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Tietokone"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> ja <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Kupla"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Ylivuoto"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>: <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Kiinnitä palkkiin"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Poista palkista"</string>
</resources>
diff --git a/quickstep/res/values-fr-rCA/strings.xml b/quickstep/res/values-fr-rCA/strings.xml
index 3201886..5b1f149 100644
--- a/quickstep/res/values-fr-rCA/strings.xml
+++ b/quickstep/res/values-fr-rCA/strings.xml
@@ -90,7 +90,7 @@
<string name="allset_title" msgid="5021126669778966707">"Tout est prêt!"</string>
<string name="allset_hint" msgid="459504134589971527">"Balayez l\'écran vers le haut pour accéder à l\'écran d\'accueil"</string>
<string name="allset_button_hint" msgid="2395219947744706291">"Toucher le bouton d\'accueil pour passer sur votre écran d\'accueil"</string>
- <string name="allset_description_generic" msgid="5385500062202019855">"Vous êtes maintenant prêt à utiliser votre <xliff:g id="DEVICE">%1$s</xliff:g>."</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Vous êtes maintenant prêt à utiliser votre <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
<string name="default_device_name" msgid="6660656727127422487">"appareil"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Paramètres de navigation du système"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Partager"</string>
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Barre des tâches à développer"</string>
<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="open_app_as_a_bubble" msgid="6642626287247807473">"Ouvrir l\'appli sous forme de bulle"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<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">"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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Bulle"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Bulle à développer"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Éping. (barre tâche)"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Détacher (bar. tâc.)"</string>
</resources>
diff --git a/quickstep/res/values-fr/strings.xml b/quickstep/res/values-fr/strings.xml
index 9982b25..95b448d 100644
--- a/quickstep/res/values-fr/strings.xml
+++ b/quickstep/res/values-fr/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Développement de la barre des tâches"</string>
<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="open_app_as_a_bubble" msgid="6642626287247807473">"Ouvrir l\'appli sous forme de bulle"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<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">"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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Bulle"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Dépassement"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> (<xliff:g id="APP_NAME">%2$s</xliff:g>)"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Épingler à la barre"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Désépingler de la barre"</string>
</resources>
diff --git a/quickstep/res/values-gl/strings.xml b/quickstep/res/values-gl/strings.xml
index df665ed..e59b451 100644
--- a/quickstep/res/values-gl/strings.xml
+++ b/quickstep/res/values-gl/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Menú adicional da barra de tarefas"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mover á parte superior ou á esquerda"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mover á parte inferior ou á dereita"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Abrir aplicación como unha burbulla"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{aplicación máis}other{aplicacións máis}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Escritorio"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> e <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Burbulla"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Menú adicional"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Fixar na barra"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Soltar da barra"</string>
</resources>
diff --git a/quickstep/res/values-gu/strings.xml b/quickstep/res/values-gu/strings.xml
index 7145c6c9..b0238d0 100644
--- a/quickstep/res/values-gu/strings.xml
+++ b/quickstep/res/values-gu/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"ટાસ્કબાર ઓવરફ્લો"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"સૌથી ઉપર ડાબી બાજુએ ખસેડો"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"સૌથી નીચે જમણી બાજુએ ખસેડો"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"બબલ તરીકે ઍપ ખોલો"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{વધુ ઍપ}one{વધુ ઍપ}other{વધુ ઍપ}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"બબલ"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ઓવરફ્લો"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g>થી <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"ટાસ્કબારમાં પિન કરો"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"ટાસ્કબારમાંથી અનપિન કરો"</string>
</resources>
diff --git a/quickstep/res/values-hi/strings.xml b/quickstep/res/values-hi/strings.xml
index 99b7b71..6aba44c 100644
--- a/quickstep/res/values-hi/strings.xml
+++ b/quickstep/res/values-hi/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"टास्कबार ओवरफ़्लो आइकॉन"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ऊपर/बाईं तरफ़ ले जाएं"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"नीचे/दाईं तरफ़ ले जाएं"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"ऐप्लिकेशन को बबल के तौर पर खोलें"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{ज़्यादा ऐप्लिकेशन}one{ज़्यादा ऐप्लिकेशन}other{ज़्यादा ऐप्लिकेशन}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"बबल"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ओवरफ़्लो"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g> की <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> वाली सूचना"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"टास्कबार में पिन करें"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"टास्कबार से अनपिन करें"</string>
</resources>
diff --git a/quickstep/res/values-hr/strings.xml b/quickstep/res/values-hr/strings.xml
index 67fe240..963ce57 100644
--- a/quickstep/res/values-hr/strings.xml
+++ b/quickstep/res/values-hr/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Dodatni izbornik trake sa zadacima"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Premjesti gore/lijevo"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Premjesti dolje/desno"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Otvori aplikaciju kao oblačić"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{dodatna aplikacija}one{dodatna aplikacija}few{dodatne aplikacije}other{dodatnih aplikacija}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Računalo"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> i <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Oblačić"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Dodatni izbornik"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>, <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Prikvači na traku"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Otkvači s trake"</string>
</resources>
diff --git a/quickstep/res/values-hu/strings.xml b/quickstep/res/values-hu/strings.xml
index f825032..093c92d 100644
--- a/quickstep/res/values-hu/strings.xml
+++ b/quickstep/res/values-hu/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Feladatsáv túlcsordulása"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mozgatás felülre vagy a bal oldalra"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mozgatás alulra vagy a jobb oldalra"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Alkalmazás megnyitása buborékként"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{további alkalmazás}other{további alkalmazás}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Asztali"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> és <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Buborék"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Túlcsordulás"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>, forrás: <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Kitűzés a feladatsávon"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Kitűzés feloldása"</string>
</resources>
diff --git a/quickstep/res/values-hy/strings.xml b/quickstep/res/values-hy/strings.xml
index 6a7653f..0fa5d2d 100644
--- a/quickstep/res/values-hy/strings.xml
+++ b/quickstep/res/values-hy/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Հավելվածների վահանակի լրացուցիչ ընտրացանկ"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Տեղափոխել վերևի ձախ անկյուն"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Տեղափոխել ներքևի աջ անկյուն"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Բացել հավելվածը պղպջակի ձևով"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{լրացուցիչ հավելված}one{լրացուցիչ հավելված}other{լրացուցիչ հավելված}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Ամպիկ"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Լրացուցիչ ընտրացանկ"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>՝ <xliff:g id="APP_NAME">%2$s</xliff:g> հավելվածից"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Ամրացնել վահանակում"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Ապամրացնել վահանակից"</string>
</resources>
diff --git a/quickstep/res/values-in/strings.xml b/quickstep/res/values-in/strings.xml
index 5b5796c..fe28340 100644
--- a/quickstep/res/values-in/strings.xml
+++ b/quickstep/res/values-in/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Tambahan Taskbar"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Pindahkan ke atas/kiri"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Pindahkan ke bawah/kanan"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Buka aplikasi sebagai balon"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{aplikasi lainnya}other{aplikasi lainnya}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Desktop"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> dan <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Balon"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Tambahan"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> dari <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Sematkan ke taskbar"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Lepaskan dari taskbar"</string>
</resources>
diff --git a/quickstep/res/values-is/strings.xml b/quickstep/res/values-is/strings.xml
index 12541f7..a0657dd 100644
--- a/quickstep/res/values-is/strings.xml
+++ b/quickstep/res/values-is/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Yfirflæði á forritastiku"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Færa efst/til vinstri"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Færa neðst/til hægri"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Opna forrit sem blöðru"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{forrit til viðbótar}one{forrit til viðbótar}other{forrit til viðbótar}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Skjáborð"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Blaðra"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Yfirflæði"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> frá <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Festa á forritastiku"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Losa af forritastiku"</string>
</resources>
diff --git a/quickstep/res/values-it/strings.xml b/quickstep/res/values-it/strings.xml
index 731184b..bc795e4 100644
--- a/quickstep/res/values-it/strings.xml
+++ b/quickstep/res/values-it/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Overflow barra delle app"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Sposta in alto/a sinistra"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Sposta in basso/a destra"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Apri l\'app come fumetto"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{altra app}other{altre app}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Desktop"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> e <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Fumetto"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Extra"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> da <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Fissa alla barra app"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Sblocca da barra app"</string>
</resources>
diff --git a/quickstep/res/values-iw/strings.xml b/quickstep/res/values-iw/strings.xml
index 0fff001..3f5ddfc 100644
--- a/quickstep/res/values-iw/strings.xml
+++ b/quickstep/res/values-iw/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"אפשרויות נוספות בסרגל האפליקציות"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"העברה לפינה השמאלית/העליונה"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"העברה לפינה הימנית/התחתונה"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"פתיחת האפליקציה בבועה"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{אפליקציה נוספת}one{אפליקציות נוספות}two{אפליקציות נוספות}other{אפליקציות נוספות}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"בועה"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"אפשרויות נוספות"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> מתוך <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"הצמדה לסרגל האפליקציות"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"ביטול ההצמדה לסרגל"</string>
</resources>
diff --git a/quickstep/res/values-ja/strings.xml b/quickstep/res/values-ja/strings.xml
index ded498a..d6f64a5 100644
--- a/quickstep/res/values-ja/strings.xml
+++ b/quickstep/res/values-ja/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"タスクバーのオーバフロー"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"上 / 左に移動"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"下 / 右に移動"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"アプリをバブルとして開く"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{個のその他のアプリ}other{個のその他のアプリ}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"ふきだし"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"オーバーフロー"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>(<xliff:g id="APP_NAME">%2$s</xliff:g>)"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"タスクバーに固定"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"タスクバーの固定解除"</string>
</resources>
diff --git a/quickstep/res/values-ka/strings.xml b/quickstep/res/values-ka/strings.xml
index 1604495..1ea1eea 100644
--- a/quickstep/res/values-ka/strings.xml
+++ b/quickstep/res/values-ka/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"ამოცანათა ზოლის გადავსება"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ზემოთ/მარცხნივ გადატანა"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ქვემოთ/მარჯვნივ გადატანა"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"აპის გახსნა ბუშტის სახით"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{სხვა აპი}other{სხვა აპი}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"ბუშტი"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"გადავსება"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>: <xliff:g id="APP_NAME">%2$s</xliff:g>-იდან"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"ამოც. ზოლში ჩანიშვნა"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"ამოც. ზოლიდან მოხსნა"</string>
</resources>
diff --git a/quickstep/res/values-kk/strings.xml b/quickstep/res/values-kk/strings.xml
index 76a6199..b06ac37 100644
--- a/quickstep/res/values-kk/strings.xml
+++ b/quickstep/res/values-kk/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"\"Тапсырмалар жолағы\" қосымша мәзірі"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Жоғары/солға жылжыту"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Төмен/оңға жылжыту"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Қолданбаны қалқыма терезе түрінде ашу"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{қосымша қолданба}other{қосымша қолданба}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Қалқыма терезе"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Қосымша мәзір"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g> ұсынатын <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Тапсырмалар жолағына бекіту"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Тапсырмалар жолағынан босату"</string>
</resources>
diff --git a/quickstep/res/values-km/strings.xml b/quickstep/res/values-km/strings.xml
index 1fdcde9..6686851 100644
--- a/quickstep/res/values-km/strings.xml
+++ b/quickstep/res/values-km/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"ម៉ឺនុយបន្ថែមរបារកិច្ចការ"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ផ្លាស់ទីទៅខាងលើ/ឆ្វេង"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ផ្លាស់ទីទៅខាងក្រោម/ស្ដាំ"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"បើកកម្មវិធីជាផ្ទាំងសារ"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{កម្មវិធីច្រើនទៀត}other{កម្មវិធីច្រើនទៀត}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"ផ្ទាំងសារ"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ម៉ឺនុយបន្ថែម"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ពី <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"ខ្ទាស់ទៅរបារកិច្ចការ"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"ដកខ្ទាស់ពីរបារកិច្ចការ"</string>
</resources>
diff --git a/quickstep/res/values-kn/strings.xml b/quickstep/res/values-kn/strings.xml
index c43a3d1..3bb3d46 100644
--- a/quickstep/res/values-kn/strings.xml
+++ b/quickstep/res/values-kn/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"ಟಾಸ್ಕ್ ಬಾರ್ ಓವರ್ಫ್ಲೋ"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ಮೇಲಿನ/ಎಡಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ಕೆಳಗಿನ/ಬಲಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"ಆ್ಯಪ್ ಅನ್ನು ಬಬಲ್ ಆಗಿ ತೆರೆಯಿರಿ"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{ಹೆಚ್ಚಿನ ಆ್ಯಪ್}one{ಹೆಚ್ಚಿನ ಆ್ಯಪ್ಗಳು}other{ಹೆಚ್ಚಿನ ಆ್ಯಪ್ಗಳು}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"ಬಬಲ್"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ಓವರ್ಫ್ಲೋ"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g> ನಿಂದ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"ಟಾಸ್ಕ್ಬಾರ್ಗೆ ಪಿನ್ ಮಾಡಿ"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"ಟಾಸ್ಕ್ಬಾರ್ನಿಂದ ಅನ್ಪಿನ್"</string>
</resources>
diff --git a/quickstep/res/values-ko/strings.xml b/quickstep/res/values-ko/strings.xml
index 4105f97..367908f 100644
--- a/quickstep/res/values-ko/strings.xml
+++ b/quickstep/res/values-ko/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"태스크 바 오버플로"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"상단/왼쪽으로 이동"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"하단/오른쪽으로 이동"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"앱을 대화창으로 열기"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{추가 앱}other{추가 앱}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"풍선"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"더보기"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g>의 <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"태스크 바에 고정"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"태스크 바에서 고정 해제"</string>
</resources>
diff --git a/quickstep/res/values-ky/strings.xml b/quickstep/res/values-ky/strings.xml
index 3109069..834e38f 100644
--- a/quickstep/res/values-ky/strings.xml
+++ b/quickstep/res/values-ky/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"\"Тапшырмалар панели\" кошумча менюсу"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Жогорку/сол бурчка жылдыруу"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Төмөнкү/оң бурчка жылдыруу"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Колдонмону көбүкчө катары ачуу"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{колдонмо бар}other{колдонмо бар}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Көбүкчө"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Кошумча меню"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g> колдонмосунан <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Тапшырмалар панелине кадоо"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Тапшырмалар панелинен алып коюу"</string>
</resources>
diff --git a/quickstep/res/values-lo/strings.xml b/quickstep/res/values-lo/strings.xml
index 3df475f..80b745b 100644
--- a/quickstep/res/values-lo/strings.xml
+++ b/quickstep/res/values-lo/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"ສ່ວນເພີ່ມເຕີມຂອງແຖບໜ້າວຽກ"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ຍ້າຍໄປຊ້າຍ/ເທິງ"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ຍ້າຍໄປຂວາ/ລຸ່ມ"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"ເປີດແອັບເປັນຟອງ"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{ແອັບເພີ່ມເຕີມ}other{ແອັບເພີ່ມເຕີມ}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"ຟອງ"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ລາຍການເພີ່ມເຕີມ"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ຈາກ <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"ປັກໝຸດໄປໃສ່ແຖບໜ້າວຽກ"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"ຖອດປັກໝຸດຈາກແຖບໜ້າວຽກ"</string>
</resources>
diff --git a/quickstep/res/values-lt/strings.xml b/quickstep/res/values-lt/strings.xml
index ec16b11..bb1fff3 100644
--- a/quickstep/res/values-lt/strings.xml
+++ b/quickstep/res/values-lt/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Užduočių juostos perpildymas"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Perkelti aukštyn, kairėn"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Perkelti žemyn, dešinėn"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Atidaryti programą kaip burbulą"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{papildoma programa}one{papildoma programa}few{papildomos programos}many{papildomos programos}other{papildomų programų}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Stalinis kompiuteris"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"„<xliff:g id="APP_NAME_1">%1$s</xliff:g>“ ir „<xliff:g id="APP_NAME_2">%2$s</xliff:g>“"</string>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Burbulas"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Perpildymas"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"„<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>“ iš „<xliff:g id="APP_NAME">%2$s</xliff:g>“"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Priseg. prie užd. j."</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Atsegti nuo užd. j."</string>
</resources>
diff --git a/quickstep/res/values-lv/strings.xml b/quickstep/res/values-lv/strings.xml
index 0f15a23..c3c21a6 100644
--- a/quickstep/res/values-lv/strings.xml
+++ b/quickstep/res/values-lv/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Uzdevumu joslas pārpilde"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Pārvietot uz augšējo/kreiso stūri"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Pārvietot uz apakšējo/labo stūri"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Atvērt lietotni kā burbuli"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{papildu lietotne}zero{papildu lietotņu}one{papildu lietotne}other{papildu lietotnes}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Darbvirsma"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"“<xliff:g id="APP_NAME_1">%1$s</xliff:g>” un “<xliff:g id="APP_NAME_2">%2$s</xliff:g>”"</string>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Burbulis"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Pārpilde"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> no lietotnes <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Piespraust uzdevumu joslai"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Atspraust no uzdevumu joslas"</string>
</resources>
diff --git a/quickstep/res/values-mk/strings.xml b/quickstep/res/values-mk/strings.xml
index 8fd0cc5..7362c82 100644
--- a/quickstep/res/values-mk/strings.xml
+++ b/quickstep/res/values-mk/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Проширено балонче на „Лента со задачи“"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Премести горе лево"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Премести долу десно"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Отвори апликација како балонче"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{дополнителна апликација}one{дополнителна апликација}other{дополнителни апликации}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Балонче"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Проширено балонче"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> од <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Закачи на лентата"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Откачи од лентата"</string>
</resources>
diff --git a/quickstep/res/values-ml/strings.xml b/quickstep/res/values-ml/strings.xml
index 849315b..9aa1d1c 100644
--- a/quickstep/res/values-ml/strings.xml
+++ b/quickstep/res/values-ml/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"ടാസ്ക്ബാർ ഓവർഫ്ലോ"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"മുകളിലേക്കോ ഇടത്തേക്കോ നീക്കുക"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"താഴേക്കോ വലത്തേക്കോ നീക്കുക"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"ആപ്പ് ഒരു ബബിളായി തുറക്കുക"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{കൂടുതൽ ആപ്പ്}other{കൂടുതൽ ആപ്പുകൾ}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"ബബിൾ"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ഓവർഫ്ലോ"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g> എന്നതിൽ നിന്നുള്ള <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"ടാസ്ക്ബാറിൽ ചേർക്കൂ"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"ടാസ്ക്ബാറിൽ നിന്ന് മാറ്റൂ"</string>
</resources>
diff --git a/quickstep/res/values-mn/strings.xml b/quickstep/res/values-mn/strings.xml
index f086314..129b75c 100644
--- a/quickstep/res/values-mn/strings.xml
+++ b/quickstep/res/values-mn/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Ажлын хэсгийн урт цэс"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Зүүн дээд хэсэг рүү зөөх"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Баруун доод хэсэг рүү зөөх"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Аппыг бөмбөлөг байдлаар нээх"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{бусад апп}other{бусад апп}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Бөмбөлөг"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Илүү хэсэг"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g>-с ирсэн <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Ажлын хэсэгт бэхлэх"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Бэхэлснийг болиулах"</string>
</resources>
diff --git a/quickstep/res/values-mr/strings.xml b/quickstep/res/values-mr/strings.xml
index 75c42ad..818b824 100644
--- a/quickstep/res/values-mr/strings.xml
+++ b/quickstep/res/values-mr/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"टास्कबार ओव्हरफ्लो"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"सर्वात वरती/डावीकडे हलवा"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"तळाशी/उजवीकडे हलवा"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"बबल म्हणून अॅप उघडा"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{आणखी अॅप}other{आणखी अॅप्स}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"बबल"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ओव्हरफ्लो"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g> वरील <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"टास्कबारवर पिन करा"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"टास्कबारवरून अनपिन करा"</string>
</resources>
diff --git a/quickstep/res/values-ms/strings.xml b/quickstep/res/values-ms/strings.xml
index b039666..c06811b 100644
--- a/quickstep/res/values-ms/strings.xml
+++ b/quickstep/res/values-ms/strings.xml
@@ -88,7 +88,7 @@
<string name="gesture_tutorial_nice" msgid="2936275692616928280">"Bagus!"</string>
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Tutorial <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Siap!"</string>
- <string name="allset_hint" msgid="459504134589971527">"Leret ke atas untuk ke laman utama"</string>
+ <string name="allset_hint" msgid="459504134589971527">"Leret ke atas untuk ke skrin utama"</string>
<string name="allset_button_hint" msgid="2395219947744706291">"Ketik butang skrin utama untuk pergi ke skrin utama anda"</string>
<string name="allset_description_generic" msgid="5385500062202019855">"Anda sudah sedia untuk mula menggunakan <xliff:g id="DEVICE">%1$s</xliff:g> anda"</string>
<string name="default_device_name" msgid="6660656727127422487">"peranti"</string>
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Limpahan Bar Tugas"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Alihkan ke atas/kiri"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Alihkan ke bawah/kanan"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Buka apl sebagai gelembung"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{apl lagi}other{apl lagi}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Desktop"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> dan <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Gelembung"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Limpahan"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> daripada <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Semat pada bar tugas"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Nyahsemat drpd bar tugas"</string>
</resources>
diff --git a/quickstep/res/values-my/strings.xml b/quickstep/res/values-my/strings.xml
index 81a10b5..b556ded 100644
--- a/quickstep/res/values-my/strings.xml
+++ b/quickstep/res/values-my/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Taskbar မီနူးအပို"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"အပေါ်/ဘယ်ဘက်သို့ ရွှေ့ရန်"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"အောက်ခြေ/ညာဘက်သို့ ရွှေ့ရန်"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"အက်ပ်ကို ပူဖောင်းကွက်အဖြစ် ဖွင့်ရန်"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{နောက်ထပ်အက်ပ်}other{နောက်ထပ်အက်ပ်များ}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"ပူဖောင်းကွက်"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"မီနူးအပို"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g> မှ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Taskbar ၌ ပင်ထိုးရန်"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Taskbar မှ ဖြုတ်ရန်"</string>
</resources>
diff --git a/quickstep/res/values-nb/strings.xml b/quickstep/res/values-nb/strings.xml
index 4c1aaaa..4528a83 100644
--- a/quickstep/res/values-nb/strings.xml
+++ b/quickstep/res/values-nb/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Overflyt for oppgavelinjen"</string>
<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="open_app_as_a_bubble" msgid="6642626287247807473">"Åpne appen som en boble"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{app til}other{apper til}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Boble"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Overflyt"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> fra <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Fest i oppgavelinjen"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Løsne i oppgavelinje"</string>
</resources>
diff --git a/quickstep/res/values-ne/strings.xml b/quickstep/res/values-ne/strings.xml
index 47b2532..2d35a22 100644
--- a/quickstep/res/values-ne/strings.xml
+++ b/quickstep/res/values-ne/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"टास्कबार ओभरफ्लो"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"सिरान/बायाँतिर सार्नुहोस्"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"फेद/दायाँतिर सार्नुहोस्"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"एपलाई बबलका रूपमा खोल्नुहोस्"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{थप एप}other{थप एपहरू}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"बबल"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ओभरफ्लो"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g> मा देखाइएका <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"टास्कबारमा पिन गर्नुहोस्"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"टास्कबारबाट पिन गर्नुहोस्"</string>
</resources>
diff --git a/quickstep/res/values-nl/strings.xml b/quickstep/res/values-nl/strings.xml
index 6cf9683..3086c40 100644
--- a/quickstep/res/values-nl/strings.xml
+++ b/quickstep/res/values-nl/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Taakbalkoverloop"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Naar boven/links verplaatsen"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Naar beneden/rechts verplaatsen"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"App openen als ballon"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{extra app}other{extra apps}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Desktop"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> en <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Bubbel"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Overloop"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> van <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Vastzetten op taakbalk"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Losmaken van taakbalk"</string>
</resources>
diff --git a/quickstep/res/values-or/strings.xml b/quickstep/res/values-or/strings.xml
index 248d4cc..4581f97 100644
--- a/quickstep/res/values-or/strings.xml
+++ b/quickstep/res/values-or/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"ଟାସ୍କବାର ଓଭରଫ୍ଲୋ"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ଶୀର୍ଷ/ବାମକୁ ମୁଭ କରନ୍ତୁ"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ନିମ୍ନ/ଡାହାଣକୁ ମୁଭ କରନ୍ତୁ"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"ଏକ ବବଲ ଭାବେ ଆପ ଖୋଲନ୍ତୁ"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{ଅଧିକ ଆପ}other{ଅଧିକ ଆପ୍ସ}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"ବବଲ"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ଓଭରଫ୍ଲୋ"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g>ରୁ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"ଟାସ୍କବାରରେ ପିନ କର"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"ଟାସ୍କବାରରୁ ଅନପିନ କର"</string>
</resources>
diff --git a/quickstep/res/values-pa/strings.xml b/quickstep/res/values-pa/strings.xml
index 8489fd1..ac88e6f 100644
--- a/quickstep/res/values-pa/strings.xml
+++ b/quickstep/res/values-pa/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"ਟਾਸਕਬਾਰ ਓਵਰਫ਼ਲੋ"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ਸਿਖਰਲੇ/ਖੱਬੇ ਪਾਸੇ ਲੈ ਕੇ ਜਾਓ"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ਹੇਠਾਂ/ਸੱਜੇ ਪਾਸੇ ਲੈ ਕੇ ਜਾਓ"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"ਐਪ ਨੂੰ ਬਬਲ ਵਜੋਂ ਖੋਲ੍ਹੋ"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{ਹੋਰ ਐਪ}one{ਹੋਰ ਐਪ}other{ਹੋਰ ਐਪਾਂ}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"ਬਬਲ"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ਓਵਰਫ਼ਲੋ"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g> ਤੋਂ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"ਟਾਸਕਬਾਰ \'ਤੇ ਪਿੰਨ ਕਰੋ"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"ਟਾਸਕਬਾਰ ਤੋਂ ਅਣਪਿੰਨ ਕਰੋ"</string>
</resources>
diff --git a/quickstep/res/values-pl/strings.xml b/quickstep/res/values-pl/strings.xml
index 5c76187..090dc4c 100644
--- a/quickstep/res/values-pl/strings.xml
+++ b/quickstep/res/values-pl/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Rozwijany pasek aplikacji"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Przesuń w górny lewy róg"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Przesuń w dolny prawy róg"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Otwórz aplikację jako dymek"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{inna aplikacja}few{inne aplikacje}many{innych aplikacji}other{innej aplikacji}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Pulpit"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> i <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Dymek"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Rozwijany"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> z aplikacji <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Przypnij do paska"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Odepnij od paska"</string>
</resources>
diff --git a/quickstep/res/values-pt-rPT/strings.xml b/quickstep/res/values-pt-rPT/strings.xml
index 84a4f58..ff5dbf7 100644
--- a/quickstep/res/values-pt-rPT/strings.xml
+++ b/quickstep/res/values-pt-rPT/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Menu adicional da Barra de tarefas"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mover para a parte superior esquerda"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mover para a part superior direita"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Abrir app como um balão"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{outra app}other{outras apps}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Computador"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> e <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Balão"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Menu adicional"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> da app <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Afixar na barra tar."</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Desaf. da barra tar."</string>
</resources>
diff --git a/quickstep/res/values-pt/strings.xml b/quickstep/res/values-pt/strings.xml
index fad3164..bc0b838 100644
--- a/quickstep/res/values-pt/strings.xml
+++ b/quickstep/res/values-pt/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Barra de tarefas flutuante"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mover para cima/para a esquerda"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mover para baixo/para a direita"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Abrir o app como um balão"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{outro app}one{outro app}other{outros apps}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Computador"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> e <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Balão"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Balão flutuante"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> do app <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Fixar na barra de tarefas"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Liberar da barra de tarefas"</string>
</resources>
diff --git a/quickstep/res/values-ro/strings.xml b/quickstep/res/values-ro/strings.xml
index a89ccdc..0d1aab1 100644
--- a/quickstep/res/values-ro/strings.xml
+++ b/quickstep/res/values-ro/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Meniu suplimentar pentru bara de activități"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mută în stânga sus"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mută în dreapta jos"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Deschide aplicația ca balon"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{aplicație suplimentară}few{mai multe aplicații}other{mai multe aplicații}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Computer"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> și <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Balon"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Suplimentar"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de la <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Fixează pe bara de activități"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Anulează fixarea din bara de activități"</string>
</resources>
diff --git a/quickstep/res/values-ru/strings.xml b/quickstep/res/values-ru/strings.xml
index 9ebbc02..3cf299d 100644
--- a/quickstep/res/values-ru/strings.xml
+++ b/quickstep/res/values-ru/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Дополнительное меню панели задач"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Переместить вверх или влево"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Переместить вниз или вправо"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Открыть приложение во всплывающем окне"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{дополнительное приложение}one{дополнительное приложение}few{дополнительных приложения}many{дополнительных приложений}other{дополнительного приложения}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Всплывающая подсказка"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Дополнительное меню"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"\"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>\" из приложения \"<xliff:g id="APP_NAME">%2$s</xliff:g>\""</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Закрепить на панели"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Открепить от панели"</string>
</resources>
diff --git a/quickstep/res/values-si/strings.xml b/quickstep/res/values-si/strings.xml
index de23b6e..0b4efc4 100644
--- a/quickstep/res/values-si/strings.xml
+++ b/quickstep/res/values-si/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"කාර්ය තීරුව පිටාර යාම"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ඉහළ/වම වෙත ගෙන යන්න"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"පහළ/දකුණ වෙත ගෙන යන්න"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"යෙදුම බුබුලක් ලෙස විවෘත කරන්න"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{තව යෙදුම}one{තවත් යෙදුම්}other{තවත් යෙදුම්}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"බුබුළු"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"පිටාර යාම"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g> සිට <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"කාර්ය තීරුවට අමුණන්න"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"කාර්ය තීරුවෙන් ඉවත් කරන්න"</string>
</resources>
diff --git a/quickstep/res/values-sk/strings.xml b/quickstep/res/values-sk/strings.xml
index bf9adff..9ca4ae7 100644
--- a/quickstep/res/values-sk/strings.xml
+++ b/quickstep/res/values-sk/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Rozšírená ponuka panela aplikácií"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Presunúť hore alebo doľava"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Presunúť dole alebo doprava"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Otvoriť aplikáciu ako bublinu"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{ďalšia aplikácia}few{ďalšie aplikácie}many{ďalšie aplikácie}other{ďalšie aplikácie}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Počítač"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> a <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Bublina"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Rozbaľovacia ponuka"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> z aplikácie <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Pripnúť na panel"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Odopnúť z panela"</string>
</resources>
diff --git a/quickstep/res/values-sl/strings.xml b/quickstep/res/values-sl/strings.xml
index 440c0f1..9a437bc 100644
--- a/quickstep/res/values-sl/strings.xml
+++ b/quickstep/res/values-sl/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Oblaček opravilne vrstice z dodatnimi elementi"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Premakni na vrh/levo"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Premakni na dno/desno"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Odpri aplikacijo kot oblaček"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{dodatna aplikacija}one{dodatna aplikacija}two{dodatni aplikaciji}few{dodatne aplikacije}other{dodatnih aplikacij}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Namizni računalnik"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> in <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Oblaček"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Oblaček z dodatnimi elementi"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> iz aplikacije <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Pripni v opravilno vrstico"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Odpni iz opravilne vrstice"</string>
</resources>
diff --git a/quickstep/res/values-sq/strings.xml b/quickstep/res/values-sq/strings.xml
index 9aedee5..c72b926 100644
--- a/quickstep/res/values-sq/strings.xml
+++ b/quickstep/res/values-sq/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Tejkalimi i shiritit të detyrave"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Lëviz në krye/majtas"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Lëviz në fund/djathtas"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Hap aplikacionin si një flluskë"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{aplikacion tjetër}other{aplikacione të tjera}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Desktop"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> dhe <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Flluska"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Tejkalimi"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"\"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>\" nga <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Gozhdo te shiriti"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Zhgozhdo nga shiriti"</string>
</resources>
diff --git a/quickstep/res/values-sr/strings.xml b/quickstep/res/values-sr/strings.xml
index e810d5a..c418302 100644
--- a/quickstep/res/values-sr/strings.xml
+++ b/quickstep/res/values-sr/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Преклопна трака задатака"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Премести горе лево"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Премести доле десно"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Отвори апликацију као облачић"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{додатна апликација}one{додатна апликација}few{додатне апликације}other{додатних апликација}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Облачић"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Преклопни"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> – <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Закачи за траку зад."</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Откачи са траке зад."</string>
</resources>
diff --git a/quickstep/res/values-sv/strings.xml b/quickstep/res/values-sv/strings.xml
index ab9f10e..76c8d3d 100644
--- a/quickstep/res/values-sv/strings.xml
+++ b/quickstep/res/values-sv/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Fler alternativ för aktivitetsfältet"</string>
<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="open_app_as_a_bubble" msgid="6642626287247807473">"Öppna appen som en bubbla"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{app till}other{appar till}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Bubbla"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Fler alternativ"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> från <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Fäst i aktivitetsfält"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Lossa från aktivitetsfält"</string>
</resources>
diff --git a/quickstep/res/values-sw/strings.xml b/quickstep/res/values-sw/strings.xml
index 5f696c5..ca800d9 100644
--- a/quickstep/res/values-sw/strings.xml
+++ b/quickstep/res/values-sw/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Upauzana wa Vipengele vya Ziada"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Sogeza juu/kushoto"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Sogeza chini/kulia"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Fungua programu kama kiputo"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{programu nyingine}other{programu zingine}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Kompyuta ya Mezani"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> na <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Kiputo"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Kiputo cha vipengee vya ziada"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> kutoka <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Bandika kwa upauzana"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Bandua kwa upauzana"</string>
</resources>
diff --git a/quickstep/res/values-ta/strings.xml b/quickstep/res/values-ta/strings.xml
index 4f22a04..236982a 100644
--- a/quickstep/res/values-ta/strings.xml
+++ b/quickstep/res/values-ta/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"செயல் பட்டிக்கான கூடுதல் விருப்பங்கள்"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"மேலே/இடதுபுறம் நகர்த்தும்"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"கீழே/வலதுபுறம் நகர்த்தும்"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"ஆப்ஸைக் குமிழாகத் திற"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{கூடுதல் ஆப்ஸ்}other{கூடுதல் ஆப்ஸ்}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"குமிழ்"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"கூடுதல் விருப்பங்களைக் காட்டும்"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g> வழங்கும் <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"செயல்பட்டியில் பின் செய்"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"செயல்பட்டியிலிருந்து அகற்று"</string>
</resources>
diff --git a/quickstep/res/values-te/strings.xml b/quickstep/res/values-te/strings.xml
index 429d2d4..d5bc982 100644
--- a/quickstep/res/values-te/strings.xml
+++ b/quickstep/res/values-te/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"టాస్క్బార్ ఓవర్ఫ్లో"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ఎగువ/ఎడమ వైపునకు తరలించండి"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"దిగువ/కుడి వైపునకు తరలించండి"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"యాప్ను బబుల్లాగా తెరవండి"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{మరో యాప్}other{మరిన్ని యాప్లు}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"బబుల్"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ఓవర్ఫ్లో"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g> నుండి <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"టాస్క్బార్కు పిన్"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"టాస్క్బార్ అన్పిన్"</string>
</resources>
diff --git a/quickstep/res/values-th/strings.xml b/quickstep/res/values-th/strings.xml
index 03852ff..6767358 100644
--- a/quickstep/res/values-th/strings.xml
+++ b/quickstep/res/values-th/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"การดำเนินการเพิ่มเติมของแถบงาน"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ย้ายไปที่ด้านบนหรือด้านซ้าย"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ย้ายไปที่ด้านล่างหรือด้านขวา"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"เปิดแอปเป็นบับเบิล"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{แอปเพิ่มเติม}other{แอปเพิ่มเติม}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"บับเบิล"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"การดำเนินการเพิ่มเติม"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> จาก <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"ปักหมุดไปยังแถบงาน"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"เลิกปักหมุดจากแถบงาน"</string>
</resources>
diff --git a/quickstep/res/values-tl/strings.xml b/quickstep/res/values-tl/strings.xml
index 1b3696e..479a5d3 100644
--- a/quickstep/res/values-tl/strings.xml
+++ b/quickstep/res/values-tl/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Taskbar Overflow"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Ilipat sa itaas/kaliwa"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Ilipat sa ibaba/kanan"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Buksan ang app bilang bubble"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{pang app}one{pang app}other{pang app}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Desktop"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> at <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Bubble"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Overflow"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> mula sa <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"I-pin sa taskbar"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"I-unpin sa taskbar"</string>
</resources>
diff --git a/quickstep/res/values-tr/strings.xml b/quickstep/res/values-tr/strings.xml
index e213b40..e68056a 100644
--- a/quickstep/res/values-tr/strings.xml
+++ b/quickstep/res/values-tr/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Görev Çubuğu Taşması"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Sol üste taşı"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Sağ alta taşı"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Uygulamayı balon olarak aç"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{uygulama daha}other{uygulama daha}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Masaüstü"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> ve <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Balon"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Taşma"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g> uygulamasından <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Çubuğa sabitle"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Çubuktan kaldır"</string>
</resources>
diff --git a/quickstep/res/values-uk/strings.xml b/quickstep/res/values-uk/strings.xml
index 2313a07..350b23b 100644
--- a/quickstep/res/values-uk/strings.xml
+++ b/quickstep/res/values-uk/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Додаткове меню панелі завдань"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Перемістити вгору або вліво"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Перемістити вниз або вправо"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Відкрити додаток у спливаючому вікні"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{інший додаток}one{інший додаток}few{інші додатки}many{інших додатків}other{іншого додатка}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Повідомлення"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Додаткове повідомлення"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> з додатка <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"На панель завдань"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"З панелі завдань"</string>
</resources>
diff --git a/quickstep/res/values-ur/strings.xml b/quickstep/res/values-ur/strings.xml
index 0a3e122..3b46f51 100644
--- a/quickstep/res/values-ur/strings.xml
+++ b/quickstep/res/values-ur/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"ٹاسک بار اوورفلو"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"اوپر/بائیں طرف منتقل کریں"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"نیچے/دائیں طرف منتقل کریں"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"ایپ کو بطور ببل کھولیں"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{مزید ایپ}other{مزید ایپس}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"ببل"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"اوورفلو"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g> سے <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"ٹاسک بار میں پن کریں"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"ٹاسک بار سے پن ہٹائیں"</string>
</resources>
diff --git a/quickstep/res/values-uz/strings.xml b/quickstep/res/values-uz/strings.xml
index 06ca97a..3f61658 100644
--- a/quickstep/res/values-uz/strings.xml
+++ b/quickstep/res/values-uz/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Vazifalar panelini kengaytirish"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Yuqoriga yoki chapga oʻtkazish"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Pastga yoki oʻngga oʻtkazish"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Ilovani qalqib chiquvchi oynada ochish"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{boshqa ilova}other{boshqa ilovalar}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Kompyuter"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> va <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Pufak"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Kengaytirish"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> (<xliff:g id="APP_NAME">%2$s</xliff:g>)"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Panelga qadash"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Vazifalar panelidan yechib olish"</string>
</resources>
diff --git a/quickstep/res/values-vi/strings.xml b/quickstep/res/values-vi/strings.xml
index bf50e05..6f48660 100644
--- a/quickstep/res/values-vi/strings.xml
+++ b/quickstep/res/values-vi/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Trình đơn mục bổ sung trên thanh tác vụ"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Chuyển lên trên cùng/sang bên trái"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Chuyển xuống dưới cùng/sang bên phải"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Mở ứng dụng dưới dạng bong bóng"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{ứng dụng khác}other{ứng dụng khác}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Máy tính"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> và <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Bong bóng"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Bong bóng bổ sung"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> từ <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Ghim vào thanh tác vụ"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Bỏ ghim khỏi thanh tác vụ"</string>
</resources>
diff --git a/quickstep/res/values-zh-rCN/strings.xml b/quickstep/res/values-zh-rCN/strings.xml
index 374a4b6..8e8badc 100644
--- a/quickstep/res/values-zh-rCN/strings.xml
+++ b/quickstep/res/values-zh-rCN/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"任务栏溢出图标"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"移到顶部/左侧"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"移到底部/右侧"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"以气泡的形式打开应用"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{多个应用}other{多个应用}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"气泡框"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"溢出式气泡框"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"来自“<xliff:g id="APP_NAME">%2$s</xliff:g>”的<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"固定到任务栏"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"从任务栏取消固定"</string>
</resources>
diff --git a/quickstep/res/values-zh-rHK/strings.xml b/quickstep/res/values-zh-rHK/strings.xml
index 42e2e37..9f5745a 100644
--- a/quickstep/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-zh-rHK/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"工作列溢位"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"移至上方/左側"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"移至底部/右側"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"在小視窗開啟應用程式"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{個其他應用程式}other{個其他應用程式}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"對話氣泡"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"展開式"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g> 的「<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>」通知"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"固定至工作列"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"取消固定至工作列"</string>
</resources>
diff --git a/quickstep/res/values-zh-rTW/strings.xml b/quickstep/res/values-zh-rTW/strings.xml
index d9d1fb5..d11cafd 100644
--- a/quickstep/res/values-zh-rTW/strings.xml
+++ b/quickstep/res/values-zh-rTW/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"工作列溢位"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"移到上方/左側"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"移到底部/右側"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"以泡泡形式開啟應用程式"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{個其他應用程式}other{個其他應用程式}}"</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>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"泡泡"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"溢位"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"「<xliff:g id="APP_NAME">%2$s</xliff:g>」的「<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>」通知"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"固定到工作列"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"從工作列中取消固定"</string>
</resources>
diff --git a/quickstep/res/values-zu/strings.xml b/quickstep/res/values-zu/strings.xml
index 660ef3e..1d99965 100644
--- a/quickstep/res/values-zu/strings.xml
+++ b/quickstep/res/values-zu/strings.xml
@@ -139,9 +139,16 @@
<string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Ukuphuphuma Kwetaskbar"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Hamba phezulu/kwesokunxele"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Hamba phansi/kwesokudla"</string>
+ <string name="open_app_as_a_bubble" msgid="6642626287247807473">"Vula i-app njengebhamuza"</string>
+ <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
+ <skip />
+ <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
+ <skip />
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{i-app eyengeziwe}one{ama-app engeziwe}other{ama-app engeziwe}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Ideskithophu"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"I-<xliff:g id="APP_NAME_1">%1$s</xliff:g> ne-<xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Ibhamuza"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Ukugcwala kakhulu"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> kusuka ku-<xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
@@ -155,4 +162,6 @@
<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>
+ <string name="pin_to_taskbar" msgid="6607778046321626950">"Phina kutaskbar"</string>
+ <string name="unpin_from_taskbar" msgid="2178811773165572676">"Susa ukuphina i-taskbar"</string>
</resources>
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index d699cdf..1ce28cf 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -47,6 +47,7 @@
<string name="wellbeing_provider_pkg" translatable="false"/>
<integer name="max_depth_blur_radius">23</integer>
+ <dimen name="max_depth_blur_radius_enhanced">34dp</dimen>
<!-- If predicted widgets from prediction service are less than this number, additional
eligible widgets may be added locally by launcher. When set to 0, no widgets will be added
@@ -56,6 +57,7 @@
<!-- Accessibility actions -->
<item type="id" name="action_move_to_top_or_left" />
<item type="id" name="action_move_to_bottom_or_right" />
+ <item type="id" name="action_create_application_bubble" />
<!-- The max scale for the wallpaper when it's zoomed in -->
<item name="config_wallpaperMaxScale" format="float" type="dimen">
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 52ebdae..c74390e 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -82,9 +82,9 @@
<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_padding_top_bottom">6dp</dimen>
+ <dimen name="task_thumbnail_header_padding_start_end">12dp</dimen>
+ <dimen name="task_thumbnail_header_margin_between_views">8dp</dimen>
<dimen name="task_thumbnail_header_icon_size">18dp</dimen>
<dimen name="task_thumbnail_header_round_corner_radius">16dp</dimen>
@@ -371,7 +371,6 @@
<dimen name="taskbar_running_app_indicator_height">2dp</dimen>
<dimen name="taskbar_running_app_indicator_width">12dp</dimen>
<dimen name="taskbar_running_app_indicator_top_margin">4dp</dimen>
- <dimen name="taskbar_minimized_app_indicator_width">6dp</dimen>
<!-- Transient taskbar -->
<dimen name="transient_taskbar_padding">12dp</dimen>
@@ -511,7 +510,8 @@
<dimen name="starting_surface_exit_animation_window_shift_length">20dp</dimen>
<!-- Keyboard Quick Switch -->
- <dimen name="keyboard_quick_switch_border_width">4dp</dimen>
+ <dimen name="keyboard_quick_switch_border_width">5dp</dimen>
+ <dimen name="keyboard_quick_switch_border_stroke">3dp</dimen>
<dimen name="keyboard_quick_switch_taskview_width">104dp</dimen>
<dimen name="keyboard_quick_switch_taskview_height">136dp</dimen>
<dimen name="keyboard_quick_switch_taskview_icon_size">52dp</dimen>
@@ -530,6 +530,11 @@
<dimen name="keyboard_quick_switch_text_button_radius">360dp</dimen>
<dimen name="keyboard_quick_switch_text_button_horizontal_padding">16dp</dimen>
<dimen name="keyboard_quick_switch_text_button_fade_edge_length">20dp</dimen>
+ <dimen name="keyboard_quick_switch_scroll_button_width">36dp</dimen>
+ <dimen name="keyboard_quick_switch_scroll_button_height">56dp</dimen>
+ <dimen name="keyboard_quick_switch_scroll_button_horizontal_padding">12dp</dimen>
+ <dimen name="keyboard_quick_switch_scroll_button_vertical_padding">32dp</dimen>
+ <dimen name="keyboard_quick_switch_scroll_button_corner_radius">18dp</dimen>
<!-- Digital Wellbeing -->
<dimen name="digital_wellbeing_toast_height">48dp</dimen>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 8e70a2b..f263f7e 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -322,6 +322,14 @@
<string name="move_drop_target_top_or_left">Move to top/left</string>
<!-- Label for moving drop target to the bottom or right side of the screen, depending on orientation (from the Taskbar only). -->
<string name="move_drop_target_bottom_or_right">Move to bottom/right</string>
+ <!-- Label for creating an application bubble (from the Taskbar only). -->
+ <string name="open_app_as_a_bubble">Open app as a bubble</string>
+
+ <!-- Accessibility pane title for quick switch view, which lists apps opened by the user, ordered by how recently the app was opened. -->
+ <string name="quick_switch_pane_title">Recent apps</string>
+
+ <!-- Content description for the quick switch view, which lists apps opened by the user, ordered by how recently the app was opened. -->
+ <string name="quick_switch_content_description">Recent app list</string>
<!-- Label for quick switch tile showing how many more apps are available. The number will be displayed above this text. [CHAR LIMIT=NONE] -->
<string name="quick_switch_overflow">{count, plural,
@@ -334,6 +342,13 @@
<!-- Accessibility label for quick switch tiles showing split tasks [CHAR LIMIT=NONE] -->
<string name="quick_switch_split_task"><xliff:g id="app_name_1" example="Chrome">%1$s</xliff:g> and <xliff:g id="app_name_2" example="Gmail">%2$s</xliff:g></string>
+ <!-- Accessibility label for quick switch tiles that include information about the tile's position in the parent list [CHAR LIMIT=NONE] -->
+ <string name="quick_switch_task_with_position_in_parent"><xliff:g id="task_description" example="Chrome">%1$s</xliff:g>, item <xliff:g id="index_in_parent" example="1">%2$d</xliff:g> of <xliff:g id="total_tasks" example="5">%3$d</xliff:g></string>
+
+ <!-- Accessibility label for an arrow button within quick switch UI that scrolls the quick switch content left -->
+ <string name="quick_switch_scroll_arrow_left">Scroll left</string>
+ <!-- Accessibility label for an arrow button within quick switch UI that scrolls the quick switch content right -->
+ <string name="quick_switch_scroll_arrow_right">Scroll right</string>
<!-- Strings for bubble bar -->
<!-- Fallback name for a bubble if it does have a title [CHAR_LIMIT=none] -->
@@ -366,4 +381,9 @@
<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>
+
+ <!-- Label for pinning an item to the taskbar. [CHAR_LIMIT=20] -->
+ <string name="pin_to_taskbar">Pin to taskbar</string>
+ <!-- Label for unpinning an item from the taskbar. [CHAR_LIMIT=20] -->
+ <string name="unpin_from_taskbar">Unpin from taskbar</string>
</resources>
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 84ae0fe..954272c 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -92,6 +92,7 @@
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
+import android.util.Log;
import android.util.Pair;
import android.util.Size;
import android.view.CrossWindowBlurListeners;
@@ -176,11 +177,13 @@
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Map.Entry;
/**
* Manages the opening and closing app transitions from Launcher
*/
public class QuickstepTransitionManager implements OnDeviceProfileChangeListener {
+ private static final String TAG = "QuickstepTransitionManager";
private static final boolean ENABLE_SHELL_STARTING_SURFACE =
SystemProperties.getBoolean("persist.debug.shell_starting_surface", true);
@@ -351,7 +354,7 @@
new RemoteAnimationAdapter(runner, duration, statusBarTransitionDelay),
new RemoteTransition(runner.toRemoteTransition(),
mLauncher.getIApplicationThread(), "QuickstepLaunch"));
- IRemoteCallback endCallback = completeRunnableListCallback(onEndCallback);
+ IRemoteCallback endCallback = completeRunnableListCallback(onEndCallback, mLauncher);
options.setOnAnimationAbortListener(endCallback);
options.setOnAnimationFinishedListener(endCallback);
options.setLaunchCookie(StableViewInfo.toLaunchCookie(itemInfo));
@@ -1206,7 +1209,7 @@
mLauncher.removeOnDeviceProfileChangeListener(this);
SystemUiProxy.INSTANCE.get(mLauncher).setStartingWindowListener(null);
if (BuildConfig.IS_STUDIO_BUILD && !mRegisteredTaskStackChangeListener.isEmpty()) {
- throw new IllegalStateException("Failed to run onEndCallback created from"
+ Log.e(TAG, "IllegalState: Failed to run onEndCallback created from"
+ " getActivityLaunchOptions()");
}
mRegisteredTaskStackChangeListener.forEach(TaskRestartedDuringLaunchListener::unregister);
@@ -1342,9 +1345,9 @@
? Collections.EMPTY_LIST
: runningTaskTarget.taskInfo.launchCookies;
- return mLauncher.getFirstMatchForAppClose(
+ return mLauncher.getFirstVisibleElementForAppClose(
StableViewInfo.fromLaunchCookies(launchCookies), packageName,
- UserHandle.of(runningTaskTarget.taskInfo.userId), true /* supportsAllAppsState */);
+ UserHandle.of(runningTaskTarget.taskInfo.userId));
}
private @NonNull RectF getDefaultWindowTargetRect() {
diff --git a/quickstep/src/com/android/launcher3/WidgetPickerActivity.java b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
index f992913..cdae9ca 100644
--- a/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
+++ b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
@@ -25,7 +25,6 @@
import static java.lang.Math.max;
import static java.lang.Math.min;
-import static java.util.Collections.emptyList;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
@@ -48,7 +47,6 @@
import com.android.launcher3.model.StringCache;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.model.WidgetPredictionsRequester;
-import com.android.launcher3.model.WidgetsFilterDataProvider;
import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.PackageItemInfo;
@@ -70,7 +68,8 @@
import java.util.regex.Pattern;
/** An Activity that can host Launcher's widget picker. */
-public class WidgetPickerActivity extends BaseActivity {
+public class WidgetPickerActivity extends BaseActivity implements
+ WidgetPredictionsRequester.WidgetPredictionsListener {
private static final String TAG = "WidgetPickerActivity";
/**
* Name of the extra that indicates that a widget being dragged.
@@ -121,9 +120,7 @@
private LauncherAppState mApp;
private StringCache mStringCache;
private WidgetPredictionsRequester mWidgetPredictionsRequester;
- private final WidgetPickerDataProvider mWidgetPickerDataProvider =
- new WidgetPickerDataProvider();
- private WidgetsFilterDataProvider mWidgetsFilterDataProvider;
+ private WidgetPickerDataProvider mWidgetPickerDataProvider;
private int mDesiredWidgetWidth;
private int mDesiredWidgetHeight;
@@ -169,8 +166,8 @@
mApp = LauncherAppState.getInstance(this);
InvariantDeviceProfile idp = mApp.getInvariantDeviceProfile();
mDeviceProfile = idp.getDeviceProfile(this);
- mModel = new WidgetsModel();
- mWidgetsFilterDataProvider = WidgetsFilterDataProvider.Companion.newInstance(this);
+ mModel = new WidgetsModel(mApp.getContext());
+ mWidgetPickerDataProvider = new WidgetPickerDataProvider(this);
setContentView(R.layout.widget_picker_activity);
mDragLayer = findViewById(R.id.drag_layer);
@@ -312,25 +309,21 @@
*/
private void refreshAndBindWidgets() {
MODEL_EXECUTOR.execute(() -> {
- LauncherAppState app = LauncherAppState.getInstance(this);
- // Don't have to setup filters - its setup when launcher loads
- // Just refresh filters with available cached info.
- mModel.updateWidgetFilters(mWidgetsFilterDataProvider);
- mModel.update(app, null);
+ mModel.update(null);
StringCache stringCache = new StringCache();
stringCache.loadStrings(this);
bindStringCache(stringCache);
- bindWidgets(mModel.getWidgetsByPackageItemForPicker(),
- mModel.getDefaultWidgetsFilter());
+ bindWidgets(mModel.getWidgetsByPackageItemForPicker());
// Open sheet once widgets are available, so that it doesn't interrupt the open
// animation.
openWidgetsSheet();
if (mUiSurface != null) {
- mWidgetPredictionsRequester = new WidgetPredictionsRequester(app.getContext(),
- mUiSurface, mModel.getWidgetsByComponentKeyForPicker());
- mWidgetPredictionsRequester.request(mAddedWidgets, this::bindRecommendedWidgets);
+ mWidgetPredictionsRequester = new WidgetPredictionsRequester(
+ getApplicationContext(), mUiSurface,
+ mModel.getWidgetsByComponentKeyForPicker());
+ mWidgetPredictionsRequester.request(mAddedWidgets, this);
}
});
}
@@ -339,26 +332,19 @@
MAIN_EXECUTOR.execute(() -> mStringCache = stringCache);
}
- private void bindWidgets(Map<PackageItemInfo, List<WidgetItem>> widgets,
- @Nullable Predicate<WidgetItem> defaultWidgetsFilter) {
+ private void bindWidgets(Map<PackageItemInfo, List<WidgetItem>> widgets) {
WidgetsListBaseEntriesBuilder builder = new WidgetsListBaseEntriesBuilder(
mApp.getContext());
-
final List<WidgetsListBaseEntry> allWidgets = builder.build(widgets, mNoShortcutsFilter);
- // Default list is shown if either defaultWidgetsFilter exists or host has additionally
- // enforced size filtering.
+ // Default list is shown if host has additionally enforced size filtering.
@Nullable Predicate<WidgetItem> defaultListFilter =
hasHostSizeFilters() ? mHostSizeAndNoShortcutsFilter : null;
- if (defaultWidgetsFilter != null) {
- defaultListFilter = defaultListFilter != null ? defaultListFilter.and(
- defaultWidgetsFilter) : defaultWidgetsFilter;
- }
- final List<WidgetsListBaseEntry> defaultWidgets = defaultListFilter != null ? builder.build(
- widgets, defaultListFilter) : emptyList();
- MAIN_EXECUTOR.execute(
- () -> mWidgetPickerDataProvider.setWidgets(allWidgets, defaultWidgets));
+ MAIN_EXECUTOR.execute(() -> {
+ mWidgetPickerDataProvider.setHostSpecifiedDefaultWidgetsFilter(defaultListFilter);
+ mWidgetPickerDataProvider.setWidgets(allWidgets);
+ });
}
private void openWidgetsSheet() {
@@ -370,7 +356,8 @@
});
}
- private void bindRecommendedWidgets(List<ItemInfo> recommendedWidgets) {
+ @Override
+ public void onPredictionsAvailable(List<ItemInfo> recommendedWidgets) {
// Bind recommendations once picker has finished open animation.
MAIN_EXECUTOR.getHandler().postDelayed(
() -> mWidgetPickerDataProvider.setWidgetRecommendations(recommendedWidgets),
@@ -380,7 +367,7 @@
@Override
protected void onDestroy() {
super.onDestroy();
- MODEL_EXECUTOR.execute(() -> mWidgetsFilterDataProvider.destroy());
+ mWidgetPickerDataProvider.destroy();
if (mWidgetPredictionsRequester != null) {
mWidgetPredictionsRequester.clear();
}
diff --git a/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchAnimatorHelper.kt b/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchAnimatorHelper.kt
index 688018b..a9e5145 100644
--- a/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchAnimatorHelper.kt
+++ b/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchAnimatorHelper.kt
@@ -64,18 +64,13 @@
fun createAnimators(info: TransitionInfo, finishCallback: (Animator) -> Unit): List<Animator> {
val launchChange = getLaunchChange(info)
- requireNotNull(launchChange) {
- val changesString =
+ if (launchChange == null) {
+ val tasksInfo =
info.changes.joinToString(", ") { change ->
- "Change: mode=${change.mode}, " +
- "taskId=${change.taskInfo?.id}, " +
- "isFreeform=${change.taskInfo?.isFreeform}"
+ "${change.taskInfo?.taskId}:${change.taskInfo?.isFreeform}"
}
- Log.e(
- TAG,
- "No launch change found: Transition type=${info.type}, changes=$changesString",
- )
- "expected an app launch Change"
+ Log.e(TAG, "No launch change found: Transition info=$info, tasks state=$tasksInfo")
+ return emptyList()
}
val transaction = transactionSupplier.get()
@@ -98,22 +93,26 @@
}
if (trampolineCloseChange != null) {
val trampolineCloseAnimator =
- createTrampolineCloseAnimator(trampolineCloseChange, transaction)
+ createTrampolineCloseAnimator(trampolineCloseChange, transaction, finishCallback)
animatorsList.add(trampolineCloseAnimator)
}
return animatorsList
}
private fun getLaunchChange(info: TransitionInfo): Change? =
- info.changes.firstOrNull { change -> change.mode in LAUNCH_CHANGE_MODES }
+ info.changes.firstOrNull { change ->
+ change.mode in LAUNCH_CHANGE_MODES && change.taskInfo?.isFreeform == true
+ }
private fun getMinimizeChange(info: TransitionInfo): Change? =
- info.changes.firstOrNull { change -> change.mode == TRANSIT_TO_BACK }
+ info.changes.firstOrNull { change ->
+ change.mode == TRANSIT_TO_BACK && change.taskInfo?.isFreeform == true
+ }
private fun getTrampolineCloseChange(info: TransitionInfo): Change? {
if (
info.changes.size < 2 ||
- !DesktopModeFlags.ENABLE_DESKTOP_TRAMPOLINE_CLOSE_ANIMATION_BUGFIX.isTrue
+ !DesktopModeFlags.ENABLE_DESKTOP_TRAMPOLINE_CLOSE_ANIMATION_BUGFIX.isTrue
) {
return null
}
@@ -195,13 +194,22 @@
)
}
- private fun createTrampolineCloseAnimator(change: Change, transaction: Transaction): Animator {
+ private fun createTrampolineCloseAnimator(
+ change: Change,
+ transaction: Transaction,
+ onAnimFinish: (Animator) -> Unit,
+ ): Animator {
return ValueAnimator.ofFloat(1f, 0f).apply {
duration = 100L
interpolator = Interpolators.LINEAR
addUpdateListener { animation ->
transaction.setAlpha(change.leash, animation.animatedValue as Float).apply()
}
+ addListener(
+ onEnd = { animation ->
+ onAnimFinish(animation)
+ }
+ )
}
}
diff --git a/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt b/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt
index 79072a6..5a8934b 100644
--- a/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt
+++ b/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt
@@ -84,6 +84,10 @@
if (animators.isEmpty()) finishedCallback.run()
}
animators += animatorHelper.createAnimators(info, animatorFinishedCallback)
+ if (animators.isEmpty()) {
+ finishedCallback.run()
+ return
+ }
animators.forEach { it.start() }
}
diff --git a/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt b/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt
index 40cfe92..a01846d 100644
--- a/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt
+++ b/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt
@@ -30,6 +30,7 @@
import com.android.launcher3.util.Executors.MAIN_EXECUTOR
import com.android.quickstep.SystemUiProxy
import com.android.quickstep.TaskViewUtils
+import com.android.quickstep.util.DesksUtils.Companion.areMultiDesksFlagsEnabled
import com.android.quickstep.views.DesktopTaskView
import com.android.quickstep.views.TaskContainer
import com.android.quickstep.views.TaskView
@@ -60,7 +61,11 @@
callback,
)
val transition = RemoteTransition(animRunner, appThread, "RecentsToDesktop")
- systemUiProxy.showDesktopApps(desktopTaskView.displayId, transition)
+ if (areMultiDesksFlagsEnabled()) {
+ systemUiProxy.activateDesk(desktopTaskView.deskId, transition)
+ } else {
+ systemUiProxy.showDesktopApps(desktopTaskView.displayId, transition)
+ }
}
/** Launch desktop tasks from recents view */
diff --git a/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java b/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java
index fd71151..3544844 100644
--- a/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java
+++ b/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java
@@ -32,9 +32,9 @@
import androidx.annotation.NonNull;
import com.android.launcher3.ConstantItem;
-import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel.ModelUpdateTask;
import com.android.launcher3.LauncherPrefs;
+import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
import com.android.launcher3.model.QuickstepModelDelegate.PredictorState;
import com.android.launcher3.model.data.AppInfo;
@@ -65,8 +65,8 @@
@Override
public void execute(@NonNull ModelTaskController taskController, @NonNull BgDataModel dataModel,
@NonNull AllAppsList apps) {
- LauncherAppState app = taskController.getApp();
- Context context = app.getContext();
+ IconCache iconCache = taskController.getIconCache();
+ Context context = taskController.getContext();
// TODO: remove this
LauncherPrefs.get(context).put(LAST_PREDICTION_ENABLED, !mTargets.isEmpty());
@@ -84,7 +84,7 @@
if (si != null) {
usersForChangedShortcuts.add(si.getUserHandle());
itemInfo = new WorkspaceItemInfo(si, context);
- app.getIconCache().getShortcutIcon(itemInfo, si);
+ iconCache.getShortcutIcon(itemInfo, si);
} else {
String className = target.getClassName();
if (COMPONENT_CLASS_MARKER.equals(className)) {
@@ -96,7 +96,7 @@
itemInfo = apps.data.stream()
.filter(info -> user.equals(info.user) && cn.equals(info.componentName))
.map(ai -> {
- app.getIconCache().getTitleAndIcon(ai, mPredictorState.lookupFlag);
+ iconCache.getTitleAndIcon(ai, mPredictorState.lookupFlag);
return ai.makeWorkspaceItem(context);
})
.findAny()
@@ -107,7 +107,7 @@
return null;
}
AppInfo ai = new AppInfo(context, lai, user);
- app.getIconCache().getTitleAndIcon(ai, lai, DEFAULT_LOOKUP_FLAG);
+ iconCache.getTitleAndIcon(ai, lai, DEFAULT_LOOKUP_FLAG);
return ai.makeWorkspaceItem(context);
});
@@ -123,8 +123,7 @@
FixedContainerItems fci = new FixedContainerItems(mPredictorState.containerId, items);
dataModel.extraItems.put(fci.containerId, fci);
taskController.bindExtraContainerItems(fci);
- usersForChangedShortcuts.forEach(
- u -> dataModel.updateShortcutPinnedState(app.getContext(), u));
+ usersForChangedShortcuts.forEach(u -> dataModel.updateShortcutPinnedState(context, u));
// Save to disk
mPredictorState.storage.write(context, fci.items);
diff --git a/quickstep/src/com/android/launcher3/model/WidgetPredictionsRequester.java b/quickstep/src/com/android/launcher3/model/WidgetPredictionsRequester.java
index d3ac975..ada7301 100644
--- a/quickstep/src/com/android/launcher3/model/WidgetPredictionsRequester.java
+++ b/quickstep/src/com/android/launcher3/model/WidgetPredictionsRequester.java
@@ -16,7 +16,6 @@
package com.android.launcher3.model;
-import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
@@ -48,7 +47,6 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -56,7 +54,7 @@
* Works with app predictor to fetch and process widget predictions displayed in a standalone
* widget picker activity for a UI surface.
*/
-public class WidgetPredictionsRequester {
+public class WidgetPredictionsRequester implements AppPredictor.Callback {
private static final int NUM_OF_RECOMMENDED_WIDGETS_PREDICATION = 20;
private static final String BUNDLE_KEY_ADDED_APP_WIDGETS = "added_app_widgets";
// container/screenid/[positionx,positiony]/[spanx,spany]
@@ -71,6 +69,9 @@
@NonNull
private final String mUiSurface;
private boolean mPredictionsAvailable;
+ @Nullable
+ private WidgetPredictionsListener mPredictionsListener = null;
+ @Nullable Predicate<WidgetItem> mFilter = null;
@NonNull
private final Map<ComponentKey, WidgetItem> mAllWidgets;
@@ -81,36 +82,49 @@
mAllWidgets = Collections.unmodifiableMap(allWidgets);
}
+ // AppPredictor.Callback -> onTargetsAvailable
+ @Override
+ @WorkerThread
+ public void onTargetsAvailable(List<AppTarget> targets) {
+ List<WidgetItem> filteredPredictions = filterPredictions(targets, mAllWidgets, mFilter);
+ List<ItemInfo> mappedPredictions = mapWidgetItemsToItemInfo(filteredPredictions);
+
+ if (!mPredictionsAvailable && mPredictionsListener != null) {
+ mPredictionsAvailable = true;
+ MAIN_EXECUTOR.execute(
+ () -> mPredictionsListener.onPredictionsAvailable(mappedPredictions));
+ }
+ }
+
/**
* Requests one time predictions from the app predictions manager and invokes provided callback
- * once predictions are available.
+ * once predictions are available. Any previous requests may be cancelled.
*
* @param existingWidgets widgets that are currently added to the surface;
- * @param callback consumer of prediction results to be called when predictions are
- * available
+ * @param listener consumer of prediction results to be called when predictions are
+ * available; any previous listener will no longer receive updates.
*/
+ @WorkerThread // e.g. MODEL_EXECUTOR
public void request(List<AppWidgetProviderInfo> existingWidgets,
- Consumer<List<ItemInfo>> callback) {
+ WidgetPredictionsListener listener) {
+ clear();
+ mPredictionsListener = listener;
+ mFilter = notOnUiSurfaceFilter(existingWidgets);
+
+ AppPredictionManager apm = mContext.getSystemService(AppPredictionManager.class);
+ if (apm == null) {
+ return;
+ }
+
Bundle bundle = buildBundleForPredictionSession(existingWidgets);
- Predicate<WidgetItem> filter = notOnUiSurfaceFilter(existingWidgets);
-
- MODEL_EXECUTOR.execute(() -> {
- clear();
- AppPredictionManager apm = mContext.getSystemService(AppPredictionManager.class);
- if (apm == null) {
- return;
- }
-
- mAppPredictor = apm.createAppPredictionSession(
- new AppPredictionContext.Builder(mContext)
- .setUiSurface(mUiSurface)
- .setExtras(bundle)
- .setPredictedTargetCount(NUM_OF_RECOMMENDED_WIDGETS_PREDICATION)
- .build());
- mAppPredictor.registerPredictionUpdates(MODEL_EXECUTOR,
- targets -> bindPredictions(targets, filter, callback));
- mAppPredictor.requestPredictionUpdate();
- });
+ mAppPredictor = apm.createAppPredictionSession(
+ new AppPredictionContext.Builder(mContext)
+ .setUiSurface(mUiSurface)
+ .setExtras(bundle)
+ .setPredictedTargetCount(NUM_OF_RECOMMENDED_WIDGETS_PREDICATION)
+ .build());
+ mAppPredictor.registerPredictionUpdates(MODEL_EXECUTOR, /*callback=*/ this);
+ mAppPredictor.requestPredictionUpdate();
}
/**
@@ -158,27 +172,14 @@
return widgetItem -> !existingComponentKeys.contains(widgetItem);
}
- /** Provides the predictions returned by the predictor to the registered callback. */
- @WorkerThread
- private void bindPredictions(List<AppTarget> targets, Predicate<WidgetItem> filter,
- Consumer<List<ItemInfo>> callback) {
- if (!mPredictionsAvailable) {
- mPredictionsAvailable = true;
- List<WidgetItem> filteredPredictions = filterPredictions(targets, mAllWidgets, filter);
- List<ItemInfo> mappedPredictions = mapWidgetItemsToItemInfo(filteredPredictions);
-
- MAIN_EXECUTOR.execute(() -> callback.accept(mappedPredictions));
- MODEL_EXECUTOR.execute(this::clear);
- }
- }
-
/**
* Applies the provided filter (e.g. widgets not on workspace) on the predictions returned by
* the predictor.
*/
@VisibleForTesting
static List<WidgetItem> filterPredictions(List<AppTarget> predictions,
- Map<ComponentKey, WidgetItem> allWidgets, Predicate<WidgetItem> filter) {
+ @NonNull Map<ComponentKey, WidgetItem> allWidgets,
+ @Nullable Predicate<WidgetItem> filter) {
List<WidgetItem> servicePredictedItems = new ArrayList<>();
for (AppTarget prediction : predictions) {
@@ -187,7 +188,7 @@
WidgetItem widgetItem = allWidgets.get(
new ComponentKey(new ComponentName(prediction.getPackageName(), className),
prediction.getUser()));
- if (widgetItem != null && filter.test(widgetItem)) {
+ if (widgetItem != null && (filter == null || filter.test(widgetItem))) {
servicePredictedItems.add(widgetItem);
}
}
@@ -200,27 +201,34 @@
* Converts the list of {@link WidgetItem}s to the list of {@link ItemInfo}s.
*/
private List<ItemInfo> mapWidgetItemsToItemInfo(List<WidgetItem> widgetItems) {
- List<ItemInfo> items;
- if (enableCategorizedWidgetSuggestions()) {
- WidgetRecommendationCategoryProvider categoryProvider =
- new WidgetRecommendationCategoryProvider();
- items = widgetItems.stream()
- .map(it -> new PendingAddWidgetInfo(it.widgetInfo, CONTAINER_WIDGETS_PREDICTION,
- categoryProvider.getWidgetRecommendationCategory(mContext, it)))
- .collect(Collectors.toList());
- } else {
- items = widgetItems.stream().map(it -> new PendingAddWidgetInfo(it.widgetInfo,
- CONTAINER_WIDGETS_PREDICTION)).collect(Collectors.toList());
- }
- return items;
+ WidgetRecommendationCategoryProvider categoryProvider =
+ new WidgetRecommendationCategoryProvider();
+ return widgetItems.stream()
+ .map(it -> new PendingAddWidgetInfo(it.widgetInfo, CONTAINER_WIDGETS_PREDICTION,
+ categoryProvider.getWidgetRecommendationCategory(mContext, it)))
+ .collect(Collectors.toList());
}
/** Cleans up any open prediction sessions. */
public void clear() {
if (mAppPredictor != null) {
+ mAppPredictor.unregisterPredictionUpdates(this);
mAppPredictor.destroy();
mAppPredictor = null;
}
+ mPredictionsListener = null;
mPredictionsAvailable = false;
+ mFilter = null;
+ }
+
+ /**
+ * Listener class to listen to updates from the {@link WidgetPredictionsRequester}
+ */
+ public interface WidgetPredictionsListener {
+ /**
+ * Callback method that is called when the predicted widgets are available.
+ * @param predictions list of predicted widgets {@link PendingAddWidgetInfo}
+ */
+ void onPredictionsAvailable(List<ItemInfo> predictions);
}
}
diff --git a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
index 8bd2ba8..da55b40 100644
--- a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
+++ b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
@@ -15,7 +15,6 @@
*/
package com.android.launcher3.model;
-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;
@@ -45,7 +44,6 @@
import java.util.Map;
import java.util.Random;
import java.util.Set;
-import java.util.function.Predicate;
import java.util.stream.Collectors;
/** Task to update model as a result of predicted widgets update */
@@ -68,8 +66,6 @@
@Override
public void execute(@NonNull ModelTaskController taskController, @NonNull BgDataModel dataModel,
@NonNull AllAppsList apps) {
- Predicate<WidgetItem> predictedWidgetsFilter = enableTieredWidgetsByDefaultInPicker()
- ? dataModel.widgetsModel.getPredictedWidgetsFilter() : null;
Set<ComponentKey> widgetsInWorkspace = dataModel.itemsIdMap
.stream()
.filter(WIDGET_FILTER)
@@ -84,11 +80,9 @@
.stream()
.filter(entry -> entry.getValue().widgetInfo != null
&& !widgetsInWorkspace.contains(entry.getValue())
- && (predictedWidgetsFilter == null
- || predictedWidgetsFilter.test(entry.getValue()))
).collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
- Context context = taskController.getApp().getContext();
+ Context context = taskController.getContext();
List<WidgetItem> servicePredictedItems = new ArrayList<>();
List<String> addedWidgetApps = new ArrayList<>();
@@ -134,20 +128,12 @@
}
}
- List<ItemInfo> items;
- if (enableCategorizedWidgetSuggestions()) {
- WidgetRecommendationCategoryProvider categoryProvider =
- new WidgetRecommendationCategoryProvider();
- items = servicePredictedItems.stream()
- .map(it -> new PendingAddWidgetInfo(it.widgetInfo, CONTAINER_WIDGETS_PREDICTION,
- categoryProvider.getWidgetRecommendationCategory(context, it)))
- .collect(Collectors.toList());
- } else {
- items = servicePredictedItems.stream()
- .map(it -> new PendingAddWidgetInfo(it.widgetInfo,
- CONTAINER_WIDGETS_PREDICTION)).collect(
- Collectors.toList());
- }
+ WidgetRecommendationCategoryProvider categoryProvider =
+ new WidgetRecommendationCategoryProvider();
+ List<ItemInfo> items = servicePredictedItems.stream()
+ .map(it -> new PendingAddWidgetInfo(it.widgetInfo, CONTAINER_WIDGETS_PREDICTION,
+ categoryProvider.getWidgetRecommendationCategory(context, it)))
+ .collect(Collectors.toList());
FixedContainerItems fixedContainerItems =
new FixedContainerItems(mPredictorState.containerId, items);
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
index 4c24d95..360210b 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
@@ -65,7 +65,7 @@
private void onLauncherDraw() {
View view = mLauncher.getDragLayer();
ViewRootImpl viewRootImpl = view.getViewRootImpl();
- setSurface(viewRootImpl != null ? viewRootImpl.getSurfaceControl() : null);
+ setBaseSurface(viewRootImpl != null ? viewRootImpl.getSurfaceControl() : null);
view.post(() -> view.getViewTreeObserver().removeOnDrawListener(mOnDrawListener));
}
@@ -127,7 +127,7 @@
mLauncher.getDragLayer().getViewTreeObserver().addOnDrawListener(mOnDrawListener);
} else {
mLauncher.getDragLayer().getViewTreeObserver().removeOnDrawListener(mOnDrawListener);
- setSurface(null);
+ setBaseSurface(null);
}
}
@@ -189,7 +189,8 @@
writer.println(prefix + "DepthController");
writer.println(prefix + "\tmMaxBlurRadius=" + mMaxBlurRadius);
writer.println(prefix + "\tmCrossWindowBlursEnabled=" + mCrossWindowBlursEnabled);
- writer.println(prefix + "\tmSurface=" + mSurface);
+ writer.println(prefix + "\tmBaseSurface=" + mBaseSurface);
+ writer.println(prefix + "\tmBaseSurfaceOverride=" + mBaseSurfaceOverride);
writer.println(prefix + "\tmStateDepth=" + stateDepth.getValue());
writer.println(prefix + "\tmWidgetDepth=" + widgetDepth.getValue());
writer.println(prefix + "\tmCurrentBlur=" + mCurrentBlur);
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt
index eb24df1..810fa6f 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt
+++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt
@@ -401,6 +401,39 @@
DisplayController.INSTANCE.get(context).notifyConfigChange()
}
+ private fun notifyOnDeskAdded(displayId: Int, deskId: Int) {
+ if (DEBUG) {
+ Log.d(TAG, "notifyOnDeskAdded: displayId=$displayId, deskId=$deskId")
+ }
+
+ for (listener in desktopVisibilityListeners) {
+ listener.onDeskAdded(displayId, deskId)
+ }
+ }
+
+ private fun notifyOnDeskRemoved(displayId: Int, deskId: Int) {
+ if (DEBUG) {
+ Log.d(TAG, "notifyOnDeskRemoved: displayId=$displayId, deskId=$deskId")
+ }
+
+ for (listener in desktopVisibilityListeners) {
+ listener.onDeskRemoved(displayId, deskId)
+ }
+ }
+
+ private fun notifyOnActiveDeskChanged(displayId: Int, newActiveDesk: Int, oldActiveDesk: Int) {
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "notifyOnActiveDeskChanged: displayId=$displayId, newActiveDesk=$newActiveDesk, oldActiveDesk=$oldActiveDesk",
+ )
+ }
+
+ for (listener in desktopVisibilityListeners) {
+ listener.onActiveDeskChanged(displayId, newActiveDesk, oldActiveDesk)
+ }
+ }
+
/** TODO: b/333533253 - Remove after flag rollout */
private fun setBackgroundStateEnabled(backgroundStateEnabled: Boolean) {
if (DEBUG) {
@@ -511,6 +544,8 @@
"Found a duplicate desk Id: $deskId on display: $displayId"
}
}
+
+ notifyOnDeskAdded(displayId, deskId)
}
private fun onDeskRemoved(displayId: Int, deskId: Int) {
@@ -526,6 +561,8 @@
it.activeDeskId = INACTIVE_DESK_ID
}
}
+
+ notifyOnDeskRemoved(displayId, deskId)
}
private fun onActiveDeskChanged(displayId: Int, newActiveDesk: Int, oldActiveDesk: Int) {
@@ -539,12 +576,16 @@
check(oldActiveDesk == it.activeDeskId) {
"Mismatch between the Shell's oldActiveDesk: $oldActiveDesk, and Launcher's: ${it.activeDeskId}"
}
- check(it.deskIds.contains(newActiveDesk)) {
+ check(newActiveDesk == INACTIVE_DESK_ID || it.deskIds.contains(newActiveDesk)) {
"newActiveDesk: $newActiveDesk was never added to display: $displayId"
}
it.activeDeskId = newActiveDesk
}
+ if (newActiveDesk != oldActiveDesk) {
+ notifyOnActiveDeskChanged(displayId, newActiveDesk, oldActiveDesk)
+ }
+
if (wasInDesktopMode != isInDesktopModeAndNotInOverview(displayId)) {
notifyIsInDesktopModeChanged(displayId, !wasInDesktopMode)
}
@@ -650,8 +691,11 @@
"duration= " +
transitionDuration),
)
- controller.get()?.isInDesktopMode = true
- controller.get()?.notifyTaskbarDesktopModeListenersForEntry(transitionDuration)
+ val controller = controller.get()
+ if (controller != null && !controller.isInDesktopMode) {
+ controller.isInDesktopMode = true
+ controller.notifyTaskbarDesktopModeListenersForEntry(transitionDuration)
+ }
}
}
@@ -663,8 +707,11 @@
"duration= " +
transitionDuration),
)
- controller.get()?.isInDesktopMode = false
- controller.get()?.notifyTaskbarDesktopModeListenersForExit(transitionDuration)
+ val controller = controller.get()
+ if (controller != null && controller.isInDesktopMode) {
+ controller.isInDesktopMode = false
+ controller.notifyTaskbarDesktopModeListenersForExit(transitionDuration)
+ }
}
}
@@ -718,6 +765,6 @@
private const val TAG = "DesktopVisController"
private const val DEBUG = false
- public const val INACTIVE_DESK_ID = -1
+ const val INACTIVE_DESK_ID = -1
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java b/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java
index b33fd38..aa3feb7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java
@@ -19,29 +19,34 @@
import android.content.Intent;
import android.content.pm.ShortcutInfo;
import android.os.UserHandle;
-import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
-import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.popup.SystemShortcut;
+import com.android.launcher3.util.BaseContext;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.Themes;
-import com.android.launcher3.views.ActivityContext;
import com.android.quickstep.SystemUiProxy;
-import java.util.ArrayList;
-import java.util.List;
-
// TODO(b/218912746): Share more behavior to avoid all apps context depending directly on taskbar.
/** Base for common behavior between taskbar window contexts. */
-public abstract class BaseTaskbarContext extends ContextThemeWrapper implements ActivityContext,
- SystemShortcut.BubbleActivityStarter {
+public abstract class BaseTaskbarContext extends BaseContext
+ implements SystemShortcut.BubbleActivityStarter {
protected final LayoutInflater mLayoutInflater;
- private final List<OnDeviceProfileChangeListener> mDPChangeListeners = new ArrayList<>();
+ private final boolean mIsPrimaryDisplay;
- public BaseTaskbarContext(Context windowContext) {
+ public BaseTaskbarContext(Context windowContext, boolean isPrimaryDisplay) {
super(windowContext, Themes.getActivityThemeRes(windowContext));
mLayoutInflater = LayoutInflater.from(this).cloneInContext(this);
+ mIsPrimaryDisplay = isPrimaryDisplay;
+ }
+
+ public boolean isTransientTaskbar() {
+ return DisplayController.isTransientTaskbar(this) && mIsPrimaryDisplay;
+ }
+
+ public boolean isPrimaryDisplay() {
+ return mIsPrimaryDisplay;
}
@Override
@@ -50,11 +55,6 @@
}
@Override
- public final List<OnDeviceProfileChangeListener> getOnDeviceProfileChangeListeners() {
- return mDPChangeListeners;
- }
-
- @Override
public void showShortcutBubble(ShortcutInfo info) {
if (info == null) return;
SystemUiProxy.INSTANCE.get(this).showShortcutBubble(info);
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
index 8555376..1698050 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
@@ -165,12 +165,12 @@
processLoadedTasks(tasks, taskIdsToExclude);
mQuickSwitchViewController.updateQuickSwitchView(
mTasks,
- mNumHiddenTasks,
+ wasOpenedFromTaskbar ? 0 : mNumHiddenTasks,
currentFocusIndexOverride,
mHasDesktopTask,
mWasDesktopTaskFilteredOut);
}, shouldShowDesktopTasks ? RecentsFilterState.EMPTY_FILTER
- : RecentsFilterState.getEmptyDesktopTaskFilter());
+ : RecentsFilterState.getDesktopTaskFilter());
}
mQuickSwitchViewController.updateLayoutForSurface(wasOpenedFromTaskbar,
@@ -205,7 +205,7 @@
// running. If not, focus that first task.
mQuickSwitchViewController.openQuickSwitchView(
mTasks,
- mNumHiddenTasks,
+ wasOpenedFromTaskbar ? 0 : mNumHiddenTasks,
/* updateTasks= */ false,
currentFocusedIndex == -1 && !mControllerCallbacks.isFirstTaskRunning()
? 0 : currentFocusedIndex,
@@ -223,7 +223,7 @@
// the correct index.
mQuickSwitchViewController.openQuickSwitchView(
mTasks,
- mNumHiddenTasks,
+ wasOpenedFromTaskbar ? 0 : mNumHiddenTasks,
/* updateTasks= */ true,
currentFocusedIndex == -1 && !mControllerCallbacks.isFirstTaskRunning()
? 0 : currentFocusedIndex,
@@ -232,7 +232,7 @@
mWasDesktopTaskFilteredOut,
wasOpenedFromTaskbar);
}, shouldShowDesktopTasks ? RecentsFilterState.EMPTY_FILTER
- : RecentsFilterState.getEmptyDesktopTaskFilter());
+ : RecentsFilterState.getDesktopTaskFilter());
}
private boolean shouldExcludeTask(GroupTask task, Set<Integer> taskIdsToExclude) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
index f80dc90..c49029d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
@@ -40,6 +40,8 @@
import com.android.quickstep.util.BorderAnimator;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.wm.shell.shared.TypefaceUtils;
+import com.android.wm.shell.shared.TypefaceUtils.FontFamily;
import kotlin.Unit;
@@ -64,6 +66,11 @@
@Nullable private ImageView mIcon2;
@Nullable private View mContent;
+ // Describe the task position in the parent container. Used to add information about the task's
+ // position in a task list to the task view's content description.
+ private int mIndexInParent = -1;
+ private int mTotalTasksInParent = -1;
+
public KeyboardQuickSwitchTaskView(@NonNull Context context) {
this(context, null);
}
@@ -108,11 +115,11 @@
TypefaceUtils.setTypeface(
mContent.findViewById(R.id.large_text),
- TypefaceUtils.FONT_FAMILY_HEADLINE_LARGE_EMPHASIZED
+ FontFamily.GSF_HEADLINE_LARGE_EMPHASIZED
);
TypefaceUtils.setTypeface(
mContent.findViewById(R.id.small_text),
- TypefaceUtils.FONT_FAMILY_LABEL_LARGE_BASELINE
+ FontFamily.GSF_LABEL_LARGE
);
Resources resources = mContext.getResources();
@@ -122,7 +129,9 @@
: resources.getDimensionPixelSize(
R.dimen.keyboard_quick_switch_task_view_radius),
/* borderWidthPx= */ resources.getDimensionPixelSize(
- R.dimen.keyboard_quick_switch_border_width),
+ R.dimen.keyboard_quick_switch_border_width),
+ /* borderStrokePx= */ resources.getDimensionPixelSize(
+ R.dimen.keyboard_quick_switch_border_stroke),
/* boundsBuilder= */ bounds -> {
bounds.set(0, 0, getWidth(), getHeight());
return Unit.INSTANCE;
@@ -153,36 +162,51 @@
applyThumbnail(mThumbnailView1, task1, thumbnailUpdateFunction);
applyThumbnail(mThumbnailView2, task2, thumbnailUpdateFunction);
+ // Update content description, even in cases task icons, and content descriptions need to be
+ // loaded asynchronously to ensure that the task has non empty description (assuming task
+ // position information was set), as KeyboardQuickSwitch view may request accessibility
+ // focus to be moved to the task when the quick switch UI gets shown. The description will
+ // be updated once the task metadata has been loaded - the delay should be very short, and
+ // the content description when task titles are not available still gives some useful
+ // information to the user (the task's position in the list).
+ updateContentDesctiptionForTasks(task1, task2);
+
if (iconUpdateFunction == null) {
applyIcon(mIcon1, task1);
applyIcon(mIcon2, task2);
- setContentDescription(task2 == null
- ? task1.titleDescription
- : getContext().getString(
- R.string.quick_switch_split_task,
- task1.titleDescription,
- task2.titleDescription));
return;
}
+
iconUpdateFunction.updateIconInBackground(task1, t -> {
applyIcon(mIcon1, task1);
if (task2 != null) {
return;
}
- setContentDescription(task1.titleDescription);
+ updateContentDesctiptionForTasks(task1, null);
});
+
if (task2 == null) {
return;
}
iconUpdateFunction.updateIconInBackground(task2, t -> {
applyIcon(mIcon2, task2);
- setContentDescription(getContext().getString(
- R.string.quick_switch_split_task,
- task1.titleDescription,
- task2.titleDescription));
+ updateContentDesctiptionForTasks(task1, task2);
});
}
+ /**
+ * Initializes information about the task's position within the parent container context - used
+ * to add position information to the view's content description.
+ * Should be called before associating the view with tasks.
+ *
+ * @param index The view's 0-based index within the parent task container.
+ * @param totalTasks The total number of tasks in the parent task container.
+ */
+ protected void setPositionInformation(int index, int totalTasks) {
+ mIndexInParent = index;
+ mTotalTasksInParent = totalTasks;
+ }
+
protected void setThumbnailsForSplitTasks(
@NonNull Task task1,
@Nullable Task task2,
@@ -281,6 +305,28 @@
constantState.newDrawable(getResources(), getContext().getTheme()));
}
+ /**
+ * Updates the task view's content description to reflect tasks represented by the view.
+ */
+ private void updateContentDesctiptionForTasks(@NonNull Task task1, @Nullable Task task2) {
+ String tasksDescription = task1.titleDescription == null || task2 == null
+ ? task1.titleDescription
+ : getContext().getString(
+ R.string.quick_switch_split_task,
+ task1.titleDescription,
+ task2.titleDescription);
+ if (mIndexInParent < 0) {
+ setContentDescription(tasksDescription);
+ return;
+ }
+
+ setContentDescription(
+ getContext().getString(R.string.quick_switch_task_with_position_in_parent,
+ tasksDescription != null ? tasksDescription : "",
+ mIndexInParent + 1,
+ mTotalTasksInParent));
+ }
+
protected interface ThumbnailUpdateFunction {
void updateThumbnailInBackground(Task task, Consumer<ThumbnailData> callback);
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
index 4b4d68d..ab147bb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
@@ -30,10 +30,12 @@
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.view.ViewTreeObserver;
import android.view.animation.Interpolator;
import android.widget.HorizontalScrollView;
+import android.widget.ImageButton;
import android.widget.TextView;
import android.window.OnBackInvokedDispatcher;
import android.window.WindowOnBackInvokedDispatcher;
@@ -45,6 +47,7 @@
import com.android.app.animation.Interpolators;
import com.android.internal.jank.Cuj;
+import com.android.launcher3.Flags;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatedFloat;
@@ -55,6 +58,8 @@
import com.android.quickstep.util.SplitTask;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
+import com.android.wm.shell.shared.TypefaceUtils;
+import com.android.wm.shell.shared.TypefaceUtils.FontFamily;
import java.util.HashMap;
import java.util.List;
@@ -102,8 +107,12 @@
private HorizontalScrollView mScrollView;
private ConstraintLayout mContent;
- private int mTaskViewWidth;
- private int mTaskViewHeight;
+ private boolean mSupportsScrollArrows = false;
+ private ImageButton mStartScrollArrow;
+ private ImageButton mEndScrollArrow;
+
+ private int mTaskViewBorderWidth;
+ private int mTaskViewRadius;
private int mSpacing;
private int mSmallSpacing;
private int mOutlineRadius;
@@ -112,11 +121,13 @@
private int mOverviewTaskIndex = -1;
private int mDesktopTaskIndex = -1;
- @Nullable private AnimatorSet mOpenAnimation;
+ @Nullable
+ private AnimatorSet mOpenAnimation;
private boolean mIsBackCallbackRegistered = false;
- @Nullable private KeyboardQuickSwitchViewController.ViewCallbacks mViewCallbacks;
+ @Nullable
+ private KeyboardQuickSwitchViewController.ViewCallbacks mViewCallbacks;
public KeyboardQuickSwitchView(@NonNull Context context) {
this(context, null);
@@ -152,21 +163,37 @@
mNoRecentItemsPane = findViewById(R.id.no_recent_items_pane);
mScrollView = findViewById(R.id.scroll_view);
mContent = findViewById(R.id.content);
+ mStartScrollArrow = findViewById(R.id.scroll_button_start);
+ mEndScrollArrow = findViewById(R.id.scroll_button_end);
+
+ setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
Resources resources = getResources();
- mTaskViewWidth = resources.getDimensionPixelSize(
- R.dimen.keyboard_quick_switch_taskview_width);
- mTaskViewHeight = resources.getDimensionPixelSize(
- R.dimen.keyboard_quick_switch_taskview_height);
mSpacing = resources.getDimensionPixelSize(R.dimen.keyboard_quick_switch_view_spacing);
mSmallSpacing = resources.getDimensionPixelSize(
R.dimen.keyboard_quick_switch_view_small_spacing);
mOutlineRadius = resources.getDimensionPixelSize(R.dimen.keyboard_quick_switch_view_radius);
+ mTaskViewBorderWidth = resources.getDimensionPixelSize(
+ R.dimen.keyboard_quick_switch_border_width);
+ mTaskViewRadius = resources.getDimensionPixelSize(
+ R.dimen.keyboard_quick_switch_task_view_radius);
+
mIsRtl = Utilities.isRtl(resources);
+ if (Flags.taskbarOverflow()) {
+ initializeScrollArrows();
+
+ if (mIsRtl) {
+ mStartScrollArrow.setContentDescription(
+ resources.getString(R.string.quick_switch_scroll_arrow_right));
+ mEndScrollArrow.setContentDescription(
+ resources.getString(R.string.quick_switch_scroll_arrow_left));
+ }
+ }
+
TypefaceUtils.setTypeface(
mNoRecentItemsPane.findViewById(R.id.no_recent_items_text),
- TypefaceUtils.FONT_FAMILY_LABEL_LARGE_BASELINE);
+ FontFamily.GSF_LABEL_LARGE);
}
private void registerOnBackInvokedCallback() {
@@ -274,6 +301,7 @@
continue;
}
+ currentTaskView.setPositionInformation(i, tasksToDisplay);
currentTaskView.setThumbnailsForSplitTasks(
task1,
task2,
@@ -331,6 +359,78 @@
});
}
+ private void initializeScrollArrows() {
+ mSupportsScrollArrows = true;
+
+ mStartScrollArrow.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mIsRtl) {
+ runScrollCommand(false, () -> {
+ mScrollView.smoothScrollBy(mScrollView.getWidth(), 0);
+ });
+ } else {
+ runScrollCommand(false, () -> {
+ mScrollView.smoothScrollBy(-mScrollView.getWidth(), 0);
+ });
+ }
+ }
+ });
+
+ mEndScrollArrow.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mIsRtl) {
+ runScrollCommand(false, () -> {
+ mScrollView.smoothScrollBy(-mScrollView.getWidth(), 0);
+ });
+ } else {
+ runScrollCommand(false, () -> {
+ mScrollView.smoothScrollBy(mScrollView.getWidth(), 0);
+ });
+ }
+ }
+ });
+
+ // Add listeners to disable arrow buttons when the scroll view cannot be further scrolled in
+ // the associated direction.
+ mScrollView.setOnScrollChangeListener(new OnScrollChangeListener() {
+ @Override
+ public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX,
+ int oldScrollY) {
+ updateArrowButtonsEnabledState();
+ }
+ });
+
+ // Update scroll view outline to clip its contents with rounded corners.
+ mScrollView.setClipToOutline(true);
+ mScrollView.setOutlineProvider(new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ int spacingWithoutBorder = mSpacing - mTaskViewBorderWidth;
+ outline.setRoundRect(spacingWithoutBorder,
+ spacingWithoutBorder, view.getWidth() - spacingWithoutBorder,
+ view.getHeight() - spacingWithoutBorder,
+ mTaskViewRadius);
+ }
+ });
+ }
+
+ private void updateArrowButtonsEnabledState() {
+ if (!mDisplayingRecentTasks) {
+ return;
+ }
+
+ int scrollX = mScrollView.getScrollX();
+ if (mIsRtl) {
+ mEndScrollArrow.setEnabled(scrollX > 0);
+ mStartScrollArrow.setEnabled(scrollX < mContent.getWidth() - mScrollView.getWidth());
+ } else {
+ mStartScrollArrow.setEnabled(scrollX > 0);
+ mEndScrollArrow.setEnabled(scrollX < mContent.getWidth() - mScrollView.getWidth());
+ }
+ }
+
int getOverviewTaskIndex() {
return mOverviewTaskIndex;
}
@@ -346,6 +446,21 @@
mViewCallbacks = null;
}
+ private void animateDisplayedContentForClose(View view, AnimatorSet animator) {
+ Animator translationYAnimation = ObjectAnimator.ofFloat(
+ view,
+ TRANSLATION_Y,
+ 0, -Utilities.dpToPx(CONTENT_START_TRANSLATION_Y_DP));
+ translationYAnimation.setDuration(CONTENT_TRANSLATION_Y_ANIMATION_DURATION_MS);
+ translationYAnimation.setInterpolator(CLOSE_TRANSLATION_Y_INTERPOLATOR);
+ animator.play(translationYAnimation);
+
+ Animator contentAlphaAnimation = ObjectAnimator.ofFloat(view, ALPHA, 1f, 0f);
+ contentAlphaAnimation.setDuration(CONTENT_ALPHA_ANIMATION_DURATION_MS);
+ animator.play(contentAlphaAnimation);
+
+ }
+
protected Animator getCloseAnimation() {
AnimatorSet closeAnimation = new AnimatorSet();
@@ -360,17 +475,11 @@
closeAnimation.play(alphaAnimation);
View displayedContent = mDisplayingRecentTasks ? mScrollView : mNoRecentItemsPane;
- Animator translationYAnimation = ObjectAnimator.ofFloat(
- displayedContent,
- TRANSLATION_Y,
- 0, -Utilities.dpToPx(CONTENT_START_TRANSLATION_Y_DP));
- translationYAnimation.setDuration(CONTENT_TRANSLATION_Y_ANIMATION_DURATION_MS);
- translationYAnimation.setInterpolator(CLOSE_TRANSLATION_Y_INTERPOLATOR);
- closeAnimation.play(translationYAnimation);
-
- Animator contentAlphaAnimation = ObjectAnimator.ofFloat(displayedContent, ALPHA, 1f, 0f);
- contentAlphaAnimation.setDuration(CONTENT_ALPHA_ANIMATION_DURATION_MS);
- closeAnimation.play(contentAlphaAnimation);
+ animateDisplayedContentForClose(displayedContent, closeAnimation);
+ if (mSupportsScrollArrows) {
+ animateDisplayedContentForClose(mStartScrollArrow, closeAnimation);
+ animateDisplayedContentForClose(mEndScrollArrow, closeAnimation);
+ }
closeAnimation.addListener(new AnimatorListenerAdapter() {
@Override
@@ -385,6 +494,31 @@
return closeAnimation;
}
+ private void animateDisplayedContentForOpen(View view, AnimatorSet animator) {
+ Animator translationXAnimation = ObjectAnimator.ofFloat(
+ view,
+ TRANSLATION_X,
+ -Utilities.dpToPx(CONTENT_START_TRANSLATION_X_DP), 0);
+ translationXAnimation.setDuration(CONTENT_TRANSLATION_X_ANIMATION_DURATION_MS);
+ translationXAnimation.setInterpolator(OPEN_TRANSLATION_X_INTERPOLATOR);
+ animator.play(translationXAnimation);
+
+ Animator translationYAnimation = ObjectAnimator.ofFloat(
+ view,
+ TRANSLATION_Y,
+ -Utilities.dpToPx(CONTENT_START_TRANSLATION_Y_DP), 0);
+ translationYAnimation.setDuration(CONTENT_TRANSLATION_Y_ANIMATION_DURATION_MS);
+ translationYAnimation.setInterpolator(OPEN_TRANSLATION_Y_INTERPOLATOR);
+ animator.play(translationYAnimation);
+
+ view.setAlpha(0.0f);
+ Animator contentAlphaAnimation = ObjectAnimator.ofFloat(view, ALPHA, 0f,
+ 1f);
+ contentAlphaAnimation.setStartDelay(CONTENT_ALPHA_ANIMATION_START_DELAY_MS);
+ contentAlphaAnimation.setDuration(CONTENT_ALPHA_ANIMATION_DURATION_MS);
+ animator.play(contentAlphaAnimation);
+ }
+
protected void animateOpen(int currentFocusIndexOverride) {
if (mOpenAnimation != null) {
// Restart animation since currentFocusIndexOverride can change the initial scroll.
@@ -407,28 +541,17 @@
mOpenAnimation.play(alphaAnimation);
View displayedContent = mDisplayingRecentTasks ? mScrollView : mNoRecentItemsPane;
- Animator translationXAnimation = ObjectAnimator.ofFloat(
- displayedContent,
- TRANSLATION_X,
- -Utilities.dpToPx(CONTENT_START_TRANSLATION_X_DP), 0);
- translationXAnimation.setDuration(CONTENT_TRANSLATION_X_ANIMATION_DURATION_MS);
- translationXAnimation.setInterpolator(OPEN_TRANSLATION_X_INTERPOLATOR);
- mOpenAnimation.play(translationXAnimation);
+ animateDisplayedContentForOpen(displayedContent, mOpenAnimation);
+ if (mSupportsScrollArrows) {
+ animateDisplayedContentForOpen(mStartScrollArrow, mOpenAnimation);
+ animateDisplayedContentForOpen(mEndScrollArrow, mOpenAnimation);
+ }
- Animator translationYAnimation = ObjectAnimator.ofFloat(
- displayedContent,
- TRANSLATION_Y,
- -Utilities.dpToPx(CONTENT_START_TRANSLATION_Y_DP), 0);
- translationYAnimation.setDuration(CONTENT_TRANSLATION_Y_ANIMATION_DURATION_MS);
- translationYAnimation.setInterpolator(OPEN_TRANSLATION_Y_INTERPOLATOR);
- mOpenAnimation.play(translationYAnimation);
-
- Animator contentAlphaAnimation = ObjectAnimator.ofFloat(displayedContent, ALPHA, 0f, 1f);
- contentAlphaAnimation.setStartDelay(CONTENT_ALPHA_ANIMATION_START_DELAY_MS);
- contentAlphaAnimation.setDuration(CONTENT_ALPHA_ANIMATION_DURATION_MS);
- mOpenAnimation.play(contentAlphaAnimation);
ViewOutlineProvider outlineProvider = getOutlineProvider();
+ int defaultFocusedTaskIndex = Math.min(
+ getTaskCount() - 1,
+ currentFocusIndexOverride == -1 ? 1 : currentFocusIndexOverride);
mOpenAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
@@ -461,9 +584,28 @@
OPEN_OUTLINE_INTERPOLATOR));
}
});
- animateFocusMove(-1, Math.min(
- getTaskCount() - 1,
- currentFocusIndexOverride == -1 ? 1 : currentFocusIndexOverride));
+
+ if (mSupportsScrollArrows) {
+ mScrollView.getViewTreeObserver().addOnGlobalLayoutListener(
+ new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ if (mScrollView.getWidth() == 0) {
+ return;
+ }
+
+ if (mContent.getWidth() > mScrollView.getWidth()) {
+ mStartScrollArrow.setVisibility(VISIBLE);
+ mEndScrollArrow.setVisibility(VISIBLE);
+ updateArrowButtonsEnabledState();
+ }
+ mScrollView.getViewTreeObserver().removeOnGlobalLayoutListener(
+ this);
+ }
+ });
+ }
+
+ animateFocusMove(-1, defaultFocusedTaskIndex);
displayedContent.setVisibility(VISIBLE);
setVisibility(VISIBLE);
requestFocus();
@@ -483,6 +625,11 @@
invalidateOutline();
mOpenAnimation = null;
InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_KEYBOARD_QUICK_SWITCH_OPEN);
+
+ View focusedTask = getTaskAt(defaultFocusedTaskIndex);
+ if (focusedTask != null) {
+ focusedTask.requestAccessibilityFocus();
+ }
}
});
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
index 5f7a026..b5f2532 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
@@ -40,7 +40,6 @@
import com.android.launcher3.desktop.DesktopAppLaunchTransition;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayDragLayer;
-import com.android.launcher3.util.DisplayController;
import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.GroupTask;
@@ -106,8 +105,7 @@
boolean hasDesktopTask,
boolean wasDesktopTaskFilteredOut,
boolean wasOpenedFromTaskbar) {
- final boolean isTransientTaskBar = DisplayController.isTransientTaskbar(
- mControllers.taskbarActivityContext);
+ final boolean isTransientTaskBar = mControllers.taskbarActivityContext.isTransientTaskbar();
positionView(wasOpenedFromTaskbar, isTransientTaskBar);
// Keep the taskbar unstashed if the KQS is opened.
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 2272d11..e998388 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -135,11 +135,11 @@
@Override
protected void onDestroy() {
onLauncherVisibilityChanged(false /* isVisible */, true /* fromInitOrDestroy */);
+ mLauncher.removeOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
super.onDestroy();
mTaskbarLauncherStateController.onDestroy();
mLauncher.setTaskbarUIController(null);
- mLauncher.removeOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
mHomeState.removeListener(mVisibilityChangeListener);
}
@@ -225,9 +225,8 @@
if (isVisible || isPinnedTaskbar) {
return getTaskbarToHomeDuration(shouldOverrideToFastAnimation, isPinnedTaskbar);
} else {
- return DisplayController.isTransientTaskbar(mLauncher)
- ? TRANSIENT_TASKBAR_TRANSITION_DURATION
- : TASKBAR_TO_APP_DURATION;
+ return mControllers.taskbarActivityContext.isTransientTaskbar()
+ ? TRANSIENT_TASKBAR_TRANSITION_DURATION : TASKBAR_TO_APP_DURATION;
}
}
@@ -279,7 +278,10 @@
private void postAdjustHotseatForBubbleBar() {
Hotseat hotseat = mLauncher.getHotseat();
if (hotseat == null || !isBubbleBarVisible()) return;
- hotseat.post(() -> adjustHotseatForBubbleBar(isBubbleBarVisible()));
+ hotseat.post(() -> {
+ if (mControllers == null) return;
+ adjustHotseatForBubbleBar(isBubbleBarVisible());
+ });
}
private boolean isBubbleBarVisible() {
@@ -334,7 +336,7 @@
}
// Persistent features EDU tooltip.
- if (!DisplayController.isTransientTaskbar(mLauncher)) {
+ if (!mControllers.taskbarActivityContext.isTransientTaskbar()) {
mControllers.taskbarEduTooltipController.maybeShowFeaturesEdu();
return;
}
@@ -357,7 +359,7 @@
}
// Persistent features EDU tooltip.
- if (!DisplayController.isTransientTaskbar(mLauncher)) {
+ if (!mControllers.taskbarActivityContext.isTransientTaskbar()) {
return !OnboardingPrefs.TASKBAR_EDU_TOOLTIP_STEP.hasReachedMax(mLauncher);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index ee5b8d1..33cd759 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -42,8 +42,8 @@
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BACK_DISABLED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BACK_DISMISS_IME;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_VISIBLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_BUTTON_VISIBLE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_VISIBLE;
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;
@@ -487,8 +487,17 @@
mPropertyHolders.add(
new StatePropertyHolder(mHomeButtonAlpha.get(
ALPHA_INDEX_KEYGUARD_OR_DISABLE),
- flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0
- && (flags & FLAG_DISABLE_HOME) == 0 && !mContext.isGestureNav()));
+ flags -> {
+ /* when the keyguard is visible hide home button. Anytime we are
+ * occluded we want to show the home button for apps over keyguard.
+ * however we don't want to show when not occluded/visible.
+ * (visible false || occluded true) && disable false && not gnav
+ */
+ return ((flags & FLAG_KEYGUARD_VISIBLE) == 0
+ || (flags & FLAG_KEYGUARD_OCCLUDED) != 0)
+ && (flags & FLAG_DISABLE_HOME) == 0
+ && !mContext.isGestureNav();
+ }));
// Recents button
mRecentsButton = addButton(R.drawable.ic_sysbar_recent, BUTTON_RECENTS,
@@ -870,6 +879,9 @@
if (predictiveBackThreeButtonNav() && buttonType == BUTTON_BACK) {
// set up special touch listener for back button to support predictive back
setBackButtonTouchListener(buttonView, navButtonController);
+ // Set this View clickable, so that NearestTouchFrame.java forwards closeby touches to
+ // this View
+ buttonView.setClickable(true);
} else {
buttonView.setOnClickListener(view ->
navButtonController.onButtonClick(buttonType, view));
diff --git a/quickstep/src/com/android/launcher3/taskbar/PinToTaskbarShortcut.kt b/quickstep/src/com/android/launcher3/taskbar/PinToTaskbarShortcut.kt
new file mode 100644
index 0000000..da6932f
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/PinToTaskbarShortcut.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.launcher3.taskbar
+
+import android.content.Context
+import android.util.SparseArray
+import android.view.View
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.LauncherAppState
+import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT
+import com.android.launcher3.R
+import com.android.launcher3.model.BgDataModel
+import com.android.launcher3.model.data.ItemInfo
+import com.android.launcher3.model.data.WorkspaceItemInfo
+import com.android.launcher3.popup.SystemShortcut
+import com.android.launcher3.views.ActivityContext
+
+/**
+ * A single menu item shortcut to allow users to pin an item to the taskbar and unpin an item from
+ * the taskbar.
+ */
+class PinToTaskbarShortcut<T>(
+ target: T,
+ itemInfo: ItemInfo?,
+ originalView: View,
+ private val mIsPin: Boolean,
+ private val mPinnedInfoList: SparseArray<ItemInfo?>,
+) :
+ SystemShortcut<T>(
+ if (mIsPin) R.drawable.ic_pin else R.drawable.ic_unpin,
+ if (mIsPin) R.string.pin_to_taskbar else R.string.unpin_from_taskbar,
+ target,
+ itemInfo,
+ originalView,
+ ) where T : Context?, T : ActivityContext? {
+
+ override fun onClick(v: View?) {
+ dismissTaskMenuView()
+ // Create a placeholder callbacks for the writer to notify other launcher model callbacks
+ // after update.
+ val callbacks: BgDataModel.Callbacks = object : BgDataModel.Callbacks {}
+
+ val writer =
+ LauncherAppState.getInstance(mOriginalView.context)
+ .model
+ .getWriter(true, mTarget!!.cellPosMapper, callbacks)
+
+ if (!mIsPin) {
+ writer.deleteItemFromDatabase(mItemInfo, "item unpinned through long-press menu")
+ return
+ }
+
+ val newInfo =
+ if (mItemInfo is com.android.launcher3.model.data.AppInfo) {
+ mItemInfo.makeWorkspaceItem(mOriginalView.context)
+ } else if (mItemInfo is WorkspaceItemInfo) {
+ mItemInfo.clone()
+ } else {
+ return
+ }
+
+ val dp: DeviceProfile = mTarget.deviceProfile
+ var targetIdx = -1
+
+ for (i in 0 until dp.numShownHotseatIcons) {
+ if (mPinnedInfoList[i] == null) {
+ targetIdx = i
+ break
+ }
+ }
+
+ val cellX = if (dp.isVerticalBarLayout()) 0 else targetIdx
+ val cellY = if (dp.isVerticalBarLayout()) (dp.numShownHotseatIcons - (targetIdx + 1)) else 0
+
+ writer.addItemToDatabase(newInfo, CONTAINER_HOTSEAT, mItemInfo.screenId, cellX, cellY)
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
index 2e5bebc..6bd3d85 100644
--- a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
@@ -36,7 +36,6 @@
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.RevealOutlineAnimation;
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
-import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.MultiPropertyFactory;
import com.android.launcher3.util.MultiValueAlpha;
@@ -217,7 +216,7 @@
.getTransientTaskbarIconLayoutBounds();
float startRadius = mStashedHandleRadius;
- if (DisplayController.isTransientTaskbar(mActivity)) {
+ if (mActivity.isTransientTaskbar()) {
// Account for the full visual height of the transient taskbar.
int heightDiff = (mTaskbarSize - visualBounds.height()) / 2;
visualBounds.top -= heightDiff;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index a1620a1..e41b2d2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -71,7 +71,6 @@
import android.os.Trace;
import android.provider.Settings;
import android.util.Log;
-import android.view.Display;
import android.view.Gravity;
import android.view.Surface;
import android.view.View;
@@ -81,6 +80,7 @@
import android.widget.Toast;
import android.window.DesktopExperienceFlags;
import android.window.DesktopModeFlags;
+import android.window.DesktopModeFlags.DesktopModeFlag;
import android.window.RemoteTransition;
import androidx.annotation.NonNull;
@@ -94,10 +94,12 @@
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.BubbleTextView.RunningAppState;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Flags;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.R;
import com.android.launcher3.allapps.ActivityAllAppsContainerView;
+import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.apppairs.AppPairIcon;
import com.android.launcher3.config.FeatureFlags;
@@ -137,6 +139,7 @@
import com.android.launcher3.taskbar.bubbles.stashing.TransientBubbleStashController;
import com.android.launcher3.taskbar.customization.TaskbarFeatureEvaluator;
import com.android.launcher3.taskbar.customization.TaskbarSpecsEvaluator;
+import com.android.launcher3.taskbar.growth.NudgeController;
import com.android.launcher3.taskbar.navbutton.NearestTouchFrame;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayController;
import com.android.launcher3.testing.TestLogging;
@@ -157,7 +160,6 @@
import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource;
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.util.VibratorWrapper;
-import com.android.launcher3.util.ViewCache;
import com.android.launcher3.views.ActivityContext;
import com.android.quickstep.NavHandle;
import com.android.quickstep.RecentsModel;
@@ -197,6 +199,9 @@
private static final String WINDOW_TITLE = "Taskbar";
+ private static final DesktopModeFlag ENABLE_TASKBAR_BEHIND_SHADE = new DesktopModeFlag(
+ Flags::enableTaskbarBehindShade, false);
+
private final @Nullable Context mNavigationBarPanelContext;
private final TaskbarDragLayer mDragLayer;
@@ -220,7 +225,6 @@
private NavigationMode mNavMode;
private boolean mImeDrawsImeNavBar;
- private final ViewCache mViewCache = new ViewCache();
private final boolean mIsSafeModeEnabled;
private final boolean mIsUserSetupComplete;
@@ -253,7 +257,7 @@
TaskbarNavButtonController buttonController,
ScopedUnfoldTransitionProgressProvider unfoldTransitionProgressProvider,
boolean isPrimaryDisplay, SystemUiProxy sysUiProxy) {
- super(windowContext);
+ super(windowContext, isPrimaryDisplay);
mIsPrimaryDisplay = isPrimaryDisplay;
mNavigationBarPanelContext = navigationBarPanelContext;
mSysUiProxy = sysUiProxy;
@@ -280,7 +284,6 @@
mIsNavBarForceVisible = mIsNavBarKidsMode;
// Get display and corners first, as views might use them in constructor.
- Display display = windowContext.getDisplay();
Context c = getApplicationContext();
mWindowManager = c.getSystemService(WindowManager.class);
@@ -326,11 +329,11 @@
new BubbleBarViewController(this, bubbleBarView, bubbleBarContainer),
bubbleStashController,
bubbleHandleController,
- new BubbleDragController(this),
+ new BubbleDragController(this, mDragLayer),
new BubbleDismissController(this, mDragLayer),
- new BubbleBarPinController(this, mDragLayer,
+ new BubbleBarPinController(this, bubbleBarContainer,
() -> DisplayController.INSTANCE.get(this).getInfo().currentSize),
- new BubblePinController(this, mDragLayer,
+ new BubblePinController(this, bubbleBarContainer,
() -> DisplayController.INSTANCE.get(this).getInfo().currentSize),
bubbleBarSwipeController,
new BubbleCreator(this)
@@ -379,9 +382,11 @@
new TaskbarPinningController(this),
bubbleControllersOptional,
new TaskbarDesktopModeController(this,
- DesktopVisibilityController.INSTANCE.get(this)));
+ DesktopVisibilityController.INSTANCE.get(this)),
+ new NudgeController(this));
mLauncherPrefs = LauncherPrefs.get(this);
+ onViewCreated();
}
/** Updates {@link DeviceProfile} instances for any Taskbar windows. */
@@ -402,7 +407,7 @@
/** Returns whether current taskbar is transient. */
public boolean isTransientTaskbar() {
- return DisplayController.isTransientTaskbar(this) && !isPhoneMode();
+ return super.isTransientTaskbar() && !isPhoneMode();
}
/**
@@ -423,7 +428,7 @@
mDeviceProfile = originDeviceProfile.toBuilder(this)
.withDimensionsOverride(overrideProvider).build();
- if (DisplayController.isTransientTaskbar(this)) {
+ if (isTransientTaskbar()) {
mTransientTaskbarDeviceProfile = mDeviceProfile;
mPersistentTaskbarDeviceProfile = mDeviceProfile
.toBuilder(this)
@@ -441,7 +446,6 @@
mNavMode = (DesktopExperienceFlags.ENABLE_TASKBAR_CONNECTED_DISPLAYS.isTrue()
&& !mIsPrimaryDisplay) ? NavigationMode.THREE_BUTTONS
: DisplayController.getNavigationMode(this);
-
}
/** Called when the visibility of the bubble bar changed. */
@@ -652,8 +656,7 @@
int windowFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_SLIPPERY
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
- boolean watchOutside = DisplayController.isTransientTaskbar(this)
- || isThreeButtonNav();
+ boolean watchOutside = isTransientTaskbar() || isThreeButtonNav();
if (watchOutside && !isRunningInTestHarness()) {
windowFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
@@ -796,11 +799,6 @@
}
@Override
- public ViewCache getViewCache() {
- return mViewCache;
- }
-
- @Override
public View.OnClickListener getItemOnClickListener() {
return this::onTaskbarIconClicked;
}
@@ -922,7 +920,7 @@
options.setSplashScreenStyle(splashScreenStyle);
options.setPendingIntentBackgroundActivityStartMode(
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
- IRemoteCallback endCallback = completeRunnableListCallback(callbacks);
+ IRemoteCallback endCallback = completeRunnableListCallback(callbacks, this);
options.setOnAnimationAbortListener(endCallback);
options.setOnAnimationFinishedListener(endCallback);
@@ -989,6 +987,7 @@
* Called when this instance of taskbar is no longer needed
*/
public void onDestroy() {
+ onViewDestroyed();
mIsDestroyed = true;
mTaskbarFeatureEvaluator.onDestroy();
setUIController(TaskbarUIController.DEFAULT);
@@ -1218,8 +1217,8 @@
bubbleControllers.bubbleBarViewController.getBubbleBarWithFlyoutMaximumHeight()
).orElse(0);
int taskbarWindowSize;
- boolean shouldTreatAsTransient = DisplayController.isTransientTaskbar(this)
- || (enableTaskbarPinning() && !isThreeButtonNav());
+ boolean shouldTreatAsTransient =
+ isTransientTaskbar() || (enableTaskbarPinning() && !isThreeButtonNav());
int extraHeightForTaskbarTooltips = enableCursorHoverStates()
? resources.getDimensionPixelSize(R.dimen.arrow_toast_arrow_height)
@@ -1292,7 +1291,7 @@
* Applies forcibly show flag to taskbar window iff transient taskbar is unstashed.
*/
public void applyForciblyShownFlagWhileTransientTaskbarUnstashed(boolean shouldForceShow) {
- if (!DisplayController.isTransientTaskbar(this) || isPhoneMode()) {
+ if (!isTransientTaskbar() || isPhoneMode()) {
return;
}
if (shouldForceShow) {
@@ -1339,7 +1338,7 @@
mControllers.uiController.startSplitSelection(splitSelectSource);
}
- boolean areDesktopTasksVisible() {
+ boolean isInDesktopMode() {
return mControllers != null
&& mControllers.taskbarDesktopModeController.isInDesktopMode(getDisplayId());
}
@@ -1355,23 +1354,23 @@
// TODO: b/316004172, b/343289567: Handle `DesktopTask` and `SplitTask`.
if (tag instanceof SingleTask singleTask) {
RemoteTransition remoteTransition =
- (areDesktopTasksVisible() && canUnminimizeDesktopTask(
+ (isInDesktopMode() && canUnminimizeDesktopTask(
singleTask.getTask().key.id))
? createDesktopAppLaunchRemoteTransition(AppLaunchType.UNMINIMIZE,
Cuj.CUJ_DESKTOP_MODE_APP_LAUNCH_FROM_ICON)
: null;
- if (areDesktopTasksVisible() && mControllers.uiController.isInOverviewUi()) {
+ if (isInDesktopMode() && mControllers.uiController.isInOverviewUi()) {
RunnableList runnableList = recents.launchRunningDesktopTaskView();
// Wrapping it in runnable so we post after DW is ready for the app
// launch.
if (runnableList != null) {
runnableList.add(() -> UI_HELPER_EXECUTOR.execute(
() -> handleGroupTaskLaunch(singleTask, remoteTransition,
- areDesktopTasksVisible(),
+ isInDesktopMode(),
DesktopTaskToFrontReason.TASKBAR_TAP)));
}
} else {
- handleGroupTaskLaunch(singleTask, remoteTransition, areDesktopTasksVisible(),
+ handleGroupTaskLaunch(singleTask, remoteTransition, isInDesktopMode(),
DesktopTaskToFrontReason.TASKBAR_TAP);
}
mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
@@ -1396,14 +1395,9 @@
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
- && mControllers.uiController.isInOverviewUi()) {
- RunnableList runnableList = taskView.launchWithAnimation();
+ if (isInDesktopMode() && mControllers.uiController.isInOverviewUi()) {
+ RunnableList runnableList = recents.launchRunningDesktopTaskView();
if (runnableList != null) {
runnableList.add(() ->
// wrapped it in runnable here since we need the post for DW to be
@@ -1574,6 +1568,7 @@
public boolean canUnminimizeDesktopTask(int taskId) {
BubbleTextView.RunningAppState runningAppState =
mControllers.taskbarRecentAppsController.getRunningAppState(taskId);
+ Log.d(TAG, "Task id=" + taskId + ", Running app state=" + runningAppState);
return runningAppState == RunningAppState.MINIMIZED
&& DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_ALTTAB_TRANSITIONS_BUGFIX.isTrue();
}
@@ -1675,7 +1670,7 @@
.launchAppPair((AppPairIcon) launchingIconView,
-1 /*cuj*/)));
} else {
- if (areDesktopTasksVisible()
+ if (isInDesktopMode()
&& mControllers.uiController.isInOverviewUi()) {
RunnableList runnableList = recents.launchRunningDesktopTaskView();
// Wrapping it in runnable so we post after DW is ready for the app
@@ -1721,7 +1716,7 @@
return;
}
}
- if (areDesktopTasksVisible()
+ if (isInDesktopMode()
&& DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX.isTrue()) {
launchDesktopApp(intent, info, displayId);
} else {
@@ -1784,7 +1779,7 @@
folder.animateOpen();
getStatsLogManager().logger().withItemInfo(folder.mInfo).log(LAUNCHER_FOLDER_OPEN);
- folder.iterateOverItems((itemInfo, itemView) -> {
+ folder.mapOverItems((itemInfo, itemView) -> {
mControllers.taskbarViewController
.setClickAndLongClickListenersForIcon(itemView);
// To play haptic when dragging, like other Taskbar items do.
@@ -1886,7 +1881,7 @@
*/
@VisibleForTesting
public void unstashTaskbarIfStashed() {
- if (DisplayController.isTransientTaskbar(this)) {
+ if (isTransientTaskbar()) {
mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(false);
}
}
@@ -1949,6 +1944,8 @@
// Override the alpha updates in the icon alignment animation.
allAppsButton.setAlpha(0);
});
+ alphaOverride.addListener(AnimatorListeners.forSuccessCallback(
+ () -> allAppsButton.setAlpha(1f)));
fullAnimation.play(alphaOverride);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
index 89cc991..5e02d81 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
@@ -30,7 +30,6 @@
import com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound
import com.android.launcher3.taskbar.TaskbarPinningController.Companion.PINNING_PERSISTENT
import com.android.launcher3.taskbar.TaskbarPinningController.Companion.PINNING_TRANSIENT
-import com.android.launcher3.util.DisplayController
import kotlin.math.min
/** Helps draw the taskbar background, made up of a rectangle plus two inverted rounded corners. */
@@ -43,7 +42,7 @@
private val maxPersistentTaskbarHeight =
context.persistentTaskbarDeviceProfile.taskbarHeight.toFloat()
var backgroundProgress =
- if (DisplayController.isTransientTaskbar(context)) {
+ if (context.isTransientTaskbar) {
PINNING_TRANSIENT
} else {
PINNING_PERSISTENT
@@ -102,7 +101,7 @@
fullCornerRadius = context.cornerRadius.toFloat()
cornerRadius = fullCornerRadius
- if (!context.areDesktopTasksVisible()) {
+ if (!context.isInDesktopMode()) {
setCornerRoundness(MAX_ROUNDNESS)
}
}
@@ -124,7 +123,7 @@
* @param cornerRoundness 0 has no round corner, 1 has complete round corner.
*/
fun setCornerRoundness(cornerRoundness: Float) {
- if (DisplayController.isTransientTaskbar(context) && !transientBackgroundBounds.isEmpty) {
+ if (context.isTransientTaskbar && !transientBackgroundBounds.isEmpty) {
return
}
@@ -146,7 +145,7 @@
/** Draws the background with the given paint and height, on the provided canvas. */
fun draw(canvas: Canvas) {
if (isInSetup) return
- val isTransientTaskbar = DisplayController.isTransientTaskbar(context)
+ val isTransientTaskbar = context.isTransientTaskbar
canvas.save()
if (!isTransientTaskbar || transientBackgroundBounds.isEmpty || isAnimatingPinning) {
drawPersistentBackground(canvas)
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
index 6ca9385..9e15a60 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
@@ -25,6 +25,7 @@
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.taskbar.allapps.TaskbarAllAppsController;
import com.android.launcher3.taskbar.bubbles.BubbleControllers;
+import com.android.launcher3.taskbar.growth.NudgeController;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayController;
import com.android.systemui.shared.rotation.RotationButtonController;
import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
@@ -67,6 +68,7 @@
public final TaskbarPinningController taskbarPinningController;
public final Optional<BubbleControllers> bubbleControllers;
public final TaskbarDesktopModeController taskbarDesktopModeController;
+ public final NudgeController nudgeController;
@Nullable private LoggableTaskbarController[] mControllersToLog = null;
@Nullable private BackgroundRendererController[] mBackgroundRendererControllers = null;
@@ -115,7 +117,8 @@
KeyboardQuickSwitchController keyboardQuickSwitchController,
TaskbarPinningController taskbarPinningController,
Optional<BubbleControllers> bubbleControllers,
- TaskbarDesktopModeController taskbarDesktopModeController) {
+ TaskbarDesktopModeController taskbarDesktopModeController,
+ NudgeController nudgeController) {
this.taskbarActivityContext = taskbarActivityContext;
this.taskbarDragController = taskbarDragController;
this.navButtonController = navButtonController;
@@ -143,6 +146,7 @@
this.taskbarPinningController = taskbarPinningController;
this.bubbleControllers = bubbleControllers;
this.taskbarDesktopModeController = taskbarDesktopModeController;
+ this.nudgeController = nudgeController;
}
/**
@@ -179,6 +183,7 @@
keyboardQuickSwitchController.init(this);
taskbarPinningController.init(this, mSharedState);
taskbarDesktopModeController.init(this, mSharedState);
+ nudgeController.init(this);
mControllersToLog = new LoggableTaskbarController[] {
taskbarDragController, navButtonController, navbarButtonsViewController,
@@ -189,14 +194,18 @@
voiceInteractionWindowController, taskbarRecentAppsController,
taskbarTranslationController, taskbarEduTooltipController,
keyboardQuickSwitchController, taskbarPinningController,
+ nudgeController
};
mBackgroundRendererControllers = new BackgroundRendererController[] {
taskbarDragLayerController, taskbarScrimViewController,
voiceInteractionWindowController
};
+ // TODO(b/401061748): get primary status from
+ // TaskbarDesktopModeController/DesktopVisibilityController.
if (taskbarDesktopModeController.isInDesktopModeAndNotInOverview(
- taskbarActivityContext.getDisplayId())) {
+ taskbarActivityContext.getDisplayId())
+ || !taskbarActivityContext.isPrimaryDisplay()) {
mCornerRoundness.value = taskbarDesktopModeController.getTaskbarCornerRoundness(
mSharedState.showCornerRadiusInDesktopMode);
} else {
@@ -257,6 +266,7 @@
mAreAllControllersInitialized = false;
mSharedState = null;
+ taskbarDragController.onDestroy();
navbarButtonsViewController.onDestroy();
uiController.onDestroy();
rotationButtonController.onDestroy();
@@ -277,7 +287,6 @@
taskbarStashController.onDestroy();
bubbleControllers.ifPresent(controllers -> controllers.onDestroy());
taskbarDesktopModeController.onDestroy();
-
mControllersToLog = null;
mBackgroundRendererControllers = null;
}
@@ -341,7 +350,7 @@
return taskbarActivityContext;
}
- protected interface LoggableTaskbarController {
+ public interface LoggableTaskbarController {
void dumpLogs(String prefix, PrintWriter pw);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
index 3f6ebe2..b663444 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
@@ -35,7 +35,6 @@
import com.android.launcher3.R
import com.android.launcher3.popup.ArrowPopup
import com.android.launcher3.popup.RoundedArrowDrawable
-import com.android.launcher3.util.DisplayController
import com.android.launcher3.util.Themes
import com.android.launcher3.views.ActivityContext
import kotlin.math.max
@@ -70,6 +69,8 @@
private lateinit var dividerView: View
private var horizontalPosition = 0.0f
+ private val taskbarActivityContext: TaskbarActivityContext =
+ ActivityContext.lookupContext(context)
private val popupCornerRadius = Themes.getDialogCornerRadius(context)
private val arrowWidth = resources.getDimension(R.dimen.popup_arrow_width)
@@ -78,7 +79,8 @@
private val minPaddingFromScreenEdge =
resources.getDimension(R.dimen.taskbar_pinning_popup_menu_min_padding_from_screen_edge)
- private var alwaysShowTaskbarOn = !DisplayController.isTransientTaskbar(context)
+ // TODO: add test for isTransientTaskbar & long presses divider and ensures the popup shows up.
+ private var alwaysShowTaskbarOn = !taskbarActivityContext.isTransientTaskbar
private var didPreferenceChange = false
private var verticalOffsetForPopupView =
resources.getDimensionPixelSize(R.dimen.taskbar_pinning_popup_menu_vertical_margin)
@@ -113,7 +115,7 @@
}
alwaysShowTaskbarSwitch.setOnClickListener { view -> (view.parent as View).performClick() }
- if (ActivityContext.lookupContext<TaskbarActivityContext>(context).isGestureNav) {
+ if (taskbarActivityContext.isGestureNav) {
taskbarSwitchOption.setOnClickListener {
alwaysShowTaskbarSwitch.isChecked = !alwaysShowTaskbarOn
onClickAlwaysShowTaskbarSwitchOption()
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index 1b516be..142f458 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -80,7 +80,6 @@
import com.android.launcher3.taskbar.bubbles.BubbleBarViewController;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
-import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.views.BubbleTextHolder;
@@ -132,6 +131,14 @@
public void init(TaskbarControllers controllers) {
mControllers = controllers;
+ mControllers.bubbleControllers.ifPresent(
+ c -> c.bubbleBarViewController.addBubbleBarDropTargets(this));
+ }
+
+ /** Called when the controller is destroyed. */
+ public void onDestroy() {
+ mControllers.bubbleControllers.ifPresent(
+ c -> c.bubbleBarViewController.removeBubbleBarDropTargets(this));
}
public void setDisallowGlobalDrag(boolean disallowGlobalDrag) {
@@ -463,7 +470,7 @@
com.android.launcher3.logging.InstanceId launcherInstanceId = instanceIds.second;
intent.putExtra(ClipDescription.EXTRA_LOGGING_INSTANCE_ID, internalInstanceId);
- if (DisplayController.isTransientTaskbar(mActivity)) {
+ if (mActivity.isTransientTaskbar()) {
// Tell WM Shell to ignore drag events in the provided transient taskbar region.
TaskbarDragLayer dragLayer = mControllers.taskbarActivityContext.getDragLayer();
int[] locationOnScreen = dragLayer.getLocationOnScreen();
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
index 55ecc37..1e193f6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
@@ -34,7 +34,6 @@
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.util.DimensionUtils;
-import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
import com.android.launcher3.util.TouchController;
@@ -108,19 +107,18 @@
if (startAnimation != null) {
// set taskbar background render animation boolean
- if (DisplayController.isTransientTaskbar(mActivity)) {
+ if (mActivity.isTransientTaskbar()) {
mTaskbarDragLayer.setIsAnimatingTransientTaskbarBackground(true);
} else {
mTaskbarDragLayer.setIsAnimatingPersistentTaskbarBackground(true);
}
- float desiredValue = DisplayController.isTransientTaskbar(mActivity)
+ float desiredValue = mActivity.isTransientTaskbar()
? PINNING_TRANSIENT
: PINNING_PERSISTENT;
- float nonDesiredvalue = !DisplayController.isTransientTaskbar(mActivity)
- ? PINNING_TRANSIENT
- : PINNING_PERSISTENT;
+ float nonDesiredvalue =
+ !mActivity.isTransientTaskbar() ? PINNING_TRANSIENT : PINNING_PERSISTENT;
ObjectAnimator objectAnimator = mTaskbarBackgroundProgress.animateToValue(
nonDesiredvalue, desiredValue);
@@ -133,9 +131,8 @@
}));
} else {
- mTaskbarBackgroundProgress.updateValue(DisplayController.isTransientTaskbar(mActivity)
- ? PINNING_TRANSIENT
- : PINNING_PERSISTENT);
+ mTaskbarBackgroundProgress.updateValue(
+ mActivity.isTransientTaskbar() ? PINNING_TRANSIENT : PINNING_PERSISTENT);
}
mBgTaskbar.value = 1;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
index 5d1288c..7a23006 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
@@ -52,6 +52,8 @@
import com.android.launcher3.views.BaseDragLayer
import com.android.quickstep.util.ContextualSearchInvoker
import com.android.quickstep.util.LottieAnimationColorUtils
+import com.android.wm.shell.shared.TypefaceUtils
+import com.android.wm.shell.shared.TypefaceUtils.FontFamily
import java.io.PrintWriter
/** First EDU step for swiping up to show transient Taskbar. */
@@ -153,7 +155,7 @@
fun maybeShowSwipeEdu() {
if (
!isTooltipEnabled ||
- !DisplayController.isTransientTaskbar(activityContext) ||
+ !activityContext.isTransientTaskbar ||
tooltipStep > TOOLTIP_STEP_SWIPE
) {
return
@@ -164,7 +166,7 @@
tooltip?.run {
TypefaceUtils.setTypeface(
requireViewById(R.id.taskbar_edu_title),
- TypefaceUtils.FONT_FAMILY_HEADLINE_SMALL_EMPHASIZED,
+ FontFamily.GSF_HEADLINE_SMALL_EMPHASIZED,
)
val swipeAnimation = requireViewById<LottieAnimationView>(R.id.swipe_animation)
swipeAnimation.supportLightTheme()
@@ -198,7 +200,7 @@
suggestionsAnim.supportLightTheme()
pinningAnim.supportLightTheme()
handleEduAnimations(listOf(splitscreenAnim, suggestionsAnim, pinningAnim))
- if (DisplayController.isTransientTaskbar(activityContext)) {
+ if (activityContext.isTransientTaskbar) {
splitscreenAnim.setAnimation(R.raw.taskbar_edu_splitscreen_transient)
suggestionsAnim.setAnimation(R.raw.taskbar_edu_suggestions_transient)
pinningEdu.visibility = if (enableTaskbarPinning()) VISIBLE else GONE
@@ -210,25 +212,25 @@
TypefaceUtils.setTypeface(
requireViewById(R.id.taskbar_edu_title),
- TypefaceUtils.FONT_FAMILY_HEADLINE_SMALL_EMPHASIZED,
+ FontFamily.GSF_HEADLINE_SMALL_EMPHASIZED,
)
TypefaceUtils.setTypeface(
requireViewById(R.id.splitscreen_text),
- TypefaceUtils.FONT_FAMILY_BODY_MEDIUM_BASELINE,
+ FontFamily.GSF_BODY_MEDIUM,
)
TypefaceUtils.setTypeface(
requireViewById(R.id.suggestions_text),
- TypefaceUtils.FONT_FAMILY_BODY_MEDIUM_BASELINE,
+ FontFamily.GSF_BODY_MEDIUM,
)
TypefaceUtils.setTypeface(
requireViewById(R.id.pinning_text),
- TypefaceUtils.FONT_FAMILY_BODY_MEDIUM_BASELINE,
+ FontFamily.GSF_BODY_MEDIUM,
)
// Set up layout parameters.
content.updateLayoutParams { width = MATCH_PARENT }
updateLayoutParams<MarginLayoutParams> {
- if (DisplayController.isTransientTaskbar(activityContext)) {
+ if (activityContext.isTransientTaskbar) {
width =
resources.getDimensionPixelSize(
if (enableTaskbarPinning())
@@ -261,7 +263,7 @@
// for the original 2 edu steps) as a proxy to needing to show the separate pinning edu
if (
!enableTaskbarPinning() ||
- !DisplayController.isTransientTaskbar(activityContext) ||
+ !activityContext.isTransientTaskbar ||
!isTooltipEnabled ||
tooltipStep > TOOLTIP_STEP_PINNING ||
tooltipStep < TOOLTIP_STEP_FEATURES
@@ -275,11 +277,11 @@
allowTouchDismissal = true
TypefaceUtils.setTypeface(
requireViewById(R.id.taskbar_edu_title),
- TypefaceUtils.FONT_FAMILY_HEADLINE_SMALL_EMPHASIZED,
+ FontFamily.GSF_HEADLINE_SMALL_EMPHASIZED,
)
TypefaceUtils.setTypeface(
requireViewById(R.id.pinning_text),
- TypefaceUtils.FONT_FAMILY_BODY_MEDIUM_BASELINE,
+ FontFamily.GSF_BODY_MEDIUM,
)
val pinningAnim =
@@ -287,7 +289,7 @@
pinningAnim.supportLightTheme()
handleEduAnimations(listOf(pinningAnim))
updateLayoutParams<BaseDragLayer.LayoutParams> {
- if (DisplayController.isTransientTaskbar(activityContext)) {
+ if (activityContext.isTransientTaskbar) {
bottomMargin += activityContext.deviceProfile.taskbarHeight
}
// Unlike other tooltips, we want to align with taskbar divider rather than center.
@@ -336,13 +338,13 @@
TypefaceUtils.setTypeface(
requireViewById(R.id.taskbar_edu_title),
- TypefaceUtils.FONT_FAMILY_HEADLINE_SMALL_EMPHASIZED,
+ FontFamily.GSF_HEADLINE_SMALL_EMPHASIZED,
)
- TypefaceUtils.setTypeface(eduSubtitle, TypefaceUtils.FONT_FAMILY_BODY_SMALL_BASELINE)
+ TypefaceUtils.setTypeface(eduSubtitle, FontFamily.GSF_BODY_SMALL)
showDisclosureText(eduSubtitle)
updateLayoutParams<BaseDragLayer.LayoutParams> {
- if (DisplayController.isTransientTaskbar(activityContext)) {
+ if (activityContext.isTransientTaskbar) {
bottomMargin += activityContext.deviceProfile.taskbarHeight
}
// Unlike other tooltips, we want to align with the all apps button rather than
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index a8ce10f..3af2ab6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -235,8 +235,7 @@
context.resources,
)
val isPinnedTaskbar =
- context.deviceProfile.isTaskbarPresent &&
- !context.deviceProfile.isTransientTaskbar
+ context.deviceProfile.isTaskbarPresent && !context.isTransientTaskbar
val mandatoryGestureHeight = if (isPinnedTaskbar) contentHeight else gestureHeight
provider.insetsSize =
getInsetsForGravityWithCutout(mandatoryGestureHeight, gravity, endRotation)
@@ -388,10 +387,7 @@
bubbleBarVisible
) {
// Taskbar has some touchable elements, take over the full taskbar area
- if (
- controllers.uiController.isInOverviewUi &&
- DisplayController.isTransientTaskbar(context)
- ) {
+ if (controllers.uiController.isInOverviewUi && context.isTransientTaskbar) {
val region =
controllers.taskbarActivityContext.dragLayer.lastDrawnTransientRect.toRegion()
val bubbleBarBounds =
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 10eb64a..b510e7e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -191,6 +191,8 @@
private boolean mIsQsbInline;
+ private RecentsAnimationCallbacks mRecentsAnimationCallbacks;
+
private final DeviceProfile.OnDeviceProfileChangeListener mOnDeviceProfileChangeListener =
new DeviceProfile.OnDeviceProfileChangeListener() {
@Override
@@ -295,6 +297,11 @@
mIsDestroyed = true;
mCanSyncViews = false;
+ if (mRecentsAnimationCallbacks != null) {
+ mRecentsAnimationCallbacks.removeListener(mTaskBarRecentsAnimationListener);
+ mRecentsAnimationCallbacks = null;
+ }
+
mIconAlignment.finishAnimation();
mLauncher.getHotseat().setIconsAlpha(1f, ALPHA_CHANNEL_TASKBAR_ALIGNMENT);
@@ -315,6 +322,7 @@
// If going to overview, stash the task bar
// If going home, align the icons to hotseat
AnimatorSet animatorSet = new AnimatorSet();
+ mRecentsAnimationCallbacks = callbacks;
// Update stashed flags first to ensure goingToUnstashedLauncherState() returns correctly.
TaskbarStashController stashController = mControllers.taskbarStashController;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index 19e528a..e8852e2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -15,8 +15,10 @@
*/
package com.android.launcher3.taskbar;
-import static android.content.Context.RECEIVER_NOT_EXPORTED;
import static android.content.Context.RECEIVER_EXPORTED;
+import static android.content.Context.RECEIVER_NOT_EXPORTED;
+import static android.content.pm.PackageManager.FEATURE_PC;
+import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
@@ -25,6 +27,8 @@
import static com.android.launcher3.Flags.enableUnfoldStateAnimation;
import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION;
import static com.android.launcher3.config.FeatureFlags.enableTaskbarNoRecreate;
+import static com.android.launcher3.taskbar.growth.GrowthConstants.BROADCAST_SHOW_NUDGE;
+import static com.android.launcher3.taskbar.growth.GrowthConstants.GROWTH_NUDGE_PERMISSION;
import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY;
import static com.android.launcher3.util.DisplayController.CHANGE_DESKTOP_MODE;
import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
@@ -32,7 +36,6 @@
import static com.android.launcher3.util.DisplayController.CHANGE_TASKBAR_PINNING;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange;
-import static com.android.launcher3.taskbar.growth.GrowthConstants.BROADCAST_SHOW_NUDGE;
import static com.android.quickstep.util.SystemActionConstants.ACTION_SHOW_TASKBAR;
import static com.android.quickstep.util.SystemActionConstants.SYSTEM_ACTION_ID_TASKBAR;
@@ -81,10 +84,12 @@
import com.android.quickstep.RecentsActivity;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.fallback.window.RecentsDisplayModel;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
import com.android.quickstep.fallback.window.RecentsWindowManager;
import com.android.quickstep.util.ContextualSearchInvoker;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.views.RecentsViewContainer;
+import com.android.server.am.Flags;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.statusbar.phone.BarTransitions;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -165,6 +170,10 @@
private final SparseArray<DeviceProfile> mExternalDeviceProfiles = new SparseArray<>();
private StatefulActivity mActivity;
private RecentsViewContainer mRecentsViewContainer;
+ /** Whether this device is a desktop android device **/
+ private boolean mIsAndroidPC;
+ /** Whether this device supports freeform windows management. Can change dynamically **/
+ private boolean mSupportsFreeformWindowsManagement;
/**
* Cache a copy here so we can initialize state whenever taskbar is recreated, since
@@ -178,6 +187,12 @@
*/
private final RecreationListener mRecreationListener = new RecreationListener();
+ // Currently, there is a duplicative call to recreate taskbars when user enter/exit Desktop
+ // Mode upon getting transition callback from shell side. So, we make sure that if taskbar is
+ // already in recreate process due to transition callback, don't recreate for
+ // DisplayInfoChangeListener.
+ private boolean mShouldIgnoreNextDesktopModeChangeFromDisplayController = false;
+
private class RecreationListener implements DisplayController.DisplayInfoChangeListener {
@Override
public void onDisplayInfoChanged(Context context, DisplayController.Info info, int flags) {
@@ -192,6 +207,7 @@
if ((flags & CHANGE_DESKTOP_MODE) != 0) {
debugTaskbarManager("onDisplayInfoChanged: Desktop mode changed",
context.getDisplayId());
+ handleDisplayUpdatesForPerceptibleTasks();
}
if ((flags & CHANGE_TASKBAR_PINNING) != 0) {
debugTaskbarManager("onDisplayInfoChanged: Taskbar pinning changed",
@@ -206,10 +222,12 @@
if ((flags & CHANGE_SHOW_LOCKED_TASKBAR) != 0) {
recreateTaskbars();
} else if ((flags & CHANGE_DESKTOP_MODE) != 0) {
+ if (mShouldIgnoreNextDesktopModeChangeFromDisplayController) {
+ mShouldIgnoreNextDesktopModeChangeFromDisplayController = false;
+ return;
+ }
// Only Handles Special Exit Cases for Desktop Mode Taskbar Recreation.
if (taskbarActivityContext != null
- && !DesktopVisibilityController.INSTANCE.get(taskbarActivityContext)
- .isInDesktopMode()
&& !DisplayController.showLockedTaskbarOnHome(context)) {
recreateTaskbars();
}
@@ -225,33 +243,30 @@
recreateTaskbars();
};
- private final PerceptibleTaskListener mTaskStackListener;
+ private PerceptibleTaskListener mTaskStackListener;
private class PerceptibleTaskListener implements TaskStackChangeListener {
private ArraySet<Integer> mPerceptibleTasks = new ArraySet<Integer>();
@Override
public void onTaskMovedToFront(int taskId) {
- if (mPerceptibleTasks.contains(taskId)) {
- return;
- }
-
// This listens to any Task, so we filter them by the ones shown in the launcher.
// For Tasks restored after startup, they will by default not be Perceptible, and no
// need to until user interacts with it by bringing it to the foreground.
for (int i = 0; i < mTaskbars.size(); i++) {
- // get pinned tasks
+ // get pinned tasks - we care about all tasks, not just the one moved to the front
Set<Integer> taskbarPinnedTasks =
mTaskbars.valueAt(i).getControllers().taskbarViewController
.getTaskIdsForPinnedApps();
- // mark as perceptible if the foregrounded task is in the list of apps shown in
- // the launcher.
- if (taskbarPinnedTasks.contains(taskId)
- && ActivityManagerWrapper.getInstance()
- .setTaskIsPerceptible(taskId, true)
- ) {
- mPerceptibleTasks.add(taskId);
+ // filter out tasks already marked as perceptible
+ taskbarPinnedTasks.removeAll(mPerceptibleTasks);
+
+ // add the filtered tasks as perceptible
+ for (int pinnedTaskId : taskbarPinnedTasks) {
+ ActivityManagerWrapper.getInstance()
+ .setTaskIsPerceptible(pinnedTaskId, true);
+ mPerceptibleTasks.add(pinnedTaskId);
}
}
}
@@ -282,6 +297,14 @@
public void onTaskRemoved(int taskId) {
mPerceptibleTasks.remove(taskId);
}
+
+ public void unregisterListener() {
+ for (Integer taskId : mPerceptibleTasks) {
+ ActivityManagerWrapper.getInstance().setTaskIsPerceptible(taskId, false);
+ }
+ TaskStackChangeListeners.getInstance().unregisterTaskStackListener(
+ mTaskStackListener);
+ }
}
private final DesktopVisibilityController.TaskbarDesktopModeListener
@@ -295,6 +318,7 @@
displayId);
if (taskbarActivityContext != null
&& !taskbarActivityContext.isInOverview()) {
+ mShouldIgnoreNextDesktopModeChangeFromDisplayController = true;
AnimatorSet animatorSet = taskbarActivityContext.onDestroyAnimation(
TASKBAR_DESTROY_DURATION);
animatorSet.addListener(AnimatorListeners.forEndCallback(
@@ -311,11 +335,15 @@
int displayId = mTaskbars.keyAt(taskbarIndex);
TaskbarActivityContext taskbarActivityContext = getTaskbarForDisplay(
displayId);
- AnimatorSet animatorSet = taskbarActivityContext.onDestroyAnimation(
- TASKBAR_DESTROY_DURATION);
- animatorSet.addListener(AnimatorListeners.forEndCallback(
- () -> recreateTaskbarForDisplay(getDefaultDisplayId(), duration)));
- animatorSet.start();
+ if (taskbarActivityContext != null) {
+ mShouldIgnoreNextDesktopModeChangeFromDisplayController = true;
+ AnimatorSet animatorSet = taskbarActivityContext.onDestroyAnimation(
+ TASKBAR_DESTROY_DURATION);
+ animatorSet.addListener(AnimatorListeners.forEndCallback(
+ () -> recreateTaskbarForDisplay(getDefaultDisplayId(),
+ duration)));
+ animatorSet.start();
+ }
}
}
@@ -326,7 +354,6 @@
}
};
-
private boolean mUserUnlocked = false;
private final SimpleBroadcastReceiver mTaskbarBroadcastReceiver;
@@ -431,7 +458,7 @@
mGrowthBroadcastReceiver =
new SimpleBroadcastReceiver(
mPrimaryWindowContext, UI_HELPER_EXECUTOR, this::showGrowthNudge);
- mGrowthBroadcastReceiver.register(RECEIVER_EXPORTED,
+ mGrowthBroadcastReceiver.register(null, GROWTH_NUDGE_PERMISSION, RECEIVER_EXPORTED,
BROADCAST_SHOW_NUDGE);
} else {
mGrowthBroadcastReceiver = null;
@@ -446,7 +473,10 @@
mTaskbarBroadcastReceiver.register(RECEIVER_NOT_EXPORTED, ACTION_SHOW_TASKBAR);
});
- if (ActivityManagerWrapper.usePerceptibleTasks(getPrimaryWindowContext())) {
+ mIsAndroidPC = getPrimaryWindowContext().getPackageManager().hasSystemFeature(FEATURE_PC);
+ mSupportsFreeformWindowsManagement = getFreeformWindowsManagementInfo();
+
+ if (eligibleForPerceptibleTasks()) {
mTaskStackListener = new PerceptibleTaskListener();
TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
} else {
@@ -456,6 +486,30 @@
debugPrimaryTaskbar("TaskbarManager created");
}
+ private void handleDisplayUpdatesForPerceptibleTasks() {
+ // 1. When desktop mode changes, detect eligibility for perceptible tasks.
+ // 2. When no longer eligible for perceptible tasks, turn off and clean up.
+ mSupportsFreeformWindowsManagement = getFreeformWindowsManagementInfo();
+ if (eligibleForPerceptibleTasks()) {
+ if (mTaskStackListener == null) {
+ mTaskStackListener = new PerceptibleTaskListener();
+ TaskStackChangeListeners.getInstance()
+ .registerTaskStackListener(mTaskStackListener);
+ }
+ } else {
+ // not eligible for perceptible tasks, so we should unregister the listener
+ if (mTaskStackListener != null) {
+ mTaskStackListener.unregisterListener();
+ mTaskStackListener = null;
+ }
+ }
+ }
+
+ private boolean getFreeformWindowsManagementInfo() {
+ return getPrimaryWindowContext().getPackageManager().hasSystemFeature(
+ FEATURE_FREEFORM_WINDOW_MANAGEMENT);
+ }
+
private void destroyAllTaskbars() {
debugPrimaryTaskbar("destroyAllTaskbars");
for (int i = 0; i < mTaskbars.size(); i++) {
@@ -493,7 +547,7 @@
debugPrimaryTaskbar("destroyTaskbarForDisplay");
// TODO: make this code displayId specific
TaskbarActivityContext taskbar = getTaskbarForDisplay(getDefaultDisplayId());
- if (ACTION_SHOW_TASKBAR.equals(intent.getAction())) {
+ if (ACTION_SHOW_TASKBAR.equals(intent.getAction()) && taskbar != null) {
taskbar.showTaskbarFromBroadcast();
}
}
@@ -618,7 +672,7 @@
/** Creates a {@link TaskbarUIController} to use with non default displays. */
private TaskbarUIController createTaskbarUIControllerForNonDefaultDisplay(int displayId) {
debugPrimaryTaskbar("createTaskbarUIControllerForNonDefaultDisplay");
- if (RecentsDisplayModel.enableOverviewInWindow()) {
+ if (RecentsWindowFlags.Companion.getEnableOverviewInWindow()) {
RecentsViewContainer rvc = mRecentsDisplayModel.getRecentsWindowManager(displayId);
if (rvc != null) {
return createTaskbarUIControllerForRecentsViewContainer(rvc);
@@ -722,8 +776,7 @@
displayId);
taskbar.updateDeviceProfile(dp);
}
- mSharedState.startTaskbarVariantIsTransient =
- DisplayController.isTransientTaskbar(taskbar);
+ mSharedState.startTaskbarVariantIsTransient = taskbar.isTransientTaskbar();
mSharedState.allAppsVisible = mSharedState.allAppsVisible && isLargeScreenTaskbar;
taskbar.init(mSharedState, duration);
@@ -977,12 +1030,12 @@
displayId);
removeAndUnregisterComponentCallbacks(displayId);
- debugTaskbarManager("onDisplayRemoved: destroying Taskbar!", displayId);
- destroyTaskbarForDisplay(displayId);
-
debugTaskbarManager("onDisplayRemoved: removing DeviceProfile from map!", displayId);
removeDeviceProfileFromMap(displayId);
+ debugTaskbarManager("onDisplayRemoved: destroying Taskbar!", displayId);
+ destroyTaskbarForDisplay(displayId);
+
debugTaskbarManager("onDisplayRemoved: removing WindowContext from map!", displayId);
removeWindowContextFromMap(displayId);
@@ -1041,17 +1094,26 @@
debugPrimaryTaskbar("destroy: unregistering component callbacks");
removeAndUnregisterComponentCallbacks(getDefaultDisplayId());
mShutdownReceiver.unregisterReceiverSafely();
- if (ActivityManagerWrapper.usePerceptibleTasks(getPrimaryWindowContext())) {
- for (Integer taskId : mTaskStackListener.mPerceptibleTasks) {
- ActivityManagerWrapper.getInstance().setTaskIsPerceptible(taskId, false);
- }
+ if (mTaskStackListener != null) {
+ mTaskStackListener.unregisterListener();
}
- TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
+
debugPrimaryTaskbar("destroy: destroying all taskbars!");
destroyAllTaskbars();
debugPrimaryTaskbar("destroy: finished!");
}
+ private boolean eligibleForPerceptibleTasks() {
+ // Perceptible tasks feature (oom boosting) is eligible for android PC devices, and
+ // other android devices that supports free form windows
+ //
+ // - isAndroidPC is set per device (in this case, desktop devices)
+ // - supportsFreeformWindowsManagement is dynamic, and is to be used for the use-case where
+ // user plugs in their device to external displays
+ return Flags.perceptibleTasks()
+ && (mIsAndroidPC || mSupportsFreeformWindowsManagement);
+ }
+
public @Nullable TaskbarActivityContext getCurrentActivityContext() {
return getTaskbarForDisplay(getDefaultDisplayId());
}
@@ -1157,7 +1219,7 @@
* @return The {@link TaskbarActivityContext} for the specified display, or
* {@code null} if no taskbar is associated with that display.
*/
- private TaskbarActivityContext getTaskbarForDisplay(int displayId) {
+ public TaskbarActivityContext getTaskbarForDisplay(int displayId) {
return mTaskbars.get(displayId);
}
@@ -1308,7 +1370,6 @@
}
}
} else {
-
getCurrentActivityContext().onConfigurationChanged(configDiff);
}
mOldConfig = new Configuration(newConfig);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
index 0fa82ae..15c7a8d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
@@ -18,6 +18,7 @@
import android.util.SparseArray;
import android.view.View;
+import androidx.annotation.NonNull;
import androidx.annotation.UiThread;
import com.android.launcher3.LauncherSettings.Favorites;
@@ -120,14 +121,15 @@
}
@Override
- public void mapOverItems(ItemOperator op) {
+ public View mapOverItems(@NonNull ItemOperator op) {
final int itemCount = mContainer.getChildCount();
for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) {
View item = mContainer.getChildAt(itemIdx);
if (item.getTag() instanceof ItemInfo itemInfo && op.evaluate(itemInfo, item)) {
- return;
+ return item;
}
}
+ return null;
}
@Override
@@ -204,6 +206,7 @@
ItemInfo[] hotseatItemInfos, List<GroupTask> recentTasks) {
mContainer.updateItems(hotseatItemInfos, recentTasks);
mControllers.taskbarViewController.updateIconViewsRunningStates();
+ mControllers.taskbarPopupController.setHotseatInfosList(mHotseatItems);
}
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarOverflowView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarOverflowView.java
index 017a12c..d909d19 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarOverflowView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarOverflowView.java
@@ -165,7 +165,6 @@
private boolean mIsRtlLayout;
private final List<Task> mItems = new ArrayList<Task>();
private int mIconSize;
- private int mPadding;
private Paint mItemBackgroundPaint;
private final MultiTranslateDelegate mTranslateDelegate = new MultiTranslateDelegate(this);
private float mScaleForReorderBounce = 1f;
@@ -214,25 +213,25 @@
TaskbarOverflowView icon = (TaskbarOverflowView) inflater.inflate(resId, group, false);
icon.mIconSize = iconSize;
- icon.mPadding = padding;
final float taskbarIconRadius =
- iconSize * IconNormalizer.ICON_VISIBLE_AREA_FACTOR / 2f - padding;
+ (iconSize - padding * 2f) * IconNormalizer.ICON_VISIBLE_AREA_FACTOR / 2f;
icon.mLeaveBehindSizeDefault = taskbarIconRadius; // 1/2 of taskbar app icon size
icon.mLeaveBehindSizeScaledDown =
icon.mLeaveBehindSizeDefault * LEAVE_BEHIND_SIZE_SCALE_DOWN_MULTIPLIER;
icon.mLeaveBehindSize = icon.mLeaveBehindSizeScaledDown;
- icon.mItemIconStrokeWidthDefault = taskbarIconRadius / 5f; // 1/10 of taskbar app icon size
+ icon.mItemIconStrokeWidthDefault =
+ taskbarIconRadius / 10f; // 1/20 of taskbar app icon size
icon.mItemIconStrokeWidth = icon.mItemIconStrokeWidthDefault;
- icon.mItemIconSizeDefault = 2 * (taskbarIconRadius - icon.mItemIconStrokeWidthDefault)
- * TWO_ITEM_ICONS_BOX_ASPECT_RATIO;
+ icon.mItemIconSizeDefault = 2f * taskbarIconRadius * TWO_ITEM_ICONS_BOX_ASPECT_RATIO;
icon.mItemIconSizeScaledDown = icon.mLeaveBehindSizeScaledDown;
icon.mItemIconSize = icon.mItemIconSizeDefault;
- icon.mItemIconCenterOffsetDefault = taskbarIconRadius - icon.mItemIconSizeDefault / 2f
+ icon.mItemIconCenterOffsetDefault = taskbarIconRadius
+ - icon.mItemIconSizeDefault * IconNormalizer.ICON_VISIBLE_AREA_FACTOR / 2f
- icon.mItemIconStrokeWidthDefault;
icon.mItemIconCenterOffset = icon.mItemIconCenterOffsetDefault;
@@ -242,9 +241,8 @@
private void init() {
mIsRtlLayout = Utilities.isRtl(getResources());
mItemBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mItemBackgroundColor = getContext().getColor(Utilities.isDarkTheme(getContext())
- ? com.android.internal.R.color.materialColorSurface
- : com.android.internal.R.color.materialColorInverseOnSurface);
+ mItemBackgroundColor = getContext().getColor(
+ com.android.internal.R.color.materialColorInverseOnSurface);
mLeaveBehindColor = Themes.getAttrColor(getContext(), android.R.attr.textColorTertiary);
setWillNotDraw(false);
@@ -260,8 +258,9 @@
private void drawAppIcons(@NonNull Canvas canvas) {
mItemBackgroundPaint.setColor(mItemBackgroundColor);
- float radius = mIconSize / 2f - mPadding;
+ float canvasCenterXY = mIconSize / 2f;
int adjustedItemIconSize = Math.round(mItemIconSize);
+ float itemIconRadius = adjustedItemIconSize / 2f;
int itemsToShow = Math.min(mItems.size(), MAX_ITEMS_IN_PREVIEW);
for (int i = itemsToShow - 1; i >= 0; --i) {
@@ -280,12 +279,12 @@
BlendMode.SRC_ATOP));
canvas.save();
- float itemIconRadius = adjustedItemIconSize / 2f;
canvas.translate(
- mPadding + itemCenterX + radius - itemIconRadius,
- mPadding + itemCenterY + radius - itemIconRadius);
+ canvasCenterXY + itemCenterX - itemIconRadius,
+ canvasCenterXY + itemCenterY - itemIconRadius);
canvas.drawCircle(itemIconRadius, itemIconRadius,
- itemIconRadius + mItemIconStrokeWidth, mItemBackgroundPaint);
+ itemIconRadius * IconNormalizer.ICON_VISIBLE_AREA_FACTOR + mItemIconStrokeWidth,
+ mItemBackgroundPaint);
iconCopy.draw(canvas);
canvas.restore();
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
index a9ee584..1a6cd60 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
@@ -16,18 +16,20 @@
package com.android.launcher3.taskbar;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_ALL_APPS;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
import static com.android.launcher3.model.data.AppInfo.COMPONENT_KEY_COMPARATOR;
-import static com.android.launcher3.popup.SystemShortcut.PIN_UNPIN_ITEM;
import static com.android.launcher3.util.SplitConfigurationOptions.getLogEventForPosition;
import android.content.Intent;
import android.content.pm.LauncherApps;
import android.graphics.Point;
import android.util.Pair;
+import android.util.SparseArray;
import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.internal.logging.InstanceId;
import com.android.launcher3.AbstractFloatingView;
@@ -50,6 +52,7 @@
import com.android.launcher3.views.ActivityContext;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.LogUtils;
+import com.android.quickstep.util.SingleTask;
import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
@@ -81,6 +84,8 @@
private TaskbarControllers mControllers;
private boolean mAllowInitialSplitSelection;
private AppInfo[] mAppInfosList;
+ // Saves the ItemInfos in the hotseat without the predicted items.
+ private SparseArray<ItemInfo> mHotseatInfosList;
private ManageWindowsTaskbarShortcut<BaseTaskbarContext> mManageWindowsTaskbarShortcut;
@@ -136,22 +141,35 @@
icon.clearFocus();
return null;
}
- // TODO(b/344657629) support GroupTask as well, for Taskbar Recent apps
- if (!(icon.getTag() instanceof ItemInfo item) || !ShortcutUtil.supportsShortcuts(item)) {
+
+ ItemInfo itemInfo;
+ if (icon.getTag() instanceof ItemInfo item && ShortcutUtil.supportsShortcuts(item)) {
+ itemInfo = item;
+ } else if (icon.getTag() instanceof SingleTask task) {
+ itemInfo = SingleTask.Companion.createTaskItemInfo(task);
+ } else {
return null;
}
PopupContainerWithArrow<BaseTaskbarContext> container;
- int deepShortcutCount = mPopupDataProvider.getShortcutCountForItem(item);
+ int deepShortcutCount = mPopupDataProvider.getShortcutCountForItem(itemInfo);
// TODO(b/198438631): add support for INSTALL shortcut factory
List<SystemShortcut> systemShortcuts = getSystemShortcuts()
- .map(s -> s.getShortcut(context, item, icon))
+ .map(s -> s.getShortcut(context, itemInfo, icon))
.filter(Objects::nonNull)
.collect(Collectors.toList());
+ // TODO(b/375648361): Revisit to see if this can be implemented within getSystemShortcuts().
+ if (Flags.enablePinningAppWithContextMenu()) {
+ SystemShortcut shortcut = createPinShortcut(context, itemInfo, icon);
+ if (shortcut != null) {
+ systemShortcuts.add(0, shortcut);
+ }
+ }
+
container = (PopupContainerWithArrow) context.getLayoutInflater().inflate(
R.layout.popup_container, context.getDragLayer(), false);
- container.populateAndShowRows(icon, deepShortcutCount, systemShortcuts);
+ container.populateAndShowRows(icon, itemInfo, deepShortcutCount, systemShortcuts);
// TODO (b/198438631): configure for taskbar/context
container.setPopupItemDragHandler(new TaskbarPopupItemDragHandler());
@@ -172,9 +190,6 @@
// append split options to APP_INFO shortcut if not in Desktop Windowing mode, the order
// here will reflect in the popup
ArrayList<SystemShortcut.Factory> shortcuts = new ArrayList<>();
- if (Flags.enablePinningAppWithContextMenu()) {
- shortcuts.add(PIN_UNPIN_ITEM);
- }
shortcuts.add(APP_INFO);
if (!mControllers.taskbarDesktopModeController
.isInDesktopModeAndNotInOverview(mContext.getDisplayId())) {
@@ -193,6 +208,26 @@
return shortcuts.stream();
}
+ @Nullable
+ private SystemShortcut createPinShortcut(BaseTaskbarContext target, ItemInfo itemInfo,
+ BubbleTextView originalView) {
+ // Predicted items use {@code HotseatPredictionController.PinPrediction} shortcut to pin.
+ if (itemInfo.isPredictedItem()) {
+ return null;
+ }
+ if (itemInfo.container == CONTAINER_HOTSEAT) {
+ return new PinToTaskbarShortcut<>(target, itemInfo, originalView, false,
+ mHotseatInfosList);
+ }
+ if (mHotseatInfosList.size()
+ < mContext.getTaskbarSpecsEvaluator().getNumShownHotseatIcons()) {
+ return new PinToTaskbarShortcut<>(target, itemInfo, originalView, true,
+ mHotseatInfosList);
+ }
+
+ return null;
+ }
+
@Override
public void dumpLogs(String prefix, PrintWriter pw) {
pw.println(prefix + "TaskbarPopupController:");
@@ -276,6 +311,10 @@
return index < 0 ? null : mAppInfosList[index];
}
+ public void setHotseatInfosList(SparseArray<ItemInfo> info) {
+ mHotseatInfosList = info;
+ }
+
/**
* Returns a stream of Multi Instance menu options if an app supports it.
*/
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
index 4a7e4f0..bf73f02 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
@@ -31,7 +31,6 @@
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.taskbar.bubbles.BubbleControllers;
-import com.android.launcher3.util.DisplayController;
import com.android.quickstep.SystemUiProxy;
import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
@@ -93,7 +92,8 @@
// There is no scrim for the bar in the phone mode.
return;
}
- if (isBubbleBarEnabled() && DisplayController.isTransientTaskbar(mActivity)) {
+ boolean isTransient = mActivity.isTransientTaskbar();
+ if (isBubbleBarEnabled() && isTransient) {
// These scrims aren't used if bubble bar & transient taskbar are active.
return;
}
@@ -112,7 +112,7 @@
boolean showScrimForBubbles = bubblesExpanded
&& !mTaskbarVisible
&& isBubbleControllersPresented
- && !DisplayController.isTransientTaskbar(mActivity)
+ && !mActivity.isTransientTaskbar()
&& !bubbleControllers.bubbleStashController.isBubblesShowingOnHome();
return bubblesExpanded && !mControllers.navbarButtonsViewController.isImeVisible()
&& !isShadeVisible
@@ -122,8 +122,8 @@
}
private float computeScrimAlpha() {
- final boolean isPersistentTaskBarVisible =
- mTaskbarVisible && !DisplayController.isTransientTaskbar(mScrimView.getContext());
+ boolean isTransient = mActivity.isTransientTaskbar();
+ final boolean isPersistentTaskBarVisible = mTaskbarVisible && !isTransient;
final boolean manageMenuExpanded =
(mSysUiStateFlags & SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED) != 0;
if (isPersistentTaskBarVisible && manageMenuExpanded) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java
index 25db960..94cff0b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java
@@ -22,6 +22,7 @@
import android.content.Intent;
import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutInfo;
import android.util.Pair;
import android.view.KeyEvent;
import android.view.View;
@@ -38,6 +39,7 @@
import com.android.launcher3.util.ShortcutUtil;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.LogUtils;
+import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
import java.util.List;
@@ -50,6 +52,7 @@
public static final int MOVE_TO_TOP_OR_LEFT = R.id.action_move_to_top_or_left;
public static final int MOVE_TO_BOTTOM_OR_RIGHT = R.id.action_move_to_bottom_or_right;
+ public static final int CREATE_APPLICATION_BUBBLE = R.id.action_create_application_bubble;
private final LauncherApps mLauncherApps;
private final StatsLogManager mStatsLogManager;
@@ -67,6 +70,9 @@
MOVE_TO_BOTTOM_OR_RIGHT,
R.string.move_drop_target_bottom_or_right,
KeyEvent.KEYCODE_R));
+ mActions.put(CREATE_APPLICATION_BUBBLE, new LauncherAction(
+ CREATE_APPLICATION_BUBBLE, R.string.open_app_as_a_bubble,
+ KeyEvent.KEYCODE_L));
}
@Override
@@ -76,11 +82,27 @@
}
out.add(mActions.get(MOVE_TO_TOP_OR_LEFT));
out.add(mActions.get(MOVE_TO_BOTTOM_OR_RIGHT));
+ if (BubbleAnythingFlagHelper.enableCreateAnyBubble()) {
+ out.add(mActions.get(CREATE_APPLICATION_BUBBLE));
+ }
}
@Override
protected boolean performAction(View host, ItemInfo item, int action, boolean fromKeyboard) {
- if (item instanceof ItemInfoWithIcon
+ if (action == DEEP_SHORTCUTS) {
+ mContext.showPopupMenuForIcon((BubbleTextView) host);
+ return true;
+ } else if (action == CREATE_APPLICATION_BUBBLE) {
+ if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
+ && item instanceof WorkspaceItemInfo) {
+ ShortcutInfo shortcutInfo = ((WorkspaceItemInfo) item).getDeepShortcutInfo();
+ SystemUiProxy.INSTANCE.get(mContext).showShortcutBubble(shortcutInfo);
+ return true;
+ } else if (item.getIntent() != null && item.getIntent().getPackage() != null) {
+ SystemUiProxy.INSTANCE.get(mContext).showAppBubble(item.getIntent(), item.user);
+ return true;
+ }
+ } else if (item instanceof ItemInfoWithIcon
&& (action == MOVE_TO_TOP_OR_LEFT || action == MOVE_TO_BOTTOM_OR_RIGHT)) {
ItemInfoWithIcon info = (ItemInfoWithIcon) item;
int side = action == MOVE_TO_TOP_OR_LEFT
@@ -112,10 +134,6 @@
instanceIds.first);
}
return true;
- } else if (action == DEEP_SHORTCUTS) {
- mContext.showPopupMenuForIcon((BubbleTextView) host);
-
- return true;
}
return false;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarSpringOnStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarSpringOnStashController.java
index f87c21e..fa35a03 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarSpringOnStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarSpringOnStashController.java
@@ -27,7 +27,6 @@
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.SpringAnimationBuilder;
import com.android.launcher3.taskbar.TaskbarControllers.LoggableTaskbarController;
-import com.android.launcher3.util.DisplayController;
import java.io.PrintWriter;
@@ -47,7 +46,7 @@
public TaskbarSpringOnStashController(TaskbarActivityContext context) {
mContext = context;
- mIsTransientTaskbar = DisplayController.isTransientTaskbar(mContext);
+ mIsTransientTaskbar = context.isTransientTaskbar();
mStartVelocityPxPerS = context.getResources()
.getDimension(R.dimen.transient_taskbar_stash_spring_velocity_dp_per_s);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 95724ad..5284edd 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -346,7 +346,7 @@
StashedHandleViewController.ALPHA_INDEX_STASHED);
mTaskbarStashedHandleHintScale = stashedHandleController.getStashedHandleHintScale();
- boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivity);
+ boolean isTransientTaskbar = mActivity.isTransientTaskbar();
boolean isInSetup = !mActivity.isUserSetupComplete() || setupUIVisible;
boolean isStashedInAppAuto =
isTransientTaskbar && !mTaskbarSharedState.getTaskbarWasPinned();
@@ -367,9 +367,7 @@
// Hide the background while stashed so it doesn't show on fast swipes home
boolean shouldHideTaskbarBackground = mActivity.isPhoneMode() ||
- (enableScalingRevealHomeAnimation()
- && DisplayController.isTransientTaskbar(mActivity)
- && isStashed());
+ (enableScalingRevealHomeAnimation() && isTransientTaskbar && isStashed());
mTaskbarBackgroundAlphaForStash.setValue(shouldHideTaskbarBackground ? 0 : 1);
@@ -414,8 +412,7 @@
if (DisplayController.isPinnedTaskbar(mActivity)) {
return PINNED_TASKBAR_TRANSITION_DURATION;
}
- return DisplayController.isTransientTaskbar(mActivity)
- ? TRANSIENT_TASKBAR_STASH_DURATION
+ return mActivity.isTransientTaskbar() ? TRANSIENT_TASKBAR_STASH_DURATION
: TASKBAR_STASH_DURATION;
}
@@ -509,8 +506,8 @@
* @see android.view.WindowInsets.Type#systemBars()
*/
public int getContentHeightToReportToApps() {
- if (mActivity.isUserSetupComplete() && (mActivity.isPhoneGestureNavMode()
- || DisplayController.isTransientTaskbar(mActivity))) {
+ boolean isTransient = mActivity.isTransientTaskbar();
+ if (mActivity.isUserSetupComplete() && (mActivity.isPhoneGestureNavMode() || isTransient)) {
return getStashedHeight();
}
@@ -577,8 +574,7 @@
*/
public void updateAndAnimateTransientTaskbar(boolean stash, boolean shouldBubblesFollow,
boolean delayTaskbarBackground) {
- if (!DisplayController.isTransientTaskbar(mActivity)
- || mActivity.isBubbleBarOnPhone()) {
+ if (!mActivity.isTransientTaskbar() || mActivity.isBubbleBarOnPhone()) {
return;
}
@@ -646,7 +642,7 @@
/** Toggles the Taskbar's stash state. */
public void toggleTaskbarStash() {
- if (!DisplayController.isTransientTaskbar(mActivity) || !hasAnyFlag(FLAGS_IN_APP)) return;
+ if (!mActivity.isTransientTaskbar() || !hasAnyFlag(FLAGS_IN_APP)) return;
updateAndAnimateTransientTaskbar(!hasAnyFlag(FLAG_STASHED_IN_APP_AUTO));
}
@@ -697,8 +693,7 @@
mAnimator = new AnimatorSet();
addJankMonitorListener(
mAnimator, /* expanding= */ !isStashed, /* tag= */ jankTag);
- boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivity);
- final float stashTranslation = mActivity.isPhoneMode() || isTransientTaskbar
+ final float stashTranslation = mActivity.isPhoneMode() || mActivity.isTransientTaskbar()
? 0
: (mUnstashedHeight - mStashedHeight);
@@ -722,7 +717,7 @@
return;
}
- if (isTransientTaskbar) {
+ if (mActivity.isTransientTaskbar()) {
createTransientAnimToIsStashed(mAnimator, isStashed, duration,
shouldDelayBackground, animationType);
} else {
@@ -1144,7 +1139,7 @@
boolean stashForBubbles = hasAnyFlag(FLAG_IN_OVERVIEW)
&& hasAnyFlag(systemUiStateFlags, SYSUI_STATE_BUBBLES_EXPANDED)
- && DisplayController.isTransientTaskbar(mActivity);
+ && mActivity.isTransientTaskbar();
updateStateForFlag(FLAG_STASHED_SYSUI,
hasAnyFlag(systemUiStateFlags, SYSUI_STATE_SCREEN_PINNING) || stashForBubbles);
updateStateForFlag(FLAG_STASHED_DEVICE_LOCKED,
@@ -1174,7 +1169,7 @@
* <p>Do not stash if a system gesture is started.
*/
private boolean shouldStashForIme() {
- if (DisplayController.isTransientTaskbar(mActivity)) {
+ if (mActivity.isTransientTaskbar()) {
return false;
}
// Do not stash if in small screen, with 3 button nav, and in landscape.
@@ -1270,7 +1265,7 @@
*/
public void setUpTaskbarSystemAction(boolean visible) {
UI_HELPER_EXECUTOR.execute(() -> {
- if (!visible || !DisplayController.isTransientTaskbar(mActivity)
+ if (!visible || !mActivity.isTransientTaskbar()
|| mActivity.isPhoneMode()) {
mAccessibilityManager.unregisterSystemAction(SYSTEM_ACTION_ID_TASKBAR);
mIsTaskbarSystemActionRegistered = false;
@@ -1321,7 +1316,7 @@
* If false, attempts to re/start the timeout
*/
public void updateTaskbarTimeout(boolean isAutohideSuspended) {
- if (!DisplayController.isTransientTaskbar(mActivity)) {
+ if (!mActivity.isTransientTaskbar()) {
return;
}
if (isAutohideSuspended) {
@@ -1335,9 +1330,7 @@
* Attempts to start timer to auto hide the taskbar based on time.
*/
private void tryStartTaskbarTimeout() {
- if (!DisplayController.isTransientTaskbar(mActivity)
- || mIsStashed
- || mEnableBlockingTimeoutDuringTests) {
+ if (!mActivity.isTransientTaskbar() || mIsStashed || mEnableBlockingTimeoutDuringTests) {
return;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashViaTouchController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashViaTouchController.kt
index deaf024..df10d24 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashViaTouchController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashViaTouchController.kt
@@ -23,7 +23,6 @@
import com.android.launcher3.touch.SingleAxisSwipeDetector
import com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_NEGATIVE
import com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL
-import com.android.launcher3.util.DisplayController
import com.android.launcher3.util.TouchController
import com.android.quickstep.inputconsumers.TaskbarUnstashInputConsumer
@@ -39,7 +38,7 @@
class TaskbarStashViaTouchController(val controllers: TaskbarControllers) : TouchController {
private val activity: TaskbarActivityContext = controllers.taskbarActivityContext
- private val enabled = DisplayController.isTransientTaskbar(activity)
+ private val enabled = activity.isTransientTaskbar
private val swipeDownDetector: SingleAxisSwipeDetector
private val translationCallback = controllers.taskbarTranslationController.transitionCallback
/** Interpolator to apply resistance as user swipes down to the bottom of the screen. */
@@ -67,7 +66,7 @@
val gestureHeight: Int =
ResourceUtils.getNavbarSize(
ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE,
- activity.resources
+ activity.resources,
)
gestureHeightYThreshold = (activity.deviceProfile.heightPx - gestureHeight).toFloat()
}
@@ -89,7 +88,7 @@
maxTouchDisplacement,
0f,
maxVisualDisplacement,
- displacementInterpolator
+ displacementInterpolator,
)
)
return false
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
index 5a5d6d0..13fb296 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
@@ -29,7 +29,6 @@
import com.android.app.animation.Interpolators;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.SpringAnimationBuilder;
-import com.android.launcher3.util.DisplayController;
import java.io.PrintWriter;
@@ -63,7 +62,7 @@
public TaskbarTranslationController(TaskbarActivityContext context) {
mContext = context;
- mIsTransientTaskbar = DisplayController.isTransientTaskbar(mContext);
+ mIsTransientTaskbar = mContext.isTransientTaskbar();
mCallback = new TransitionCallback();
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index ea0b81e..061a5a1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -37,7 +37,6 @@
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.taskbar.bubbles.BubbleBarController;
-import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.quickstep.util.SplitTask;
import com.android.quickstep.views.RecentsView;
@@ -116,9 +115,8 @@
*/
public void hideOverlayWindow() {
mControllers.keyboardQuickSwitchController.closeQuickSwitchView();
-
- if (!DisplayController.isTransientTaskbar(mControllers.taskbarActivityContext)
- || mControllers.taskbarAllAppsController.isOpen()) {
+ boolean isTransientTaskbar = mControllers.taskbarActivityContext.isTransientTaskbar();
+ if (!isTransientTaskbar || mControllers.taskbarAllAppsController.isOpen()) {
mControllers.taskbarOverlayController.hideWindow();
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index de8e286..b2989cd 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -16,11 +16,11 @@
package com.android.launcher3.taskbar;
import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
+import static android.window.DesktopModeFlags.ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION;
import static com.android.launcher3.BubbleTextView.DISPLAY_TASKBAR;
import static com.android.launcher3.Flags.enableCursorHoverStates;
import static com.android.launcher3.Flags.enableRecentsInTaskbar;
-import static com.android.launcher3.Flags.taskbarRecentsLayoutTransition;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_FOLDER;
import static com.android.launcher3.config.FeatureFlags.enableTaskbarPinning;
@@ -61,7 +61,6 @@
import com.android.launcher3.taskbar.customization.TaskbarAllAppsButtonContainer;
import com.android.launcher3.taskbar.customization.TaskbarDividerContainer;
import com.android.launcher3.uioverrides.PredictedAppIcon;
-import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ActivityContext;
import com.android.quickstep.util.GroupTask;
@@ -70,13 +69,11 @@
import com.android.systemui.shared.recents.model.Task;
import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
-import java.util.function.Predicate;
/**
* Hosts the Taskbar content such as Hotseat and Recent Apps. Drawn on top of other apps.
@@ -132,9 +129,10 @@
private final int mAllAppsButtonTranslationOffset;
- private final int mNumStaticViews;
+ private int mNumStaticViews;
private Set<GroupTask> mPrevRecentTasks = Collections.emptySet();
+ private Set<GroupTask> mPrevOverflowTasks = Collections.emptySet();
public TaskbarView(@NonNull Context context) {
this(context, null);
@@ -185,8 +183,9 @@
setWillNotDraw(false);
mAllAppsButtonContainer = new TaskbarAllAppsButtonContainer(context);
- mAllAppsButtonTranslationOffset = (int) getResources().getDimension(
- mAllAppsButtonContainer.getAllAppsButtonTranslationXOffset(isTransientTaskbar()));
+ mAllAppsButtonTranslationOffset = (int) getResources().getDimension(
+ mAllAppsButtonContainer.getAllAppsButtonTranslationXOffset(
+ mActivityContext.isTransientTaskbar()));
if (enableTaskbarPinning() || enableRecentsInTaskbar()) {
mTaskbarDividerContainer = new TaskbarDividerContainer(context);
@@ -200,9 +199,6 @@
// TODO: Disable touch events on QSB otherwise it can crash.
mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false);
-
- mNumStaticViews = taskbarRecentsLayoutTransition() && !mActivityContext.isPhoneMode()
- ? addStaticViews() : 0;
}
/**
@@ -242,7 +238,7 @@
enableTaskbarPinning() && !mActivityContext.isThreeButtonNav();
availableWidth -= iconSize - (int) getResources().getDimension(
mAllAppsButtonContainer.getAllAppsButtonTranslationXOffset(
- forceTransientTaskbarSize || isTransientTaskbar()));
+ forceTransientTaskbarSize || mActivityContext.isTransientTaskbar()));
++additionalIcons;
return Math.floorDiv(availableWidth, iconSize) + additionalIcons;
@@ -372,23 +368,9 @@
if (!(view.getTag() instanceof CollectionInfo)) {
mActivityContext.getViewCache().recycleView(view.getSourceLayoutResId(), view);
}
- if (view instanceof FolderIcon fi) {
- // We should clear FolderInfo's Folder and FolderIcon to avoid memory leak.
- fi.removeListeners();
- }
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) {
if (mActivityContext.isDestroyed()) return;
@@ -399,7 +381,7 @@
// TODO(b/343289567 and b/316004172): support app pairs and desktop mode.
recentTasks = recentTasks.stream().filter(it -> it instanceof SingleTask).toList();
- if (taskbarRecentsLayoutTransition()) {
+ if (ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION.isTrue()) {
updateItemsWithLayoutTransition(hotseatItemInfos, recentTasks);
} else {
updateItemsWithoutLayoutTransition(hotseatItemInfos, recentTasks);
@@ -429,7 +411,7 @@
mAddedDividerForRecents = true;
}
- updateRecents(recentTasks);
+ updateRecents(recentTasks, hotseatItemInfos.length);
addView(mAllAppsButtonContainer, mIsRtl ? hotseatItemInfos.length : 0);
@@ -449,6 +431,9 @@
private void updateItemsWithLayoutTransition(
ItemInfo[] hotseatItemInfos, List<GroupTask> recentTasks) {
+ if (mNumStaticViews == 0) {
+ mNumStaticViews = addStaticViews();
+ }
// Skip static views and potential All Apps divider, if they are on the left.
mNextViewIndex = mIsRtl ? 0 : mNumStaticViews;
@@ -458,7 +443,7 @@
// Update left section.
if (mIsRtl) {
- updateRecents(recentTasks.reversed());
+ updateRecents(recentTasks.reversed(), hotseatItemInfos.length);
} else {
updateHotseatItems(hotseatItemInfos);
}
@@ -473,11 +458,11 @@
if (mIsRtl) {
updateHotseatItems(hotseatItemInfos);
} else {
- updateRecents(recentTasks);
+ updateRecents(recentTasks, hotseatItemInfos.length);
}
// Recents divider takes priority.
- if (!mAddedDividerForRecents) {
+ if (!mAddedDividerForRecents && !mActivityContext.isInDesktopMode()) {
updateAllAppsDivider();
}
}
@@ -579,6 +564,9 @@
LayoutParams lp = new LayoutParams(mIconTouchSize, mIconTouchSize);
hotseatView.setPadding(mItemPadding, mItemPadding, mItemPadding, mItemPadding);
addView(hotseatView, mNextViewIndex, lp);
+ } else if (hotseatView instanceof FolderIcon fi) {
+ fi.onItemsChanged(false);
+ fi.getFolder().reapplyItemInfo();
}
// Apply the Hotseat ItemInfos, or hide the view if there is none for a given index.
@@ -604,47 +592,59 @@
}
}
- private void updateRecents(List<GroupTask> recentTasks) {
- // At this point, the all apps button has not been added as a child view, but needs to be
- // accounted for when comparing current icon count to max number of icons.
- int nonTaskIconsToBeAdded = 1;
-
+ private void updateRecents(List<GroupTask> recentTasks, int hotseatSize) {
boolean supportsOverflow = Flags.taskbarOverflow() && recentTasks.size() > 1;
int overflowSize = 0;
- if (supportsOverflow) {
- mIdealNumIcons = mNextViewIndex + recentTasks.size() + nonTaskIconsToBeAdded;
+ boolean hasOverflow = false;
+ if (supportsOverflow && mTaskbarOverflowView != null) {
+ // Need to account for All Apps and the divider. If we need to have an overflow, we will
+ // have a divider for recents.
+ final int nonTaskIconsToBeAdded = 2;
+ mIdealNumIcons = hotseatSize + recentTasks.size() + nonTaskIconsToBeAdded;
overflowSize = mIdealNumIcons - mMaxNumIcons;
+ hasOverflow = overflowSize > 0;
- if (overflowSize > 0 && mTaskbarOverflowView != null) {
+ if (!ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION.isTrue() && hasOverflow) {
addView(mTaskbarOverflowView, mNextViewIndex++);
- } else if (mTaskbarOverflowView != null) {
+ } else if (ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION.isTrue()) {
+ // RTL case is handled after we add the recent icons, because the button needs to
+ // then be to the right of them.
+ if (hasOverflow && !mIsRtl) {
+ if (mPrevOverflowTasks.isEmpty()) addView(mTaskbarOverflowView, mNextViewIndex);
+ // NOTE: If overflow already existed, assume the overflow view is already
+ // at the correct position.
+ mNextViewIndex++;
+ } else if (!hasOverflow && !mPrevOverflowTasks.isEmpty()) {
+ removeView(mTaskbarOverflowView);
+ mTaskbarOverflowView.clearItems();
+ }
+ } else {
mTaskbarOverflowView.clearItems();
}
}
- List<Task> overflownTasks = null;
// An extra item needs to be added to overflow button to account for the space taken up by
// the overflow button.
final int itemsToAddToOverflow =
- (overflowSize > 0) ? Math.min(overflowSize + 1, recentTasks.size()) : 0;
- if (overflowSize > 0) {
- overflownTasks = new ArrayList<>(itemsToAddToOverflow);
+ hasOverflow ? Math.min(overflowSize + 1, recentTasks.size()) : 0;
+ final Set<GroupTask> overflownRecentsSet;
+ if (hasOverflow && mTaskbarOverflowView != null) {
+ final int startIndex = mIsRtl ? recentTasks.size() - itemsToAddToOverflow : 0;
+ final int endIndex = mIsRtl ? recentTasks.size() : itemsToAddToOverflow;
+ final List<GroupTask> overflownRecents = recentTasks.subList(startIndex, endIndex);
+ mTaskbarOverflowView.setItems(
+ overflownRecents.stream().map(t -> ((SingleTask) t).getTask()).toList());
+ overflownRecentsSet = new ArraySet<>(overflownRecents);
+ } else {
+ overflownRecentsSet = Collections.emptySet();
}
// Add Recent/Running icons.
final Set<GroupTask> recentTasksSet = new ArraySet<>(recentTasks);
- for (GroupTask task : recentTasks) {
- if (mTaskbarOverflowView != null && overflownTasks != null
- && overflownTasks.size() < itemsToAddToOverflow
- && task instanceof SingleTask singleTask) {
- // TODO(b/343289567 and b/316004172): support app pairs and desktop mode.
- overflownTasks.add(singleTask.getTask());
- if (overflownTasks.size() == itemsToAddToOverflow) {
- mTaskbarOverflowView.setItems(overflownTasks);
- }
- continue;
- }
-
+ final int startIndex = mIsRtl ? 0 : itemsToAddToOverflow;
+ final int endIndex =
+ mIsRtl ? recentTasks.size() - itemsToAddToOverflow : recentTasks.size();
+ for (GroupTask task : recentTasks.subList(startIndex, endIndex)) {
// Replace any Recent views with the appropriate type if it's not already that type.
final int expectedLayoutResId;
boolean isCollection = false;
@@ -663,17 +663,19 @@
View recentIcon = null;
// If a task is new, we should not reuse a view so that it animates in when it is added.
- final boolean canReuseView = !taskbarRecentsLayoutTransition()
- || mPrevRecentTasks.contains(task);
+ final boolean canReuseView = !ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION.isTrue()
+ || (mPrevRecentTasks.contains(task) && !mPrevOverflowTasks.contains(task));
while (canReuseView && isNextViewInSection(GroupTask.class)) {
recentIcon = getChildAt(mNextViewIndex);
+ GroupTask tag = (GroupTask) recentIcon.getTag();
// see if the view can be reused
if ((recentIcon.getSourceLayoutResId() != expectedLayoutResId)
- || (isCollection && (recentIcon.getTag() != task))
+ || (isCollection && tag != task)
// Remove view corresponding to removed task so that it animates out.
- || (taskbarRecentsLayoutTransition()
- && !recentTasksSet.contains(recentIcon.getTag()))) {
+ || (ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION.isTrue()
+ && (!recentTasksSet.contains(tag)
+ || overflownRecentsSet.contains(tag)))) {
removeAndRecycle(recentIcon);
recentIcon = null;
} else {
@@ -704,7 +706,15 @@
removeAndRecycle(getChildAt(mNextViewIndex));
}
+ if (ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION.isTrue() && mIsRtl && hasOverflow) {
+ if (mPrevOverflowTasks.isEmpty()) {
+ addView(mTaskbarOverflowView, mNextViewIndex);
+ }
+ mNextViewIndex++;
+ }
+
mPrevRecentTasks = recentTasksSet;
+ mPrevOverflowTasks = overflownRecentsSet;
}
private boolean isNextViewInSection(Class<?> tagClass) {
@@ -1078,11 +1088,6 @@
// Ignore, we just implement Insettable to draw behind system insets.
}
- private boolean isTransientTaskbar() {
- return DisplayController.isTransientTaskbar(mActivityContext)
- && !mActivityContext.isPhoneMode();
- }
-
public boolean areIconsVisible() {
// Consider the overall visibility
return getVisibility() == VISIBLE;
@@ -1097,29 +1102,6 @@
}
/**
- * Finds the first icon to match one of the given matchers, from highest to lowest priority.
- *
- * @return The first match, or All Apps button if no match was found.
- */
- public View getFirstMatch(Predicate<ItemInfo>... matchers) {
- for (Predicate<ItemInfo> matcher : matchers) {
- for (int i = 0; i < getChildCount(); i++) {
- View item = getChildAt(i);
- if (!(item.getTag() instanceof ItemInfo)) {
- // Should only happen for All Apps button.
- // Will also happen for Recent/Running app icons. (Which have GroupTask as tags)
- continue;
- }
- ItemInfo info = (ItemInfo) item.getTag();
- if (matcher.test(info)) {
- return item;
- }
- }
- }
- return mAllAppsButtonContainer;
- }
-
- /**
* This method only works for bubble bar enabled in persistent task bar and the taskbar is start
* aligned.
*/
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java
index 066d4df..dcb9fbf 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java
@@ -16,7 +16,8 @@
package com.android.launcher3.taskbar;
-import static com.android.launcher3.Flags.taskbarRecentsLayoutTransition;
+import static android.window.DesktopModeFlags.ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION;
+
import static com.android.launcher3.config.FeatureFlags.enableTaskbarPinning;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_ALLAPPS_BUTTON_LONG_PRESS;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_ALLAPPS_BUTTON_TAP;
@@ -123,7 +124,7 @@
/** Callback invoked before Taskbar icons are laid out. */
void onPreLayoutChildren() {
- if (enableTaskbarPinning() && taskbarRecentsLayoutTransition()) {
+ if (enableTaskbarPinning() && ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION.isTrue()) {
mControllers.taskbarViewController.updateTaskbarIconTranslationXForPinning();
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 6ae13d4..6786aed 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -19,13 +19,14 @@
import static android.animation.LayoutTransition.CHANGE_APPEARING;
import static android.animation.LayoutTransition.CHANGE_DISAPPEARING;
import static android.animation.LayoutTransition.DISAPPEARING;
+import static android.window.DesktopModeFlags.ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION;
import static com.android.app.animation.Interpolators.EMPHASIZED;
import static com.android.app.animation.Interpolators.FINAL_FRAME;
import static com.android.app.animation.Interpolators.LINEAR;
+import static com.android.launcher3.BubbleTextView.LINE_INDICATOR_ANIM_DURATION;
import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
import static com.android.launcher3.Flags.taskbarOverflow;
-import static com.android.launcher3.Flags.taskbarRecentsLayoutTransition;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
@@ -138,6 +139,8 @@
private static final int TRANSITION_DEFAULT_DURATION = 500;
private static final int TRANSITION_FADE_IN_DURATION = 167;
private static final int TRANSITION_FADE_OUT_DURATION = 83;
+ private static final int APPEARING_LINE_INDICATOR_ANIM_DELAY =
+ TRANSITION_DEFAULT_DURATION - LINE_INDICATOR_ANIM_DURATION;
private final TaskbarActivityContext mActivity;
private @Nullable TaskbarDragLayerController mDragLayerController;
@@ -189,7 +192,7 @@
private final View.OnLayoutChangeListener mTaskbarViewLayoutChangeListener =
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
- if (!taskbarRecentsLayoutTransition()) {
+ if (!ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION.isTrue()) {
// update shiftX is handled with the animation at the end of the method
updateTaskbarIconTranslationXForPinning(/* updateShiftXForBubbleBar = */ false);
}
@@ -264,9 +267,8 @@
: mActivity.getDeviceProfile().taskbarHeight;
mTaskbarIconScaleForStash.updateValue(1f);
- float pinningValue = DisplayController.isTransientTaskbar(mActivity)
- ? PINNING_TRANSIENT
- : PINNING_PERSISTENT;
+ float pinningValue =
+ mActivity.isTransientTaskbar() ? PINNING_TRANSIENT : PINNING_PERSISTENT;
mTaskbarIconScaleForPinning.updateValue(pinningValue);
mTaskbarIconTranslationYForPinning.updateValue(pinningValue);
mTaskbarIconTranslationXForPinning.updateValue(pinningValue);
@@ -391,7 +393,6 @@
if (enableTaskbarPinning()) {
mTaskbarView.removeOnLayoutChangeListener(mTaskbarViewLayoutChangeListener);
}
- mTaskbarView.removeFolderIconListeners();
LauncherAppState.getInstance(mActivity).getModel().removeCallbacks(mModelCallbacks);
mActivity.removeOnDeviceProfileChangeListener(mDeviceProfileChangeListener);
}
@@ -736,7 +737,7 @@
public void updateIconViewsRunningStates() {
for (View iconView : getIconViews()) {
if (iconView instanceof BubbleTextView btv) {
- btv.updateRunningState(getRunningAppState(btv));
+ updateRunningState(btv);
if (shouldUpdateIconContentDescription(btv)) {
btv.setContentDescription(
btv.getContentDescription() + " " + btv.getIconStateDescription());
@@ -770,6 +771,10 @@
return pinnedAppsWithTasks;
}
+ private void updateRunningState(BubbleTextView btv) {
+ btv.updateRunningState(getRunningAppState(btv), mTaskbarView.getLayoutTransition() != null);
+ }
+
private BubbleTextView.RunningAppState getRunningAppState(BubbleTextView btv) {
Object tag = btv.getTag();
if (tag instanceof TaskItemInfo itemInfo) {
@@ -929,7 +934,7 @@
mOnControllerPreCreateCallback.run();
DeviceProfile taskbarDp = mActivity.getDeviceProfile();
Rect hotseatPadding = launcherDp.getHotseatLayoutPadding(mActivity);
- boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivity);
+ boolean isTransientTaskbar = mActivity.isTransientTaskbar();
float scaleUp = ((float) launcherDp.iconSizePx) / taskbarDp.taskbarIconSize;
int borderSpacing = launcherDp.hotseatBorderSpace;
@@ -1191,8 +1196,8 @@
* 3) All Apps button
*/
public View getFirstIconMatch(Predicate<ItemInfo> matcher) {
- Predicate<ItemInfo> collectionMatcher = ItemInfoMatcher.forFolderMatch(matcher);
- return mTaskbarView.getFirstMatch(matcher, collectionMatcher);
+ View icon = mModelCallbacks.getFirstMatch(matcher, ItemInfoMatcher.forFolderMatch(matcher));
+ return icon != null ? icon : mTaskbarView.getAllAppsButtonContainer();
}
/**
@@ -1206,7 +1211,8 @@
/** Called when there's a change in running apps to update the UI. */
public void commitRunningAppsToUI() {
mModelCallbacks.commitRunningAppsToUI();
- if (taskbarRecentsLayoutTransition() && mTaskbarView.getLayoutTransition() == null) {
+ if (ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION.isTrue()
+ && mTaskbarView.getLayoutTransition() == null) {
// Set up after the first commit so that the initial recents do not animate (janky).
mTaskbarView.setLayoutTransition(createLayoutTransitionForRunningApps());
}
@@ -1224,13 +1230,22 @@
view.setAlpha(0f);
view.setScaleX(0f);
view.setScaleY(0f);
+ if (view instanceof BubbleTextView btv) {
+ // Defer so that app is mostly scaled in before showing indicator.
+ btv.setLineIndicatorAnimStartDelay(APPEARING_LINE_INDICATOR_ANIM_DELAY);
+ }
+ } else if (type == DISAPPEARING && view instanceof BubbleTextView btv) {
+ // Running state updates happen after removing this view, so update it here.
+ updateRunningState(btv);
}
}
@Override
public void endTransition(
LayoutTransition transition, ViewGroup container, View view, int type) {
- // Do nothing.
+ if (type == APPEARING && view instanceof BubbleTextView btv) {
+ btv.setLineIndicatorAnimStartDelay(0);
+ }
}
});
diff --git a/quickstep/src/com/android/launcher3/taskbar/TypefaceUtils.kt b/quickstep/src/com/android/launcher3/taskbar/TypefaceUtils.kt
deleted file mode 100644
index e9c62d1..0000000
--- a/quickstep/src/com/android/launcher3/taskbar/TypefaceUtils.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.taskbar
-
-import android.graphics.Typeface
-import android.widget.TextView
-import com.android.launcher3.Flags
-
-/**
- * Helper util class to set pre-defined typefaces to textviews
- *
- * If the typeface font family is already defined here, you can just reuse it directly. Otherwise,
- * please define it here for future use. You do not need to define the font style. If you need
- * anything other than [Typeface.NORMAL], pass it inline when calling [setTypeface]
- */
-class TypefaceUtils {
-
- companion object {
- const val FONT_FAMILY_BODY_SMALL_BASELINE = "variable-body-small"
- const val FONT_FAMILY_BODY_MEDIUM_BASELINE = "variable-body-medium"
- const val FONT_FAMILY_BODY_LARGE_BASELINE = "variable-body-large"
- const val FONT_FAMILY_LABEL_LARGE_BASELINE = "variable-label-large"
- const val FONT_FAMILY_DISPLAY_SMALL_EMPHASIZED = "variable-display-small-emphasized"
- const val FONT_FAMILY_DISPLAY_MEDIUM_EMPHASIZED = "variable-display-medium-emphasized"
- const val FONT_FAMILY_HEADLINE_SMALL_EMPHASIZED = "variable-headline-small-emphasized"
- const val FONT_FAMILY_HEADLINE_LARGE_EMPHASIZED = "variable-headline-large-emphasized"
-
- @JvmStatic
- @JvmOverloads
- fun setTypeface(
- textView: TextView?,
- fontFamilyName: String,
- fontStyle: Int = Typeface.NORMAL,
- ) {
- if (!Flags.expressiveThemeInTaskbarAndNavigation()) return
- textView?.typeface = Typeface.create(fontFamilyName, fontStyle)
- }
- }
-}
diff --git a/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt b/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
index c380c8d..c7d42b1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
@@ -22,7 +22,6 @@
import android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION
import android.view.WindowManager
import android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
-import com.android.launcher3.util.DisplayController
import com.android.launcher3.views.BaseDragLayer
import com.android.systemui.animation.ViewRootSync
import java.io.PrintWriter
@@ -41,8 +40,7 @@
class VoiceInteractionWindowController(val context: TaskbarActivityContext) :
TaskbarControllers.LoggableTaskbarController, TaskbarControllers.BackgroundRendererController {
- private val isSeparateBackgroundEnabled =
- !DisplayController.isTransientTaskbar(context) && !context.isPhoneMode
+ private val isSeparateBackgroundEnabled = !context.isTransientTaskbar && !context.isPhoneMode
private val taskbarBackgroundRenderer = TaskbarBackgroundRenderer(context)
private val nonTouchableInsetsComputer =
ViewTreeObserver.OnComputeInternalInsetsListener {
@@ -97,7 +95,7 @@
separateWindowLayoutParams =
context.createDefaultWindowLayoutParams(
TYPE_APPLICATION_OVERLAY,
- TEMP_BACKGROUND_WINDOW_TITLE
+ TEMP_BACKGROUND_WINDOW_TITLE,
)
separateWindowLayoutParams?.isSystemApplicationOverlay = true
}
@@ -163,7 +161,7 @@
// First add the temporary window, then hide the overlapping taskbar background.
context.addWindowView(
separateWindowForTaskbarBackground,
- separateWindowLayoutParams
+ separateWindowLayoutParams,
);
{ controllers.taskbarDragLayerController.setIsBackgroundDrawnElsewhere(true) }
} else {
@@ -179,7 +177,7 @@
ViewRootSync.synchronizeNextDraw(
separateWindowForTaskbarBackground!!,
context.dragLayer,
- onWindowsSynchronized
+ onWindowsSynchronized,
)
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
index 5830095..916b28e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
@@ -15,7 +15,9 @@
*/
package com.android.launcher3.taskbar.allapps;
+import static com.android.app.animation.Interpolators.DECELERATED_EASE;
import static com.android.app.animation.Interpolators.EMPHASIZED;
+import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.launcher3.touch.AllAppsSwipeController.ALL_APPS_FADE_MANUAL;
import static com.android.launcher3.touch.AllAppsSwipeController.SCRIM_FADE_MANUAL;
@@ -35,6 +37,7 @@
import com.android.app.animation.Interpolators;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Flags;
import com.android.launcher3.Insettable;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatorListeners;
@@ -48,9 +51,11 @@
public class TaskbarAllAppsSlideInView extends AbstractSlideInView<TaskbarOverlayContext>
implements Insettable, DeviceProfile.OnDeviceProfileChangeListener {
private final Handler mHandler;
+ private final int mMaxBlurRadius;
private TaskbarAllAppsContainerView mAppsView;
private float mShiftRange;
+ private int mBlurRadius;
private @Nullable Runnable mShowOnFullyAttachedToWindowRunnable;
// Initialized in init.
@@ -64,6 +69,8 @@
int defStyleAttr) {
super(context, attrs, defStyleAttr);
mHandler = new Handler(Looper.myLooper());
+ mMaxBlurRadius = getResources().getDimensionPixelSize(
+ R.dimen.max_depth_blur_radius_enhanced);
}
void init(TaskbarAllAppsCallbacks callbacks) {
@@ -99,6 +106,7 @@
if (!animate) {
mAllAppsCallbacks.onAllAppsTransitionEnd(true);
setTranslationShift(TRANSLATION_SHIFT_OPENED);
+ mBlurRadius = mMaxBlurRadius;
return;
}
@@ -123,6 +131,16 @@
animation.setViewAlpha(mAppsView, 1 - mToTranslationShift, allAppsFadeInterpolator);
}
+ if (Flags.allAppsBlur()) {
+ Interpolator blurInterpolator = isOpening ? LINEAR : DECELERATED_EASE;
+ animation.addOnFrameListener(a -> {
+ float blurProgress =
+ isOpening ? a.getAnimatedFraction() : 1 - a.getAnimatedFraction();
+ mBlurRadius =
+ (int) (mMaxBlurRadius * blurInterpolator.getInterpolation(blurProgress));
+ });
+ }
+
mAllAppsCallbacks.onAllAppsAnimationPending(animation, isOpening);
}
@@ -219,6 +237,7 @@
// to pass extra bottom offset to background scrim to fill the bottom gap during predictive
// back swipe.
mAppsView.drawOnScrimWithBottomOffset(canvas, getBottomOffsetPx());
+ mActivityContext.getOverlayController().setBackgroundBlurRadius(mBlurRadius);
super.dispatchDraw(canvas);
}
@@ -230,9 +249,13 @@
@Override
protected int getScrimColor(Context context) {
- return mActivityContext.getDeviceProfile().isPhone
- ? Themes.getAttrColor(context, R.attr.allAppsScrimColor)
- : context.getColor(R.color.widgets_picker_scrim);
+ if (!mActivityContext.getDeviceProfile().shouldShowAllAppsOnSheet()) {
+ return Themes.getAttrColor(context, R.attr.allAppsScrimColor);
+ }
+ if (Flags.allAppsBlur()) {
+ return Themes.getAttrColor(context, R.attr.allAppsScrimColorOverBlur);
+ }
+ return context.getResources().getColor(R.color.widgets_picker_scrim);
}
@Override
@@ -254,6 +277,7 @@
public void onDeviceProfileChanged(DeviceProfile dp) {
setShiftRange(dp.allAppsShiftRange);
setTranslationShift(TRANSLATION_SHIFT_OPENED);
+ mBlurRadius = mMaxBlurRadius;
}
private void setShiftRange(float shiftRange) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
index 52f7176..f1ccd39 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
@@ -31,7 +31,6 @@
import com.android.launcher3.taskbar.TaskbarStashController;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayController;
-import com.android.launcher3.util.DisplayController;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import java.util.Optional;
@@ -90,7 +89,7 @@
}
private void setUpTaskbarStashing() {
- if (DisplayController.isTransientTaskbar(mContext)) {
+ if (mContext.isTransientTaskbar()) {
mTaskbarStashController.updateStateForFlag(FLAG_STASHED_IN_TASKBAR_ALL_APPS, true);
mTaskbarStashController.applyState();
}
@@ -103,7 +102,7 @@
AbstractFloatingView.closeOpenContainer(
mContext, AbstractFloatingView.TYPE_ACTION_POPUP);
- if (DisplayController.isTransientTaskbar(mContext)) {
+ if (mContext.isTransientTaskbar()) {
mTaskbarStashController.updateStateForFlag(FLAG_STASHED_IN_TASKBAR_ALL_APPS, false);
mTaskbarStashController.applyState();
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarLocationDropTarget.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarLocationDropTarget.kt
index 383f4d2..24e7d99 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarLocationDropTarget.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarLocationDropTarget.kt
@@ -60,8 +60,6 @@
override fun onDrop(dragObject: DropTarget.DragObject, options: DragOptions) {
val itemInfo = dragObject.dragInfo ?: return
- // TODO(b/397459664) : fix task bar icon animation after drop
- // TODO(b/397459664) : update bubble bar location
bubbleBarDragListener.onLauncherItemDroppedOverBubbleBarDragZone(
bubbleBarLocation,
itemInfo,
@@ -77,8 +75,6 @@
}
override fun onDragExit(dragObject: DropTarget.DragObject) {
- // TODO(b/397459664) : fix the issue for no bubbles, when moving task bar icon out of
- // the bubble bar drag zone drag ends and swipes gesture swipes the overview
if (!isShowingDropTarget) return
isShowingDropTarget = false
bubbleBarDragListener.onLauncherItemDraggedOutsideBubbleBarDropZone()
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index 1f5c541..7fb6480 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -41,15 +41,16 @@
import androidx.annotation.Nullable;
import com.android.app.animation.Interpolators;
+import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
+import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarControllers;
-import com.android.launcher3.taskbar.TaskbarDragController;
import com.android.launcher3.taskbar.TaskbarInsetsController;
import com.android.launcher3.taskbar.TaskbarSharedState;
import com.android.launcher3.taskbar.TaskbarStashController;
@@ -126,7 +127,6 @@
});
private final BubbleBarDragListener mDragListener = new BubbleBarDragListener() {
- @NonNull
@Override
public void getBubbleBarLocationHitRect(@NonNull BubbleBarLocation bubbleBarLocation,
Rect outRect) {
@@ -145,32 +145,32 @@
@Override
public void onLauncherItemDroppedOverBubbleBarDragZone(@NonNull BubbleBarLocation location,
@NonNull ItemInfo itemInfo) {
- //TODO(b/397459664) : fix drag interruption when there are no bubbles
- //TODO(b/397459664) : update bubble bar location
- ShortcutInfo shortcutInfo = null;
+ AbstractFloatingView.closeAllOpenViews(mActivity);
if (itemInfo instanceof WorkspaceItemInfo) {
- shortcutInfo = ((WorkspaceItemInfo) itemInfo).getDeepShortcutInfo();
+ ShortcutInfo shortcutInfo = ((WorkspaceItemInfo) itemInfo).getDeepShortcutInfo();
+ if (shortcutInfo != null) {
+ mSystemUiProxy.showShortcutBubble(shortcutInfo, location);
+ return;
+ }
}
Intent itemIntent = itemInfo.getIntent();
- SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.get(mActivity);
- if (shortcutInfo != null) {
- systemUiProxy.showShortcutBubble(shortcutInfo);
- } else if (itemIntent != null && itemIntent.getComponent() != null) {
- systemUiProxy.showAppBubble(itemIntent, itemInfo.user);
+ if (itemIntent != null && itemIntent.getComponent() != null) {
+ itemIntent.setPackage(itemIntent.getComponent().getPackageName());
+ mSystemUiProxy.showAppBubble(itemIntent, itemInfo.user, location);
}
}
@Override
public void onLauncherItemDraggedOutsideBubbleBarDropZone() {
- //TODO(b/397459664) : hide expanded view drop target
onItemDraggedOutsideBubbleBarDropZone();
+ mSystemUiProxy.showBubbleDropTarget(/* show = */ false);
}
@Override
public void onLauncherItemDraggedOverBubbleBarDragZone(
@NonNull BubbleBarLocation location) {
- //TODO(b/397459664) : show expanded view drop target
onDragItemOverBubbleBarDragZone(location);
+ mSystemUiProxy.showBubbleDropTarget(/* show = */ true, location);
}
@NonNull
@@ -195,13 +195,13 @@
private boolean mShouldShowEducation;
public boolean mOverflowAdded;
private boolean mIsLocationUpdatedForDropTarget = false;
+ private boolean mWasStashedBeforeEnteringBubbleDragZone = false;
private BubbleBarViewAnimator mBubbleBarViewAnimator;
private final FrameLayout mBubbleBarContainer;
private BubbleBarFlyoutController mBubbleBarFlyoutController;
private BubbleBarPinController mBubbleBarPinController;
private TaskbarSharedState mTaskbarSharedState;
- private TaskbarDragController mTaskbarDragController;
private final BubbleBarLocationDropTarget mBubbleBarLeftDropTarget;
private final BubbleBarLocationDropTarget mBubbleBarRightDropTarget;
private final TimeSource mTimeSource = System::currentTimeMillis;
@@ -238,7 +238,6 @@
/** Initializes controller. */
public void init(TaskbarControllers controllers, BubbleControllers bubbleControllers,
TaskbarViewPropertiesProvider taskbarViewPropertiesProvider) {
- mTaskbarDragController = controllers.taskbarDragController;
mTaskbarSharedState = controllers.getSharedState();
mBubbleStashController = bubbleControllers.bubbleStashController;
mBubbleBarController = bubbleControllers.bubbleBarController;
@@ -275,7 +274,7 @@
mBoundsChangeListener.onBoundsChanged();
}
});
- float pinningValue = DisplayController.isTransientTaskbar(mActivity)
+ float pinningValue = mActivity.isTransientTaskbar()
? PINNING_TRANSIENT
: PINNING_PERSISTENT;
mBubbleBarPinning.updateValue(pinningValue);
@@ -340,8 +339,18 @@
mBubbleBarController.updateBubbleBarLocation(location, source);
}
};
- mTaskbarDragController.addDropTarget(mBubbleBarLeftDropTarget);
- mTaskbarDragController.addDropTarget(mBubbleBarRightDropTarget);
+ }
+
+ /** Adds bubble bar locations drop zones to the drag controller. */
+ public void addBubbleBarDropTargets(DragController<?> dragController) {
+ dragController.addDropTarget(mBubbleBarLeftDropTarget);
+ dragController.addDropTarget(mBubbleBarRightDropTarget);
+ }
+
+ /** Removes bubble bar locations drop zones to the drag controller. */
+ public void removeBubbleBarDropTargets(DragController<?> dragController) {
+ dragController.removeDropTarget(mBubbleBarLeftDropTarget);
+ dragController.removeDropTarget(mBubbleBarRightDropTarget);
}
/** Returns animated float property responsible for pinning transition animation. */
@@ -619,14 +628,30 @@
* updated.
*/
public void onDragItemOverBubbleBarDragZone(@NonNull BubbleBarLocation bubbleBarLocation) {
+ Log.w("BBAnimation", "onDragItemOverBubbleBarDragZone()");
mBarView.showDropTarget(/* isDropTarget = */ true);
boolean isRtl = mBarView.isLayoutRtl();
mIsLocationUpdatedForDropTarget = getBubbleBarLocation().isOnLeft(isRtl)
!= bubbleBarLocation.isOnLeft(isRtl);
- if (mIsLocationUpdatedForDropTarget) {
- animateBubbleBarLocation(bubbleBarLocation);
- }
- if (!hasBubbles()) {
+ mWasStashedBeforeEnteringBubbleDragZone = hasBubbles()
+ && mBubbleStashController.isStashed();
+ if (mWasStashedBeforeEnteringBubbleDragZone) {
+ if (mIsLocationUpdatedForDropTarget) {
+ // bubble bar is stashed an location updated
+ //TODO(b/399678274) add animation
+ mBubbleStashController.showBubbleBarImmediate();
+ animateBubbleBarLocation(bubbleBarLocation);
+ } else {
+ // bubble bar is stashed and location the same - just un-stash
+ mBubbleStashController.showBubbleBar(/* expandBubbles = */ false);
+ }
+ } else if (hasBubbles()) {
+ if (mIsLocationUpdatedForDropTarget) {
+ // bubble bar has bubbles and location is changed - animate bar to the opposite side
+ animateBubbleBarLocation(bubbleBarLocation);
+ }
+ } else {
+ // bubble bar has no bubbles flow just show the empty drop target
mBubbleBarPinController.showDropTarget(bubbleBarLocation);
}
}
@@ -646,12 +671,27 @@
* mode and reset the value returned from {@link #isLocationUpdatedForDropTarget()} to false.
*/
public void onItemDraggedOutsideBubbleBarDropZone() {
+ Log.w("BBAnimation", "onItemDraggedOutsideBubbleBarDropZone()");
mBarView.showDropTarget(/* isDropTarget = */ false);
- if (mIsLocationUpdatedForDropTarget) {
- animateBubbleBarLocation(getBubbleBarLocation());
+ if (mWasStashedBeforeEnteringBubbleDragZone) {
+ if (mIsLocationUpdatedForDropTarget) {
+ // bubble bar was stashed and location updated
+ //TODO(b/399678274) add animation
+ animateBubbleBarLocation(getBubbleBarLocation());
+ mBubbleStashController.stashBubbleBarImmediate();
+ } else {
+ // bubble bar was stashed and location the same - just stash it back
+ mBubbleStashController.stashBubbleBar();
+ }
+ } else if (hasBubbles()) {
+ if (mIsLocationUpdatedForDropTarget) {
+ // bubble bar has bubbles and location was changed - return to the original location
+ animateBubbleBarLocation(getBubbleBarLocation());
+ }
}
mBubbleBarPinController.hideDropTarget();
mIsLocationUpdatedForDropTarget = false;
+ mWasStashedBeforeEnteringBubbleDragZone = false;
}
/**
@@ -659,9 +699,11 @@
* The controller will hide the drop target if there are no bubbles and exit drop target mode.
*/
public void onItemDroppedInBubbleBarDragZone() {
+ Log.w("BBAnimation", "onItemDroppedInBubbleBarDragZone()");
mBarView.showDropTarget(/* isDropTarget = */ false);
mBubbleBarPinController.hideDropTarget();
mIsLocationUpdatedForDropTarget = false;
+ mWasStashedBeforeEnteringBubbleDragZone = false;
}
/**
@@ -1358,8 +1400,19 @@
/** Called when the controller is destroyed. */
public void onDestroy() {
adjustTaskbarAndHotseatToBubbleBarState(/*isBubbleBarExpanded = */false);
- mTaskbarDragController.removeDropTarget(mBubbleBarLeftDropTarget);
- mTaskbarDragController.removeDropTarget(mBubbleBarRightDropTarget);
+ }
+
+ /**
+ * Removes the bubble from the bubble bar and notifies sysui that the bubble should move to
+ * full screen.
+ */
+ public void moveDraggedBubbleToFullscreen(@NonNull BubbleView bubbleView, Point dropLocation) {
+ if (bubbleView.getBubble() == null) {
+ return;
+ }
+ String key = bubbleView.getBubble().getKey();
+ mSystemUiProxy.moveDraggedBubbleToFullscreen(key, dropLocation);
+ onBubbleDismissed(bubbleView);
}
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
index 0abd88c..3df10c0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
@@ -16,11 +16,14 @@
package com.android.launcher3.taskbar.bubbles;
import android.annotation.SuppressLint;
+import android.graphics.Point;
import android.graphics.PointF;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -28,7 +31,16 @@
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.wm.shell.shared.bubbles.BaseBubblePinController.LocationChangeListener;
+import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.DeviceConfig;
+import com.android.wm.shell.shared.bubbles.DragZone;
+import com.android.wm.shell.shared.bubbles.DragZoneFactory;
+import com.android.wm.shell.shared.bubbles.DragZoneFactory.DesktopWindowModeChecker;
+import com.android.wm.shell.shared.bubbles.DragZoneFactory.SplitScreenModeChecker;
+import com.android.wm.shell.shared.bubbles.DraggedObject;
+import com.android.wm.shell.shared.bubbles.DropTargetManager;
+import com.android.wm.shell.shared.bubbles.DropTargetManager.DragZoneChangedListener;
/**
* Controls bubble bar drag interactions.
@@ -76,11 +88,36 @@
private BubbleDismissController mBubbleDismissController;
private BubbleBarPinController mBubbleBarPinController;
private BubblePinController mBubblePinController;
+ private final DropTargetManager mDropTargetManager;
+ private final DragZoneFactory mDragZoneFactory;
+ private final BubbleDragZoneChangedListener mBubbleDragZoneChangedListener;
private boolean mIsDragging;
- public BubbleDragController(TaskbarActivityContext activity) {
+ public BubbleDragController(TaskbarActivityContext activity, FrameLayout dropTargetParent) {
mActivity = activity;
+ WindowManager windowManager =
+ mActivity.getApplicationContext().getSystemService(WindowManager.class);
+ DeviceConfig deviceConfig =
+ DeviceConfig.create(mActivity.getApplicationContext(), windowManager);
+ SplitScreenModeChecker splitScreenModeChecker = new SplitScreenModeChecker() {
+ @NonNull
+ @Override
+ public SplitScreenMode getSplitScreenMode() {
+ return SplitScreenMode.NONE;
+ }
+ };
+ DesktopWindowModeChecker desktopWindowModeChecker = new DesktopWindowModeChecker() {
+ @Override
+ public boolean isSupported() {
+ return false;
+ }
+ };
+ mDragZoneFactory = new DragZoneFactory(mActivity.getApplicationContext(), deviceConfig,
+ splitScreenModeChecker, desktopWindowModeChecker);
+ mBubbleDragZoneChangedListener = new BubbleDragZoneChangedListener();
+ mDropTargetManager = new DropTargetManager(mActivity.getApplicationContext(),
+ dropTargetParent, mBubbleDragZoneChangedListener);
}
/**
@@ -130,47 +167,90 @@
}
};
+ private BubbleBarLocation getBubbleBarLocationDuringDrag() {
+ return BubbleAnythingFlagHelper.enableBubbleToFullscreen()
+ ? mBubbleDragZoneChangedListener.mBubbleBarLocation
+ : mReleasedLocation;
+ }
+
@Override
void onDragStart() {
- mBubblePinController.setListener(mLocationChangeListener);
mBubbleBarViewController.onBubbleDragStart(bubbleView);
- mBubblePinController.onDragStart(
- mBubbleBarViewController.getBubbleBarLocation().isOnLeft(
- bubbleView.isLayoutRtl()));
+ if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
+ DraggedObject.Bubble draggedBubble =
+ new DraggedObject.Bubble(
+ mBubbleBarViewController.getBubbleBarLocation());
+ mDropTargetManager.onDragStarted(draggedBubble,
+ mDragZoneFactory.createSortedDragZones(draggedBubble));
+ } else {
+ mBubblePinController.setListener(mLocationChangeListener);
+ mBubblePinController.onDragStart(
+ mBubbleBarViewController.getBubbleBarLocation().isOnLeft(
+ bubbleView.isLayoutRtl()));
+ }
}
@Override
protected void onDragUpdate(float x, float y, float newTx, float newTy) {
bubbleView.setDragTranslationX(newTx);
bubbleView.setTranslationY(newTy);
- mBubblePinController.onDragUpdate(x, y);
+ if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
+ mDropTargetManager.onDragUpdated((int) x, (int) y);
+ } else {
+ mBubblePinController.onDragUpdate(x, y);
+ }
}
@Override
protected void onDragRelease() {
- mBubblePinController.onDragEnd();
- mBubbleBarViewController.onBubbleDragRelease(mReleasedLocation);
+ if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
+ mDropTargetManager.onDragEnded();
+ if (!mBubbleDragZoneChangedListener.isDraggedToFullscreen()) {
+ // TODO b/393173014: check for desktop window and split once they're
+ // implemented. this notifies wm shell that the dragged bubble was
+ // released so that we can show the expanded view. we only want to show it
+ // after releasing in a Bubble zone. But Split and Desktop Window aren't
+ // implemented yet, so we only check for full screen for now.
+ mBubbleBarViewController.onBubbleDragRelease(
+ getBubbleBarLocationDuringDrag());
+ }
+ } else {
+ mBubblePinController.onDragEnd();
+ mBubbleBarViewController.onBubbleDragRelease(getBubbleBarLocationDuringDrag());
+ }
}
@Override
protected void onDragDismiss() {
- mBubblePinController.onDragEnd();
+ if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
+ mDropTargetManager.onDragEnded();
+ } else {
+ mBubblePinController.onDragEnd();
+ }
mBubbleBarViewController.onBubbleDismissed(bubbleView);
mBubbleBarViewController.onBubbleDragEnd();
}
@Override
- void onDragEnd() {
- mBubbleBarController.updateBubbleBarLocation(mReleasedLocation,
+ void onDragEnd(float x, float y) {
+ mBubbleBarController.updateBubbleBarLocation(getBubbleBarLocationDuringDrag(),
BubbleBarLocation.UpdateSource.DRAG_BUBBLE);
+ if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
+ mDropTargetManager.onDragEnded();
+ if (mBubbleDragZoneChangedListener.isDraggedToFullscreen()) {
+ mBubbleBarViewController.moveDraggedBubbleToFullscreen(
+ bubbleView, new Point((int) x, (int) y));
+ }
+ } else {
+ mBubblePinController.setListener(null);
+ }
mBubbleBarViewController.onBubbleDragEnd();
- mBubblePinController.setListener(null);
}
@Override
protected PointF getRestingPosition() {
return mBubbleBarViewController.getDraggedBubbleReleaseTranslation(
- getInitialPosition(), mReleasedLocation);
+ getInitialPosition(), getBubbleBarLocationDuringDrag());
}
});
}
@@ -188,6 +268,12 @@
private final LocationChangeListener mLocationChangeListener =
location -> mReleasedLocation = location;
+ private BubbleBarLocation getBubbleBarLocationDuringDrag() {
+ return BubbleAnythingFlagHelper.enableBubbleToFullscreen()
+ ? mBubbleDragZoneChangedListener.mBubbleBarLocation
+ : mReleasedLocation;
+ }
+
@Override
protected boolean onTouchDown(@NonNull View view, @NonNull MotionEvent event) {
if (bubbleBarView.isExpanded()) return false;
@@ -196,50 +282,74 @@
@Override
void onDragStart() {
- mBubbleBarPinController.setListener(mLocationChangeListener);
initialRelativePivot.set(bubbleBarView.getRelativePivotX(),
bubbleBarView.getRelativePivotY());
// By default the bubble bar view pivot is in bottom right corner, while dragging
// it should be centered in order to align it with the dismiss target view
bubbleBarView.setRelativePivot(/* x = */ 0.5f, /* y = */ 0.5f);
bubbleBarView.setIsDragging(true);
- mBubbleBarPinController.onDragStart(
- bubbleBarView.getBubbleBarLocation().isOnLeft(bubbleBarView.isLayoutRtl()));
+ if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
+ DraggedObject.BubbleBar draggedBubbleBar = new DraggedObject.BubbleBar(
+ mBubbleBarViewController.getBubbleBarLocation());
+ mDropTargetManager.onDragStarted(draggedBubbleBar,
+ mDragZoneFactory.createSortedDragZones(draggedBubbleBar));
+ } else {
+ mBubbleBarPinController.setListener(mLocationChangeListener);
+ mBubbleBarPinController.onDragStart(
+ bubbleBarView.getBubbleBarLocation().isOnLeft(
+ bubbleBarView.isLayoutRtl()));
+ }
}
@Override
protected void onDragUpdate(float x, float y, float newTx, float newTy) {
bubbleBarView.setTranslationX(newTx);
bubbleBarView.setTranslationY(newTy);
- mBubbleBarPinController.onDragUpdate(x, y);
+ if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
+ mDropTargetManager.onDragUpdated((int) x, (int) y);
+ } else {
+ mBubbleBarPinController.onDragUpdate(x, y);
+ }
}
@Override
protected void onDragRelease() {
- mBubbleBarPinController.onDragEnd();
+ if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
+ mDropTargetManager.onDragEnded();
+ } else {
+ mBubbleBarPinController.onDragEnd();
+ }
}
@Override
protected void onDragDismiss() {
- mBubbleBarPinController.onDragEnd();
+ if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
+ mDropTargetManager.onDragEnded();
+ } else {
+ mBubbleBarPinController.onDragEnd();
+ }
}
@Override
- void onDragEnd() {
+ void onDragEnd(float x, float y) {
// Make sure to update location as the first thing. Pivot update causes a relayout
- mBubbleBarController.updateBubbleBarLocation(mReleasedLocation,
+ mBubbleBarController.updateBubbleBarLocation(getBubbleBarLocationDuringDrag(),
BubbleBarLocation.UpdateSource.DRAG_BAR);
bubbleBarView.setIsDragging(false);
// Restoring the initial pivot for the bubble bar view
bubbleBarView.setRelativePivot(initialRelativePivot.x, initialRelativePivot.y);
mBubbleBarViewController.onBubbleBarDragEnd();
- mBubbleBarPinController.setListener(null);
+ if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
+ mDropTargetManager.onDragEnded();
+ } else {
+ mBubbleBarPinController.setListener(null);
+ }
}
@Override
protected PointF getRestingPosition() {
return mBubbleBarViewController.getBubbleBarDragReleaseTranslation(
- getInitialPosition(), mReleasedLocation);
+ getInitialPosition(), getBubbleBarLocationDuringDrag());
}
});
}
@@ -320,7 +430,7 @@
/**
* Called when the dragging interaction has ended and all the animations have completed
*/
- abstract void onDragEnd();
+ abstract void onDragEnd(float x, float y);
/**
* Called when the dragged bubble is released outside of the dismiss target area and will
@@ -471,7 +581,7 @@
Runnable onComplete = () -> {
mActivity.setTaskbarWindowFullscreen(false);
cleanUp(view);
- onDragEnd();
+ onDragEnd(event.getRawX(), event.getRawY());
};
if (mBubbleDismissController.handleTouchEvent(event)) {
@@ -479,8 +589,17 @@
mAnimator.animateDismiss(mViewInitialPosition, onComplete);
} else {
onDragRelease();
- mAnimator.animateToRestingState(getRestingPosition(), getCurrentVelocity(),
+ if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
+ if (mBubbleDragZoneChangedListener.isDraggedToFullscreen()) {
+ onComplete.run();
+ } else {
+ mAnimator.animateToRestingState(getRestingPosition(), getCurrentVelocity(),
+ onComplete);
+ }
+ } else {
+ mAnimator.animateToRestingState(getRestingPosition(), getCurrentVelocity(),
onComplete);
+ }
}
mBubbleDismissController.hideDismissView();
}
@@ -520,4 +639,46 @@
return new PointF(mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
}
}
+
+ private class BubbleDragZoneChangedListener implements DragZoneChangedListener {
+
+ private BubbleBarLocation mBubbleBarLocation = BubbleBarLocation.DEFAULT;
+ private DragZone mDragZone;
+
+ boolean isDraggedToFullscreen() {
+ return mDragZone instanceof DragZone.FullScreen;
+ }
+
+ @Override
+ public void onInitialDragZoneSet(@NonNull DragZone dragZone) {
+ mDragZone = dragZone;
+ if (dragZone instanceof DragZone.Bubble.Left) {
+ mBubbleBarLocation = BubbleBarLocation.LEFT;
+ } else if (dragZone instanceof DragZone.Bubble.Right) {
+ mBubbleBarLocation = BubbleBarLocation.RIGHT;
+ }
+ }
+
+ @Override
+ public void onDragZoneChanged(@NonNull DraggedObject draggedObject, @NonNull DragZone from,
+ @NonNull DragZone to) {
+ mDragZone = to;
+ if (to instanceof DragZone.Bubble.Left
+ && mBubbleBarLocation != BubbleBarLocation.LEFT) {
+ if (draggedObject instanceof DraggedObject.Bubble) {
+ mBubbleBarController.animateBubbleBarLocation(BubbleBarLocation.LEFT);
+ }
+ mBubbleBarLocation = BubbleBarLocation.LEFT;
+ } else if (to instanceof DragZone.Bubble.Right
+ && mBubbleBarLocation != BubbleBarLocation.RIGHT) {
+ if (draggedObject instanceof DraggedObject.Bubble) {
+ mBubbleBarController.animateBubbleBarLocation(BubbleBarLocation.RIGHT);
+ }
+ mBubbleBarLocation = BubbleBarLocation.RIGHT;
+ }
+ }
+
+ @Override
+ public void onDragEnded(@NonNull DragZone zone) {}
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt
index 30cfafe..0da8c1f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt
@@ -215,6 +215,7 @@
animator.spring(DynamicAnimation.TRANSLATION_Y, totalTranslationY, initialVelocity ?: 0f)
animator.addUpdateListener { handle, values ->
val ty = values[DynamicAnimation.TRANSLATION_Y]?.value ?: return@addUpdateListener
+ if (animatingBubble == null) return@addUpdateListener
when {
ty >= stashedHandleTranslationYForAnimation -> {
// we're in the first leg of the animation. only animate the handle. the bubble
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt
index 75bf937..ac87b5e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt
@@ -36,6 +36,8 @@
import androidx.core.animation.ArgbEvaluator
import com.android.launcher3.R
import com.android.launcher3.popup.RoundedArrowDrawable
+import com.android.wm.shell.shared.TypefaceUtils
+import com.android.wm.shell.shared.TypefaceUtils.Companion.setTypeface
import kotlin.math.min
/** The flyout view used to notify the user of a new bubble notification. */
@@ -163,6 +165,9 @@
LayoutInflater.from(context).inflate(R.layout.bubblebar_flyout, this, true)
id = R.id.bubble_bar_flyout_view
+ setTypeface(title, TypefaceUtils.FontFamily.GSF_LABEL_LARGE)
+ setTypeface(message, TypefaceUtils.FontFamily.GSF_BODY_MEDIUM)
+
val ta = context.obtainStyledAttributes(intArrayOf(android.R.attr.dialogCornerRadius))
cornerRadius = ta.getDimensionPixelSize(0, 0).toFloat()
ta.recycle()
diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt
index 4932654..bb2acd6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt
@@ -32,7 +32,6 @@
import com.android.launcher3.config.FeatureFlags.enableTaskbarPinning
import com.android.launcher3.taskbar.TaskbarActivityContext
import com.android.launcher3.taskbar.TaskbarViewCallbacks
-import com.android.launcher3.util.DisplayController
import com.android.launcher3.util.Executors.MAIN_EXECUTOR
import com.android.launcher3.views.ActivityContext
import com.android.launcher3.views.IconButtonView
@@ -69,7 +68,7 @@
)
backgroundTintList = ColorStateList.valueOf(TRANSPARENT)
setIconDrawable(drawable)
- if (!DisplayController.isTransientTaskbar(context)) {
+ if (!activityContext.isTransientTaskbar) {
setPadding(dpToPx(activityContext.taskbarSpecsEvaluator.taskbarIconPadding.toFloat()))
}
setForegroundTint(activityContext.getColor(R.color.all_apps_button_color))
diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt
index d5f72d5..060ce46 100644
--- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt
@@ -26,7 +26,6 @@
import com.android.launcher3.Utilities.dpToPx
import com.android.launcher3.taskbar.TaskbarActivityContext
import com.android.launcher3.taskbar.TaskbarViewCallbacks
-import com.android.launcher3.util.DisplayController
import com.android.launcher3.views.ActivityContext
import com.android.launcher3.views.IconButtonView
@@ -52,7 +51,7 @@
backgroundTintList = ColorStateList.valueOf(TRANSPARENT)
val drawable = resources.getDrawable(R.drawable.taskbar_divider_button)
setIconDrawable(drawable)
- if (!DisplayController.isTransientTaskbar(context)) {
+ if (!activityContext.isTransientTaskbar) {
setPadding(dpToPx(activityContext.taskbarSpecsEvaluator.taskbarIconPadding.toFloat()))
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarFeatureEvaluator.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarFeatureEvaluator.kt
index f130d29..a14c5a4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarFeatureEvaluator.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarFeatureEvaluator.kt
@@ -19,7 +19,6 @@
import com.android.launcher3.Flags.enableRecentsInTaskbar
import com.android.launcher3.config.FeatureFlags.enableTaskbarPinning
import com.android.launcher3.taskbar.TaskbarActivityContext
-import com.android.launcher3.util.DisplayController
/** Evaluates all the features taskbar can have. */
class TaskbarFeatureEvaluator
@@ -36,7 +35,7 @@
get() = enableTaskbarPinning() || isRecentsEnabled
val isTransient: Boolean
- get() = DisplayController.isTransientTaskbar(taskbarActivityContext)
+ get() = taskbarActivityContext.isTransientTaskbar
val isLandscape: Boolean
get() = taskbarActivityContext.deviceProfile.isLandscape
diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarSpecsEvaluator.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarSpecsEvaluator.kt
index 822ca64..f1ed6c5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarSpecsEvaluator.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarSpecsEvaluator.kt
@@ -26,6 +26,8 @@
numColumns: Int = taskbarActivityContext.deviceProfile.inv.numColumns,
) {
var taskbarIconSize: TaskbarIconSize = getIconSizeByGrid(numColumns, numRows)
+ val numShownHotseatIcons
+ get() = taskbarActivityContext.deviceProfile.numShownHotseatIcons
// TODO(b/341146605) : initialize it to taskbar container in later cl.
private var taskbarContainer: List<TaskbarContainer> = emptyList()
diff --git a/quickstep/src/com/android/launcher3/taskbar/growth/GrowthConstants.java b/quickstep/src/com/android/launcher3/taskbar/growth/GrowthConstants.java
index 78ef152..7d760fc 100644
--- a/quickstep/src/com/android/launcher3/taskbar/growth/GrowthConstants.java
+++ b/quickstep/src/com/android/launcher3/taskbar/growth/GrowthConstants.java
@@ -24,5 +24,13 @@
*/
public static final String BROADCAST_SHOW_NUDGE =
"com.android.launcher3.growth.BROADCAST_SHOW_NUDGE";
+
+ /**
+ * For filtering package of broadcast intent received.
+ */
+ public static final String GROWTH_NUDGE_PERMISSION =
+ "com.android.growth.permission.GROWTH_NUDGE_PERMISSION"
+ + " android:protectionLevel=\"signature|preinstalled\"";
+
private GrowthConstants() {}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/growth/NudgeController.kt b/quickstep/src/com/android/launcher3/taskbar/growth/NudgeController.kt
new file mode 100644
index 0000000..04e41bc
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/growth/NudgeController.kt
@@ -0,0 +1,61 @@
+/*
+ * 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.taskbar.growth
+
+import android.content.Context
+import com.android.launcher3.Utilities
+import com.android.launcher3.taskbar.TaskbarActivityContext
+import com.android.launcher3.taskbar.TaskbarControllers
+import com.android.launcher3.taskbar.TaskbarControllers.LoggableTaskbarController
+import com.android.launcher3.util.DisplayController
+import com.android.launcher3.views.ActivityContext
+import java.io.PrintWriter
+
+/** Controls nudge lifecycles. */
+class NudgeController(context: Context) : LoggableTaskbarController {
+
+ protected val activityContext: TaskbarActivityContext = ActivityContext.lookupContext(context)
+
+ private val isNudgeEnabled: Boolean
+ get() {
+ return !Utilities.isRunningInTestHarness() &&
+ !activityContext.isPhoneMode &&
+ !activityContext.isTinyTaskbar
+ }
+
+ private lateinit var controllers: TaskbarControllers
+
+ fun init(controllers: TaskbarControllers) {
+ this.controllers = controllers
+ }
+
+ fun maybeShow(payload: NudgePayload) {
+ if (!isNudgeEnabled || !DisplayController.isTransientTaskbar(activityContext)) {
+ return
+ }
+ // TODO: b/398033012 - create and show nudge view based on the payload.
+ }
+
+ /** Closes the current [nudgeView]. */
+ fun hide() {
+ // TODO: b/398033012 - hide the nudge view.
+ }
+
+ override fun dumpLogs(prefix: String?, pw: PrintWriter?) {
+ pw?.println(prefix + "NudgeController:")
+ pw?.println("$prefix\tisNudgeEnabled=$isNudgeEnabled")
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/growth/NudgePayload.kt b/quickstep/src/com/android/launcher3/taskbar/growth/NudgePayload.kt
new file mode 100644
index 0000000..7498cbc
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/growth/NudgePayload.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.taskbar.growth
+
+sealed interface Action {
+ data class Dismiss(
+ val markAsDismissed: Boolean = true,
+ val dismissRetentionInDays: Int? = null,
+ ) : Action
+
+ data class OpenUrl(val url: String) : Action
+}
+
+sealed class Image {
+ data class ResourceId(val resId: Int) : Image()
+
+ data class Url(val url: String) : Image()
+}
+
+data class ButtonPayload(val label: String, val actions: List<Action>)
+
+data class NudgePayload(
+ val titleText: String,
+ val bodyText: String,
+ val image: Image?,
+ val primaryButton: ButtonPayload?,
+ val secondaryButton: ButtonPayload?,
+
+ // TODO: b/396223717 - add anchoring information.
+)
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/NearestTouchFrame.java b/quickstep/src/com/android/launcher3/taskbar/navbutton/NearestTouchFrame.java
index bbf08bf..844f1af 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/NearestTouchFrame.java
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/NearestTouchFrame.java
@@ -194,6 +194,7 @@
event.offsetLocation(mTouchingChild.getWidth() / 2 - x,
mTouchingChild.getHeight() / 2 - y);
return mTouchingChild.getVisibility() == VISIBLE
+ && mTouchingChild.isAttachedToWindow()
&& mTouchingChild.dispatchTouchEvent(event);
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
index 636d89b..dd91d17 100644
--- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
@@ -55,7 +55,7 @@
Context windowContext,
TaskbarActivityContext taskbarContext,
TaskbarControllers controllers) {
- super(windowContext);
+ super(windowContext, taskbarContext.isPrimaryDisplay());
mTaskbarContext = taskbarContext;
mOverlayController = controllers.taskbarOverlayController;
mDragController = new TaskbarDragController(this);
@@ -64,6 +64,12 @@
mStashedTaskbarHeight = controllers.taskbarStashController.getStashedHeight();
mUiController = controllers.uiController;
+ onViewCreated();
+ }
+
+ /** Called when the controller is destroyed. */
+ public void onDestroy() {
+ mDragController.onDestroy();
}
public @Nullable TaskbarSearchSessionController getSearchSessionController() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
index 79cb748..675d55b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
@@ -26,8 +26,12 @@
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.PixelFormat;
+import android.util.Log;
+import android.view.AttachedSurfaceControl;
import android.view.Gravity;
import android.view.MotionEvent;
+import android.view.SurfaceControl;
+import android.view.ViewRootImpl;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
@@ -36,8 +40,10 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Flags;
+import com.android.launcher3.R;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarControllers;
+import com.android.systemui.shared.system.BlurUtils;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
@@ -52,12 +58,14 @@
*/
public final class TaskbarOverlayController {
+ private static final String TAG = "TaskbarOverlayController";
private static final String WINDOW_TITLE = "Taskbar Overlay";
private final TaskbarActivityContext mTaskbarContext;
private final Context mWindowContext;
private final TaskbarOverlayProxyView mProxyView;
private final LayoutParams mLayoutParams;
+ private final int mMaxBlurRadius;
private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
@Override
@@ -88,6 +96,8 @@
private DeviceProfile mLauncherDeviceProfile;
private @Nullable TaskbarOverlayContext mOverlayContext;
private TaskbarControllers mControllers; // Initialized in init.
+ // True if we have alerted surface flinger of an expensive call for blur.
+ private boolean mInEarlyWakeUp;
public TaskbarOverlayController(
TaskbarActivityContext taskbarContext, DeviceProfile launcherDeviceProfile) {
@@ -96,6 +106,8 @@
mProxyView = new TaskbarOverlayProxyView();
mLayoutParams = createLayoutParams();
mLauncherDeviceProfile = launcherDeviceProfile;
+ mMaxBlurRadius = mTaskbarContext.getResources().getDimensionPixelSize(
+ R.dimen.max_depth_blur_radius_enhanced);
}
/** Initialize the controller. */
@@ -150,9 +162,13 @@
/** Destroys the controller and any overlay window if present. */
public void onDestroy() {
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
- Optional.ofNullable(mOverlayContext)
- .map(c -> c.getSystemService(WindowManager.class))
- .ifPresent(m -> m.removeViewImmediate(mOverlayContext.getDragLayer()));
+ Optional.ofNullable(mOverlayContext).ifPresent(c -> {
+ c.onDestroy();
+ WindowManager wm = c.getSystemService(WindowManager.class);
+ if (wm != null) {
+ wm.removeViewImmediate(mOverlayContext.getDragLayer());
+ }
+ });
mOverlayContext = null;
}
@@ -197,6 +213,65 @@
}
/**
+ * Sets the blur radius for the overlay window.
+ *
+ * @param radius the blur radius in pixels. This will automatically change to {@code 0} if blurs
+ * are unsupported on the device.
+ */
+ public void setBackgroundBlurRadius(int radius) {
+ if (!Flags.allAppsBlur()) {
+ return;
+ }
+ if (!BlurUtils.supportsBlursOnWindows()) {
+ Log.d(TAG, "setBackgroundBlurRadius: not supported, setting to 0");
+ radius = 0;
+ // intentionally falling through in case a non-0 blur was previously set.
+ }
+ if (mOverlayContext == null) {
+ Log.w(TAG, "setBackgroundBlurRadius: no overlay context");
+ return;
+ }
+ TaskbarOverlayDragLayer dragLayer = mOverlayContext.getDragLayer();
+ if (dragLayer == null) {
+ Log.w(TAG, "setBackgroundBlurRadius: no drag layer");
+ return;
+ }
+ ViewRootImpl dragLayerViewRoot = dragLayer.getViewRootImpl();
+ if (dragLayerViewRoot == null) {
+ Log.w(TAG, "setBackgroundBlurRadius: dragLayerViewRoot is null");
+ return;
+ }
+ AttachedSurfaceControl rootSurfaceControl = dragLayer.getRootSurfaceControl();
+ if (rootSurfaceControl == null) {
+ Log.w(TAG, "setBackgroundBlurRadius: rootSurfaceControl is null");
+ return;
+ }
+ SurfaceControl surfaceControl = dragLayerViewRoot.getSurfaceControl();
+ if (surfaceControl == null || !surfaceControl.isValid()) {
+ Log.w(TAG, "setBackgroundBlurRadius: surfaceControl is null or invalid");
+ return;
+ }
+ Log.v(TAG, "setBackgroundBlurRadius: " + radius);
+ SurfaceControl.Transaction transaction =
+ new SurfaceControl.Transaction().setBackgroundBlurRadius(surfaceControl, radius);
+
+ // Set early wake-up flags when we know we're executing an expensive operation, this way
+ // SurfaceFlinger will adjust its internal offsets to avoid jank.
+ boolean wantsEarlyWakeUp = radius > 0 && radius < mMaxBlurRadius;
+ if (wantsEarlyWakeUp && !mInEarlyWakeUp) {
+ Log.d(TAG, "setBackgroundBlurRadius: setting early wakeup");
+ transaction.setEarlyWakeupStart();
+ mInEarlyWakeUp = true;
+ } else if (!wantsEarlyWakeUp && mInEarlyWakeUp) {
+ Log.d(TAG, "setBackgroundBlurRadius: clearing early wakeup");
+ transaction.setEarlyWakeupEnd();
+ mInEarlyWakeUp = false;
+ }
+
+ rootSurfaceControl.applyTransactionOnDraw(transaction);
+ }
+
+ /**
* Proxy view connecting taskbar drag layer to the overlay window.
*
* Overlays are in a separate window and has its own drag layer, but this proxy lets its views
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
index 669850c..41694ec 100644
--- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
@@ -34,7 +34,6 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
-import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.views.BaseDragLayer;
@@ -147,7 +146,7 @@
* 2) Sets tappableInsets bottom inset to 0.
*/
private WindowInsets updateInsetsDueToStashing(WindowInsets oldInsets) {
- if (!DisplayController.isTransientTaskbar(mContainer)) {
+ if (!mContainer.isTransientTaskbar()) {
return oldInsets;
}
WindowInsets.Builder updatedInsetsBuilder = new WindowInsets.Builder(oldInsets);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index aab8ad1..806b8ab 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -48,7 +48,6 @@
import static com.android.launcher3.popup.SystemShortcut.BUBBLE_SHORTCUT;
import static com.android.launcher3.popup.SystemShortcut.DONT_SUGGEST_APP;
import static com.android.launcher3.popup.SystemShortcut.INSTALL;
-import static com.android.launcher3.popup.SystemShortcut.PIN_UNPIN_ITEM;
import static com.android.launcher3.popup.SystemShortcut.PRIVATE_PROFILE_INSTALL;
import static com.android.launcher3.popup.SystemShortcut.UNINSTALL_APP;
import static com.android.launcher3.popup.SystemShortcut.WIDGETS;
@@ -85,7 +84,9 @@
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.Display;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
@@ -117,6 +118,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
+import com.android.launcher3.allapps.AllAppsRecyclerView;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.apppairs.AppPairIcon;
@@ -166,8 +168,10 @@
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource;
+import com.android.launcher3.util.StableViewInfo;
import com.android.launcher3.util.StartActivityParams;
import com.android.launcher3.util.TouchController;
+import com.android.launcher3.views.FloatingIconView;
import com.android.launcher3.widget.LauncherWidgetHolder;
import com.android.quickstep.OverviewCommandHelper;
import com.android.quickstep.OverviewComponentObserver;
@@ -223,6 +227,7 @@
public class QuickstepLauncher extends Launcher implements RecentsViewContainer,
SystemShortcut.BubbleActivityStarter {
+ private static final String TAG = "QuickstepLauncher";
private static final boolean TRACE_LAYOUTS =
SystemProperties.getBoolean("persist.debug.trace_layouts", false);
private static final String TRACE_RELAYOUT_CLASS =
@@ -475,9 +480,6 @@
List<SystemShortcut.Factory> shortcuts = new ArrayList(Arrays.asList(
APP_INFO, WellbeingModel.SHORTCUT_FACTORY, mHotseatPredictionController));
- if (Flags.enablePinningAppWithContextMenu()) {
- shortcuts.add(0, PIN_UNPIN_ITEM);
- }
shortcuts.addAll(getSplitShortcuts());
shortcuts.add(WIDGETS);
shortcuts.add(INSTALL);
@@ -561,20 +563,35 @@
@Override
public void onDestroy() {
+ // wrap non-trivial clean up blocks in try-catch to avoid stopping clean up of rest of
+ // objects
+
if (mAppTransitionManager != null) {
- mAppTransitionManager.onActivityDestroyed();
+ try {
+ mAppTransitionManager.onActivityDestroyed();
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to destroy mAppTransitionManager", e);
+ }
}
mAppTransitionManager = null;
mIsPredictiveBackToHomeInProgress = false;
if (mUnfoldTransitionProgressProvider != null) {
- SystemUiProxy.INSTANCE.get(this).setUnfoldAnimationListener(null);
- mUnfoldTransitionProgressProvider.destroy();
+ try {
+ SystemUiProxy.INSTANCE.get(this).setUnfoldAnimationListener(null);
+ mUnfoldTransitionProgressProvider.destroy();
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to destroy mUnfoldTransitionProgressProvider", e);
+ }
}
OverviewComponentObserver.INSTANCE.get(this)
.removeOverviewChangeListener(mOverviewChangeListener);
- mTISBindHelper.onDestroy();
+ try {
+ mTISBindHelper.onDestroy();
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to destroy mTISBindHelper", e);
+ }
if (mLauncherUnfoldAnimationController != null) {
mLauncherUnfoldAnimationController.onDestroy();
@@ -584,15 +601,22 @@
mSplitSelectStateController.onDestroy();
}
- RecentsView recentsView = getOverviewPanel();
- if (recentsView != null) {
- recentsView.destroy();
+ try {
+ RecentsView recentsView = getOverviewPanel();
+ if (recentsView != null) {
+ recentsView.destroy();
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to destroy RecentsView", e);
}
- super.onDestroy();
- mHotseatPredictionController.destroy();
- if (mViewCapture != null) mViewCapture.close();
- removeBackAnimationCallback(mSplitSelectStateController.getSplitBackHandler());
+ try {
+ super.onDestroy();
+ } finally { // trivial close operations in finally.
+ mHotseatPredictionController.destroy();
+ if (mViewCapture != null) mViewCapture.close();
+ removeBackAnimationCallback(mSplitSelectStateController.getSplitBackHandler());
+ }
}
@Override
@@ -1274,7 +1298,7 @@
options.setPendingIntentBackgroundActivityStartMode(
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
- IRemoteCallback endCallback = completeRunnableListCallback(callbacks);
+ IRemoteCallback endCallback = completeRunnableListCallback(callbacks, this);
options.setOnAnimationAbortListener(endCallback);
options.setOnAnimationFinishedListener(endCallback);
return new ActivityOptionsWrapper(options, callbacks);
@@ -1455,6 +1479,45 @@
mBubbleBarLocation = bubbleBarLocation;
}
+ /**
+ * Similar to {@link #getFirstHomeElementForAppClose} but also matches all apps if its visible
+ */
+ @Nullable
+ public View getFirstVisibleElementForAppClose(
+ @Nullable StableViewInfo svi, String packageName, UserHandle user) {
+ if (isInState(LauncherState.ALL_APPS)) {
+ AllAppsRecyclerView activeRecyclerView = getAppsView().getActiveRecyclerView();
+ View v = null;
+ if (svi != null) {
+ // Preferred item match
+ v = activeRecyclerView.findViewByPredicate(view ->
+ view.isAggregatedVisible()
+ && view.getTag() instanceof ItemInfo info && svi.matches(info));
+ }
+ if (v == null) {
+ // Package user match
+ v = activeRecyclerView.findViewByPredicate(view ->
+ view.isAggregatedVisible() && view.getTag() instanceof ItemInfo info
+ && info.itemType == ITEM_TYPE_APPLICATION
+ && info.user.equals(user)
+ && TextUtils.equals(info.getTargetPackage(), packageName));
+ }
+
+ if (v != null && activeRecyclerView.computeVerticalScrollOffset() > 0) {
+ RectF locationBounds = new RectF();
+ FloatingIconView.getLocationBoundsForView(this, v, false, locationBounds,
+ new Rect());
+ if (locationBounds.top < getAppsView().getHeaderBottom()) {
+ // Icon is covered by scrim, return null to play fallback animation.
+ return null;
+ }
+ }
+ return v;
+ }
+
+ return getFirstHomeElementForAppClose(svi, packageName, user);
+ }
+
@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 79328df..23dc81d 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.kt
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.kt
@@ -76,10 +76,7 @@
if (state.displayOverviewTasksAsGrid(launcher.deviceProfile)) 1f else 0f,
)
if (enableDesktopExplodedView()) {
- DESK_EXPLODE_PROGRESS.set(
- recentsView,
- if (state.displayOverviewTasksAsGrid(launcher.deviceProfile)) 1f else 0f,
- )
+ DESK_EXPLODE_PROGRESS.set(recentsView, if (state.showExplodedDesktopView()) 1f else 0f)
}
TASK_THUMBNAIL_SPLASH_ALPHA.set(
@@ -168,7 +165,7 @@
builder.setFloat(
recentsView,
DESK_EXPLODE_PROGRESS,
- if (toState.isRecentsViewVisible) 1f else 0f,
+ if (toState.showExplodedDesktopView()) 1f else 0f,
getOverviewInterpolator(fromState, toState),
)
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index b27c6e8..54b4fa2 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -23,6 +23,7 @@
import com.android.internal.jank.Cuj;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Flags;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
@@ -153,7 +154,7 @@
return new PageAlphaProvider(DECELERATE_2) {
@Override
public float getPageAlpha(int pageIndex) {
- return launcher.getDeviceProfile().isTablet
+ return isWorkspaceVisible(launcher.getDeviceProfile())
? superPageAlphaProvider.getPageAlpha(pageIndex)
: 0;
}
@@ -163,13 +164,17 @@
@Override
public int getVisibleElements(Launcher launcher) {
int elements = ALL_APPS_CONTENT | FLOATING_SEARCH_BAR;
- // When All Apps is presented on a bottom sheet, HOTSEAT_ICONS are visible.
- if (launcher.getDeviceProfile().isTablet) {
+ if (isWorkspaceVisible(launcher.getDeviceProfile())) {
elements |= HOTSEAT_ICONS;
}
return elements;
}
+ private static boolean isWorkspaceVisible(DeviceProfile deviceProfile) {
+ // Currently we hide the workspace with the all apps blur flag for simplicity.
+ return deviceProfile.isTablet && !Flags.allAppsBlur();
+ }
+
@Override
public int getFloatingSearchBarRestingMarginBottom(Launcher launcher) {
return 0;
@@ -201,8 +206,12 @@
@Override
public int getWorkspaceScrimColor(Launcher launcher) {
- return launcher.getDeviceProfile().shouldShowAllAppsOnSheet()
- ? launcher.getResources().getColor(R.color.widgets_picker_scrim)
- : Themes.getAttrColor(launcher, R.attr.allAppsScrimColor);
+ if (!launcher.getDeviceProfile().shouldShowAllAppsOnSheet()) {
+ return Themes.getAttrColor(launcher, R.attr.allAppsScrimColor);
+ }
+ if (Flags.allAppsBlur()) {
+ return Themes.getAttrColor(launcher, R.attr.allAppsScrimColorOverBlur);
+ }
+ return launcher.getResources().getColor(R.color.widgets_picker_scrim);
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index b1196af..a5b1ee7 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -97,6 +97,11 @@
}
@Override
+ public boolean showExplodedDesktopView() {
+ return false;
+ }
+
+ @Override
protected float getDepthUnchecked(Context context) {
if (Launcher.getLauncher(context).areDesktopTasksVisible()) {
// Don't blur the background while desktop tasks are visible
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 5fdedcc..8529dcf 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -16,10 +16,13 @@
package com.android.launcher3.uioverrides.states;
import static com.android.app.animation.Interpolators.DECELERATE_2;
+import static com.android.launcher3.Flags.enableDesktopExplodedView;
+import static com.android.launcher3.Flags.enableOverviewBackgroundWallpaperBlur;
import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
import android.content.Context;
+import android.graphics.Color;
import android.graphics.Rect;
import android.os.SystemProperties;
@@ -157,7 +160,8 @@
@Override
public int getWorkspaceScrimColor(Launcher launcher) {
- return Themes.getAttrColor(launcher, R.attr.overviewScrimColor);
+ return enableOverviewBackgroundWallpaperBlur() ? Color.TRANSPARENT
+ : Themes.getAttrColor(launcher, R.attr.overviewScrimColor);
}
@Override
@@ -171,6 +175,11 @@
}
@Override
+ public boolean showExplodedDesktopView() {
+ return enableDesktopExplodedView();
+ }
+
+ @Override
public boolean disallowTaskbarGlobalDrag() {
// Disable global drag in overview
return true;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchControllerDeprecated.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchControllerDeprecated.java
index b1a36c7..f26bd13 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchControllerDeprecated.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchControllerDeprecated.java
@@ -239,7 +239,7 @@
pa = new PendingAnimation(maxDuration);
mRecentsView.createTaskDismissAnimation(pa, mTaskBeingDragged,
true /* animateTaskView */, true /* removeTask */, maxDuration,
- false /* dismissingForSplitSelection*/);
+ false /* dismissingForSplitSelection*/, false /* isExpressiveDismiss */);
mEndDisplacement = -secondaryTaskDimension;
} else {
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index c51f659..168b856 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -31,6 +31,7 @@
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.enableGestureNavHorizontalTouchSlop;
import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
import static com.android.launcher3.Flags.msdlFeedback;
import static com.android.launcher3.PagedView.INVALID_PAGE;
@@ -102,6 +103,7 @@
import android.window.TransitionInfo;
import android.window.WindowAnimationState;
+import androidx.annotation.CallSuper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
@@ -134,6 +136,7 @@
import com.android.launcher3.util.WindowBounds;
import com.android.quickstep.GestureState.GestureEndTarget;
import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
import com.android.quickstep.util.ActiveGestureErrorDetector;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.ActiveGestureProtoLogProxy;
@@ -221,6 +224,7 @@
private final Runnable mLauncherOnDestroyCallback = () -> {
ActiveGestureProtoLogProxy.logLauncherDestroyed();
+ mRecentsView.removeOnScrollChangedListener(mOnRecentsScrollListener);
mRecentsView = null;
mContainer = null;
mStateCallback.clearState(STATE_LAUNCHER_PRESENT);
@@ -313,7 +317,7 @@
*/
private static final int LOG_NO_OP_PAGE_INDEX = -1;
- protected final TaskAnimationManager mTaskAnimationManager;
+ protected TaskAnimationManager mTaskAnimationManager;
// Either RectFSpringAnim (if animating home) or ObjectAnimator (from mCurrentShift) otherwise
private RunningWindowAnim[] mRunningWindowAnim;
// Possible second animation running at the same time as mRunningWindowAnim
@@ -410,7 +414,7 @@
mMSDLPlayerWrapper = msdlPlayerWrapper;
initTransitionEndpoints(mRemoteTargetHandles[0].getTaskViewSimulator()
- .getOrientationState().getLauncherDeviceProfile());
+ .getOrientationState().getLauncherDeviceProfile(gestureState.getDisplayId()));
initStateCallbacks();
mIsTransientTaskbar = mDp.isTaskbarPresent
@@ -900,7 +904,9 @@
}
public Intent getLaunchIntent() {
- return mGestureState.getOverviewIntent();
+ // todo differentiate intent based on if we are on home or in app for overview in window
+ boolean useHomeIntentForWindow = RecentsWindowFlags.getEnableOverviewInWindow();
+ return useHomeIntentForWindow ? getHomeIntent() : mGestureState.getOverviewIntent();
}
/**
* Called when the value of {@link #mCurrentShift} changes
@@ -988,7 +994,8 @@
// both split and non-split
RecentsOrientedState orientationState = mRemoteTargetHandles[0].getTaskViewSimulator()
.getOrientationState();
- DeviceProfile dp = orientationState.getLauncherDeviceProfile();
+ DeviceProfile dp = orientationState.getLauncherDeviceProfile(
+ mGestureState.getDisplayId());
if (targets.minimizedHomeBounds != null && primaryTaskTarget != null) {
Rect overviewStackBounds = mContainerInterface
.getOverviewWindowBounds(targets.minimizedHomeBounds, primaryTaskTarget);
@@ -1102,7 +1109,12 @@
public void onGestureCancelled() {
updateDisplacement(0);
mStateCallback.setStateOnUiThread(STATE_GESTURE_COMPLETED);
- handleNormalGestureEnd(0, false, new PointF(), true /* isCancel */);
+ handleNormalGestureEnd(
+ /* endVelocityPxPerMs= */ 0,
+ /* isFling= */ false,
+ /* velocityPxPerMs= */ new PointF(),
+ /* isCancel= */ true,
+ /* horizontalTouchSlopPassed= */ false);
}
/**
@@ -1111,12 +1123,10 @@
* @param velocityPxPerMs The x and y components of the velocity when the gesture ends.
*/
@UiThread
- public void onGestureEnded(float endVelocityPxPerMs, PointF velocityPxPerMs) {
+ public void onGestureEnded(
+ float endVelocityPxPerMs, PointF velocityPxPerMs, boolean horizontalTouchSlopPassed) {
float flingThreshold = mContext.getResources()
.getDimension(R.dimen.quickstep_fling_threshold_speed);
- Log.d(TAG, "onGestureEnded: mGestureStarted=" + mGestureStarted
- + ", mIsMotionPaused=" + mIsMotionPaused
- + ", flingThresholdPassed=" + (Math.abs(endVelocityPxPerMs) > flingThreshold));
boolean isFling = mGestureStarted && !mIsMotionPaused
&& Math.abs(endVelocityPxPerMs) > flingThreshold;
mStateCallback.setStateOnUiThread(STATE_GESTURE_COMPLETED);
@@ -1127,7 +1137,11 @@
mLogDirectionUpOrLeft = velocityPxPerMs.x < 0;
}
Runnable handleNormalGestureEndCallback = () -> handleNormalGestureEnd(
- endVelocityPxPerMs, isFling, velocityPxPerMs, /* isCancel= */ false);
+ endVelocityPxPerMs,
+ isFling,
+ velocityPxPerMs,
+ /* isCancel= */ false,
+ horizontalTouchSlopPassed);
if (mRecentsView != null) {
mRecentsView.runOnPageScrollsInitialized(handleNormalGestureEndCallback);
} else {
@@ -1260,27 +1274,30 @@
}
private GestureEndTarget calculateEndTarget(
- PointF velocityPxPerMs, float endVelocityPxPerMs, boolean isFlingY, boolean isCancel) {
+ PointF velocityPxPerMs,
+ float endVelocityPxPerMs,
+ boolean isFlingY,
+ boolean isCancel,
+ boolean horizontalTouchSlopPassed) {
ActiveGestureProtoLogProxy.logOnCalculateEndTarget(
dpiFromPx(velocityPxPerMs.x),
dpiFromPx(velocityPxPerMs.y),
Math.toDegrees(Math.atan2(-velocityPxPerMs.y, velocityPxPerMs.x)));
+
if (mGestureState.isHandlingAtomicEvent()) {
// Button mode, this is only used to go to recents.
return RECENTS;
}
- Log.d(TAG, "calculateEndTarget: isCancel=" + isCancel + ", isFlingY=" + isFlingY);
GestureEndTarget endTarget;
if (isCancel) {
endTarget = LAST_TASK;
} else if (isFlingY) {
endTarget = calculateEndTargetForFlingY(velocityPxPerMs, endVelocityPxPerMs);
} else {
- endTarget = calculateEndTargetForNonFling(velocityPxPerMs);
+ endTarget = calculateEndTargetForNonFling(velocityPxPerMs, horizontalTouchSlopPassed);
}
- Log.d(TAG, "calculateEndTarget: endTarget(1)=" + endTarget);
if (mDeviceState.isOverviewDisabled() && endTarget == RECENTS) {
return LAST_TASK;
}
@@ -1299,7 +1316,6 @@
return LAST_TASK;
}
}
- Log.d(TAG, "calculateEndTarget: endTarget(2)=" + endTarget);
return endTarget;
}
@@ -1308,27 +1324,23 @@
final boolean willGoToNewTask =
isScrollingToNewTask() && Math.abs(velocity.x) > Math.abs(endVelocity);
final boolean isSwipeUp = endVelocity < 0;
- Log.d(TAG, "calculateEndTargetForFlingY: willGoToNewTask=" + willGoToNewTask
- + ", isSwipeUp=" + isSwipeUp);
if (!isSwipeUp) {
final boolean isCenteredOnNewTask = mRecentsView != null
&& mRecentsView.getDestinationPage() != mRecentsView.getRunningTaskIndex();
- Log.d(TAG, "calculateEndTargetForFlingY: isCenteredOnNewTask=" + isCenteredOnNewTask);
return willGoToNewTask || isCenteredOnNewTask ? NEW_TASK : LAST_TASK;
}
return willGoToNewTask ? NEW_TASK : HOME;
}
- private GestureEndTarget calculateEndTargetForNonFling(PointF velocity) {
+ private GestureEndTarget calculateEndTargetForNonFling(
+ PointF velocity, boolean horizontalTouchSlopPassed) {
final boolean isScrollingToNewTask = isScrollingToNewTask();
// Fully gestural mode.
final boolean isFlingX = Math.abs(velocity.x) > mContext.getResources()
- .getDimension(R.dimen.quickstep_fling_threshold_speed);
- Log.d(TAG, "calculateEndTargetForNonFling: isScrollingToNewTask=" + isScrollingToNewTask
- + ", isFlingX=" + isFlingX
- + ", mIsMotionPaused=" + mIsMotionPaused);
+ .getDimension(R.dimen.quickstep_fling_threshold_speed)
+ && (!enableGestureNavHorizontalTouchSlop() || horizontalTouchSlopPassed);
if (isScrollingToNewTask && isFlingX) {
// Flinging towards new task takes precedence over mIsMotionPaused (which only
// checks y-velocity).
@@ -1338,7 +1350,6 @@
} else if (isScrollingToNewTask) {
return NEW_TASK;
}
- Log.d(TAG, "calculateEndTargetForNonFling: mCanSlowSwipeGoHome=" + mCanSlowSwipeGoHome);
return velocity.y < 0 && mCanSlowSwipeGoHome ? HOME : LAST_TASK;
}
@@ -1369,11 +1380,15 @@
@UiThread
private void handleNormalGestureEnd(
- float endVelocityPxPerMs, boolean isFling, PointF velocityPxPerMs, boolean isCancel) {
+ float endVelocityPxPerMs,
+ boolean isFling,
+ PointF velocityPxPerMs,
+ boolean isCancel,
+ boolean horizontalTouchSlopPassed) {
long duration = MAX_SWIPE_DURATION;
float currentShift = mCurrentShift.value;
final GestureEndTarget endTarget = calculateEndTarget(
- velocityPxPerMs, endVelocityPxPerMs, isFling, isCancel);
+ velocityPxPerMs, endVelocityPxPerMs, isFling, isCancel, horizontalTouchSlopPassed);
// Set the state, but don't notify until the animation completes
mGestureState.setEndTarget(endTarget, false /* isAtomic */);
mAnimationFactory.setEndTarget(endTarget);
@@ -1382,7 +1397,7 @@
&& mIsTransientTaskbar
&& mContainerInterface.getTaskbarController() != null) {
mContainerInterface.getTaskbarController()
- .setUserIsNotGoingHome(endTarget != GestureState.GestureEndTarget.HOME);
+ .setUserIsNotGoingHome(endTarget != HOME);
}
float endShift = endTarget.isLauncher ? 1 : 0;
@@ -2070,7 +2085,7 @@
* specific edge case: if we switch from A to B, and back to A before B appears, we need to
* start A again to ensure it stays on top.
*/
- @androidx.annotation.CallSuper
+ @CallSuper
protected void onRestartPreviouslyAppearedTask() {
// Finish the controller here, since we won't get onTaskAppeared() for a task that already
// appeared.
diff --git a/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt b/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt
index 914855b..4280baf 100644
--- a/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt
+++ b/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt
@@ -82,6 +82,7 @@
taskKey.numActivities,
taskKey.isTopActivityNoDisplay,
taskKey.isActivityStackTransparent,
+ taskKey.userId,
) -> null
!taskContainer.task.isDockable -> null
diff --git a/quickstep/src/com/android/quickstep/DeviceConfigWrapper.kt b/quickstep/src/com/android/quickstep/DeviceConfigWrapper.kt
index f610014..9d7f704 100644
--- a/quickstep/src/com/android/quickstep/DeviceConfigWrapper.kt
+++ b/quickstep/src/com/android/quickstep/DeviceConfigWrapper.kt
@@ -152,6 +152,9 @@
writer.println("$prefix\tanimateLpnh=$animateLpnh")
writer.println("$prefix\tshrinkNavHandleOnPress=$shrinkNavHandleOnPress")
writer.println("$prefix\tlpnhTimeoutMs=$lpnhTimeoutMs")
+ writer.println("$prefix\tenableLpnhTwoStages=$enableLpnhTwoStages")
+ writer.println("$prefix\ttwoStageDurationPercentage=$twoStageDurationPercentage")
+ writer.println("$prefix\ttwoStageSlopPercentage=$twoStageSlopPercentage")
writer.println("$prefix\tenableLongPressNavHandle=$enableLongPressNavHandle")
writer.println("$prefix\tenableSearchHapticHint=$enableSearchHapticHint")
writer.println("$prefix\tenableSearchHapticCommit=$enableSearchHapticCommit")
diff --git a/quickstep/src/com/android/quickstep/DisplayModel.kt b/quickstep/src/com/android/quickstep/DisplayModel.kt
index cbc2f7d..ac94375 100644
--- a/quickstep/src/com/android/quickstep/DisplayModel.kt
+++ b/quickstep/src/com/android/quickstep/DisplayModel.kt
@@ -20,28 +20,28 @@
import android.hardware.display.DisplayManager
import android.util.Log
import android.util.SparseArray
+import android.view.Display
import androidx.core.util.valueIterator
+import com.android.launcher3.util.Executors
import com.android.quickstep.DisplayModel.DisplayResource
+import java.io.PrintWriter
/** data model for managing resources with lifecycles that match that of the connected display */
abstract class DisplayModel<RESOURCE_TYPE : DisplayResource>(val context: Context) {
companion object {
- private const val TAG = "DisplayViewModel"
+ private const val TAG = "DisplayModel"
private const val DEBUG = false
}
- protected val displayManager =
- context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
+ private val displayManager = context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
protected val displayResourceArray = SparseArray<RESOURCE_TYPE>()
- abstract fun createDisplayResource(displayId: Int)
-
- protected val displayListener: DisplayManager.DisplayListener =
+ private val displayListener: DisplayManager.DisplayListener =
(object : DisplayManager.DisplayListener {
override fun onDisplayAdded(displayId: Int) {
if (DEBUG) Log.d(TAG, "onDisplayAdded: displayId=$displayId")
- createDisplayResource(displayId)
+ storeDisplayResource(displayId)
}
override fun onDisplayRemoved(displayId: Int) {
@@ -54,6 +54,17 @@
}
})
+ protected abstract fun createDisplayResource(display: Display): RESOURCE_TYPE
+
+ protected fun registerDisplayListener() {
+ displayManager.registerDisplayListener(displayListener, Executors.MAIN_EXECUTOR.handler)
+ // In the scenario where displays were added before this display listener was
+ // registered, we should store the DisplayResources for those displays directly.
+ displayManager.displays
+ .filter { getDisplayResource(it.displayId) == null }
+ .forEach { storeDisplayResource(it.displayId) }
+ }
+
fun destroy() {
displayResourceArray.valueIterator().forEach { displayResource ->
displayResource.cleanup()
@@ -73,7 +84,36 @@
displayResourceArray.remove(displayId)
}
- abstract class DisplayResource() {
+ fun storeDisplayResource(displayId: Int) {
+ if (DEBUG) Log.d(TAG, "store: displayId=$displayId")
+ getDisplayResource(displayId)?.let {
+ return
+ }
+ val display = displayManager.getDisplay(displayId)
+ if (display == null) {
+ if (DEBUG)
+ Log.w(
+ TAG,
+ "storeDisplayResource: could not create display for displayId=$displayId",
+ Exception(),
+ )
+ return
+ }
+ displayResourceArray[displayId] = createDisplayResource(display)
+ }
+
+ fun dump(prefix: String, writer: PrintWriter) {
+ writer.println("${prefix}${this::class.simpleName}: display resources=[")
+
+ displayResourceArray.valueIterator().forEach { displayResource ->
+ displayResource.dump("${prefix}\t", writer)
+ }
+ writer.println("${prefix}]")
+ }
+
+ abstract class DisplayResource {
abstract fun cleanup()
+
+ abstract fun dump(prefix: String, writer: PrintWriter)
}
}
diff --git a/quickstep/src/com/android/quickstep/ExternalDisplaySystemShortcut.kt b/quickstep/src/com/android/quickstep/ExternalDisplaySystemShortcut.kt
index f97cf9c..3b823f5 100644
--- a/quickstep/src/com/android/quickstep/ExternalDisplaySystemShortcut.kt
+++ b/quickstep/src/com/android/quickstep/ExternalDisplaySystemShortcut.kt
@@ -80,6 +80,7 @@
taskKey.numActivities,
taskKey.isTopActivityNoDisplay,
taskKey.isActivityStackTransparent,
+ taskKey.userId,
) -> null
else -> {
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index e1d4536..c4ba2d5 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -30,6 +30,7 @@
import android.content.Intent;
import android.os.SystemClock;
+import android.view.Display;
import android.view.MotionEvent;
import android.view.RemoteAnimationTarget;
import android.window.TransitionInfo;
@@ -37,10 +38,10 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.launcher3.Flags;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StatefulContainer;
import com.android.quickstep.TopTaskTracker.CachedTaskInfo;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
import com.android.quickstep.util.ActiveGestureErrorDetector;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.ActiveGestureProtoLogProxy;
@@ -158,6 +159,7 @@
private final BaseContainerInterface mContainerInterface;
private final MultiStateCallback mStateCallback;
private final int mGestureId;
+ private final int mDisplayId;
public enum TrackpadGestureType {
NONE,
@@ -190,16 +192,18 @@
private boolean mHandlingAtomicEvent;
private boolean mIsInExtendedSlopRegion;
- public GestureState(OverviewComponentObserver componentObserver, int gestureId) {
+ public GestureState(OverviewComponentObserver componentObserver, int displayId, int gestureId) {
+ mDisplayId = displayId;
mHomeIntent = componentObserver.getHomeIntent();
mOverviewIntent = componentObserver.getOverviewIntent();
- mContainerInterface = componentObserver.getContainerInterface();
+ mContainerInterface = componentObserver.getContainerInterface(displayId);
mStateCallback = new MultiStateCallback(
STATE_NAMES.toArray(new String[0]), GestureState::getTrackedEventForState);
mGestureId = gestureId;
}
public GestureState(GestureState other) {
+ mDisplayId = other.mDisplayId;
mHomeIntent = other.mHomeIntent;
mOverviewIntent = other.mOverviewIntent;
mContainerInterface = other.mContainerInterface;
@@ -214,6 +218,7 @@
public GestureState() {
// Do nothing, only used for initializing the gesture state prior to user unlock
+ mDisplayId = Display.DEFAULT_DISPLAY;
mHomeIntent = new Intent();
mOverviewIntent = new Intent();
mContainerInterface = null;
@@ -285,6 +290,13 @@
}
/**
+ * @return the id for the display this particular gesture was performed on.
+ */
+ public int getDisplayId() {
+ return mDisplayId;
+ }
+
+ /**
* Sets if the gesture is is from the trackpad, if so, whether 3-finger, or 4-finger
*/
public void setTrackpadGestureType(TrackpadGestureType trackpadGestureType) {
@@ -311,8 +323,7 @@
*/
public boolean useSyntheticRecentsTransition() {
return mRunningTask.isHomeTask()
- && (Flags.enableFallbackOverviewInWindow()
- || Flags.enableLauncherOverviewInWindow());
+ && RecentsWindowFlags.Companion.getEnableOverviewInWindow();
}
/**
@@ -545,6 +556,7 @@
public void dump(String prefix, PrintWriter pw) {
pw.println(prefix + "GestureState:");
+ pw.println(prefix + "\tdisplayID=" + mDisplayId);
pw.println(prefix + "\tgestureID=" + mGestureId);
pw.println(prefix + "\trunningTask=" + mRunningTask);
pw.println(prefix + "\tendTarget=" + mEndTarget);
diff --git a/quickstep/src/com/android/quickstep/InputConsumer.java b/quickstep/src/com/android/quickstep/InputConsumer.java
index 0185737..e5cbc66 100644
--- a/quickstep/src/com/android/quickstep/InputConsumer.java
+++ b/quickstep/src/com/android/quickstep/InputConsumer.java
@@ -17,6 +17,7 @@
import android.annotation.TargetApi;
import android.os.Build;
+import android.view.Display;
import android.view.InputEvent;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -62,10 +63,26 @@
"TYPE_BUBBLE_BAR", // 16
};
- InputConsumer NO_OP = () -> TYPE_NO_OP;
+ InputConsumer DEFAULT_NO_OP = createNoOpInputConsumer(Display.DEFAULT_DISPLAY);
+
+ static InputConsumer createNoOpInputConsumer(int displayId) {
+ return new InputConsumer() {
+ @Override
+ public int getType() {
+ return TYPE_NO_OP;
+ }
+
+ @Override
+ public int getDisplayId() {
+ return displayId;
+ }
+ };
+ }
int getType();
+ int getDisplayId();
+
/**
* Returns true if the user has crossed the threshold for it to be an explicit action.
*/
diff --git a/quickstep/src/com/android/quickstep/InputConsumerUtils.kt b/quickstep/src/com/android/quickstep/InputConsumerUtils.kt
index c340c92..c8ca534 100644
--- a/quickstep/src/com/android/quickstep/InputConsumerUtils.kt
+++ b/quickstep/src/com/android/quickstep/InputConsumerUtils.kt
@@ -57,7 +57,7 @@
@JvmStatic
fun <S : BaseState<S>, T> newConsumer(
context: Context,
- resetGestureInputConsumer: ResetGestureInputConsumer?,
+ userUnlocked: Boolean,
overviewComponentObserver: OverviewComponentObserver,
deviceState: RecentsAnimationDeviceState,
previousGestureState: GestureState,
@@ -76,7 +76,12 @@
val bubbleControllers = tac?.bubbleControllers
if (bubbleControllers != null && BubbleBarInputConsumer.isEventOnBubbles(tac, event)) {
val consumer: InputConsumer =
- BubbleBarInputConsumer(context, bubbleControllers, inputMonitorCompat)
+ BubbleBarInputConsumer(
+ context,
+ gestureState.displayId,
+ bubbleControllers,
+ inputMonitorCompat,
+ )
logInputConsumerSelectionReason(
consumer,
newCompoundString("event is on bubbles, creating new input consumer"),
@@ -117,7 +122,8 @@
// camera).
createDeviceLockedInputConsumer(
context,
- resetGestureInputConsumer,
+ userUnlocked,
+ taskbarManager,
deviceState,
gestureState,
taskAnimationManager,
@@ -126,7 +132,10 @@
)
} else {
getDefaultInputConsumer(
- resetGestureInputConsumer,
+ gestureState.displayId,
+ userUnlocked,
+ taskAnimationManager,
+ taskbarManager,
reasonString.append("%scannot start system gesture", SUBSTRING_PREFIX),
)
}
@@ -148,7 +157,8 @@
base =
newBaseConsumer<S, T>(
context,
- resetGestureInputConsumer,
+ userUnlocked,
+ taskbarManager,
overviewComponentObserver,
deviceState,
previousGestureState,
@@ -167,7 +177,14 @@
"cannot start system gesture and recents " +
"animation was not running, trying to use default input consumer"
)
- base = getDefaultInputConsumer(resetGestureInputConsumer, reasonString)
+ base =
+ getDefaultInputConsumer(
+ gestureState.displayId,
+ userUnlocked,
+ taskAnimationManager,
+ taskbarManager,
+ reasonString,
+ )
}
if (deviceState.isGesturalNavMode || gestureState.isTrackpadGesture) {
handleOrientationSetup(base)
@@ -232,7 +249,14 @@
SUBSTRING_PREFIX,
)
// Bubbles can handle home gesture itself.
- base = getDefaultInputConsumer(resetGestureInputConsumer, reasonString)
+ base =
+ getDefaultInputConsumer(
+ gestureState.displayId,
+ userUnlocked,
+ taskAnimationManager,
+ taskbarManager,
+ reasonString,
+ )
}
}
@@ -274,7 +298,14 @@
SUBSTRING_PREFIX,
)
// Bubbles can handle home gesture itself.
- base = getDefaultInputConsumer(resetGestureInputConsumer, reasonString)
+ base =
+ getDefaultInputConsumer(
+ gestureState.displayId,
+ userUnlocked,
+ taskAnimationManager,
+ taskbarManager,
+ reasonString,
+ )
}
}
@@ -285,7 +316,13 @@
"%ssystem dialog is showing, using SysUiOverlayInputConsumer",
SUBSTRING_PREFIX,
)
- base = SysUiOverlayInputConsumer(context, deviceState, inputMonitorCompat)
+ base =
+ SysUiOverlayInputConsumer(
+ context,
+ gestureState.displayId,
+ deviceState,
+ inputMonitorCompat,
+ )
}
if (
@@ -299,7 +336,13 @@
"%sTrackpad 3-finger gesture, using TrackpadStatusBarInputConsumer",
SUBSTRING_PREFIX,
)
- base = TrackpadStatusBarInputConsumer(context, base, inputMonitorCompat)
+ base =
+ TrackpadStatusBarInputConsumer(
+ context,
+ gestureState.displayId,
+ base,
+ inputMonitorCompat,
+ )
}
if (deviceState.isScreenPinningActive) {
@@ -322,7 +365,14 @@
reasonPrefix,
SUBSTRING_PREFIX,
)
- base = OneHandedModeInputConsumer(context, deviceState, base, inputMonitorCompat)
+ base =
+ OneHandedModeInputConsumer(
+ context,
+ gestureState.displayId,
+ deviceState,
+ base,
+ inputMonitorCompat,
+ )
}
if (deviceState.isAccessibilityMenuAvailable) {
@@ -332,7 +382,14 @@
reasonPrefix,
SUBSTRING_PREFIX,
)
- base = AccessibilityInputConsumer(context, deviceState, base, inputMonitorCompat)
+ base =
+ AccessibilityInputConsumer(
+ context,
+ gestureState.displayId,
+ deviceState,
+ base,
+ inputMonitorCompat,
+ )
}
} else {
val reasonPrefix = "device is not in gesture navigation mode"
@@ -343,7 +400,14 @@
"%sscreen pinning is active, trying to use default input consumer",
SUBSTRING_PREFIX,
)
- base = getDefaultInputConsumer(resetGestureInputConsumer, reasonString)
+ base =
+ getDefaultInputConsumer(
+ gestureState.displayId,
+ userUnlocked,
+ taskAnimationManager,
+ taskbarManager,
+ reasonString,
+ )
}
if (deviceState.canTriggerOneHandedAction(event)) {
@@ -354,7 +418,14 @@
reasonPrefix,
SUBSTRING_PREFIX,
)
- base = OneHandedModeInputConsumer(context, deviceState, base, inputMonitorCompat)
+ base =
+ OneHandedModeInputConsumer(
+ context,
+ gestureState.displayId,
+ deviceState,
+ base,
+ inputMonitorCompat,
+ )
}
}
logInputConsumerSelectionReason(base, reasonString)
@@ -373,7 +444,7 @@
context,
deviceState,
inputMonitorCompat,
- InputConsumer.NO_OP,
+ InputConsumer.createNoOpInputConsumer(gestureState.displayId),
gestureState,
motionEvent,
CompoundString.NO_OP,
@@ -412,7 +483,8 @@
@JvmStatic
fun <S : BaseState<S>, T> newBaseConsumer(
context: Context,
- resetGestureInputConsumer: ResetGestureInputConsumer?,
+ userUnlocked: Boolean,
+ taskbarManager: TaskbarManager,
overviewComponentObserver: OverviewComponentObserver,
deviceState: RecentsAnimationDeviceState,
previousGestureState: GestureState,
@@ -429,7 +501,8 @@
// This handles apps showing over the lockscreen (e.g. camera)
return createDeviceLockedInputConsumer(
context,
- resetGestureInputConsumer,
+ userUnlocked,
+ taskbarManager,
deviceState,
gestureState,
taskAnimationManager,
@@ -483,7 +556,9 @@
return if (gestureState.getContainerInterface<S, T>().isInLiveTileMode()) {
createOverviewInputConsumer<S, T>(
- resetGestureInputConsumer,
+ userUnlocked,
+ taskAnimationManager,
+ taskbarManager,
deviceState,
inputMonitorCompat,
previousGestureState,
@@ -496,7 +571,10 @@
)
} else if (runningTask == null) {
getDefaultInputConsumer(
- resetGestureInputConsumer,
+ gestureState.displayId,
+ userUnlocked,
+ taskAnimationManager,
+ taskbarManager,
reasonString.append("%srunning task == null", SUBSTRING_PREFIX),
)
} else if (
@@ -505,7 +583,9 @@
forceOverviewInputConsumer
) {
createOverviewInputConsumer<S, T>(
- resetGestureInputConsumer,
+ userUnlocked,
+ taskAnimationManager,
+ taskbarManager,
deviceState,
inputMonitorCompat,
previousGestureState,
@@ -527,7 +607,10 @@
)
} else if (deviceState.isGestureBlockedTask(runningTask) || launcherChildActivityResumed) {
getDefaultInputConsumer(
- resetGestureInputConsumer,
+ gestureState.displayId,
+ userUnlocked,
+ taskAnimationManager,
+ taskbarManager,
reasonString.append(
if (launcherChildActivityResumed)
"%sis launcher child-task, trying to use default input consumer"
@@ -554,7 +637,8 @@
private fun createDeviceLockedInputConsumer(
context: Context,
- resetGestureInputConsumer: ResetGestureInputConsumer?,
+ userUnlocked: Boolean,
+ taskbarManager: TaskbarManager,
deviceState: RecentsAnimationDeviceState,
gestureState: GestureState,
taskAnimationManager: TaskAnimationManager,
@@ -579,7 +663,10 @@
)
} else {
getDefaultInputConsumer(
- resetGestureInputConsumer,
+ gestureState.displayId,
+ userUnlocked,
+ taskAnimationManager,
+ taskbarManager,
reasonString.append(
if (deviceState.isFullyGesturalNavMode || gestureState.isTrackpadGesture)
"%srunning task == null, trying to use default input consumer"
@@ -593,7 +680,9 @@
}
private fun <S : BaseState<S>, T> createOverviewInputConsumer(
- resetGestureInputConsumer: ResetGestureInputConsumer?,
+ userUnlocked: Boolean,
+ taskAnimationManager: TaskAnimationManager,
+ taskbarManager: TaskbarManager,
deviceState: RecentsAnimationDeviceState,
inputMonitorCompat: InputMonitorCompat,
previousGestureState: GestureState,
@@ -604,7 +693,10 @@
val container: T =
gestureState.getContainerInterface<S, T>().getCreatedContainer()
?: return getDefaultInputConsumer(
- resetGestureInputConsumer,
+ gestureState.displayId,
+ userUnlocked,
+ taskAnimationManager,
+ taskbarManager,
reasonString.append(
"%sactivity == null, trying to use default input consumer",
SUBSTRING_PREFIX,
@@ -656,24 +748,34 @@
}
/** Returns the [ResetGestureInputConsumer] if user is unlocked, else NO_OP. */
- private fun getDefaultInputConsumer(
- resetGestureInputConsumer: ResetGestureInputConsumer?,
+ @JvmStatic
+ fun getDefaultInputConsumer(
+ displayId: Int,
+ userUnlocked: Boolean,
+ taskAnimationManager: TaskAnimationManager?,
+ taskbarManager: TaskbarManager?,
reasonString: CompoundString,
): InputConsumer {
- return if (resetGestureInputConsumer != null) {
+ return if (userUnlocked && taskAnimationManager != null && taskbarManager != null) {
reasonString.append(
- "%smResetGestureInputConsumer initialized, using ResetGestureInputConsumer",
+ "%sResetGestureInputConsumer available, using ResetGestureInputConsumer",
SUBSTRING_PREFIX,
)
- resetGestureInputConsumer
+ ResetGestureInputConsumer(displayId, taskAnimationManager) {
+ taskbarManager.getTaskbarForDisplay(displayId)
+ }
} else {
reasonString.append(
- "%smResetGestureInputConsumer not initialized, using no-op input consumer",
+ "%s${
+ if (userUnlocked) "user is locked"
+ else if (taskAnimationManager == null) "taskAnimationManager is null"
+ else "taskbarManager is null"
+ }, using no-op input consumer",
SUBSTRING_PREFIX,
)
- // mResetGestureInputConsumer isn't initialized until onUserUnlocked(), so reset to
+ // ResetGestureInputConsumer isn't available until onUserUnlocked(), so reset to
// NO_OP until then (we never want these to be null).
- InputConsumer.NO_OP
+ InputConsumer.createNoOpInputConsumer(displayId)
}
}
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
index 783ec2c..2ff42cd 100644
--- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -220,7 +220,7 @@
mHandler.post(() -> {
LauncherBackAnimationController controller = mControllerRef.get();
if (controller != null) {
- controller.startBack(backEvent);
+ controller.initBackMotion(backEvent);
mProgressAnimator.onBackStarted(backEvent, event -> {
float backProgress = event.getProgress();
controller.mBackProgress =
@@ -270,6 +270,7 @@
}
}
controller.mAnimationFinishedCallback = finishedCallback;
+ controller.startBack();
}
@Override
@@ -294,34 +295,32 @@
mBackCallback = null;
}
- private void startBack(BackMotionEvent backEvent) {
+ private void initBackMotion(BackMotionEvent backEvent) {
// in case we're still animating an onBackCancelled event, let's remove the finish-
// callback from the progress animator to prevent calling finishAnimation() before
// restarting a new animation
- // Side note: startBack is never called during the post-commit phase if the back gesture
- // was committed (not cancelled). BackAnimationController prevents that. Therefore we
- // don't have to handle that case.
+ // Side note: initBackMotion is never called during the post-commit phase if the back
+ // gesture was committed (not cancelled). BackAnimationController prevents that. Therefore
+ // we don't have to handle that case.
mProgressAnimator.removeOnBackCancelledFinishCallback();
mBackInProgress = true;
- RemoteAnimationTarget appTarget = backEvent.getDepartingAnimationTarget();
-
- if (appTarget == null || appTarget.leash == null || !appTarget.leash.isValid()) {
+ mInitialTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY());
+ }
+ private void startBack() {
+ if (mBackTarget == null) {
return;
}
mTransaction
- .show(appTarget.leash)
+ .show(mBackTarget.leash)
.setAnimationTransaction();
- mBackTarget = appTarget;
- mInitialTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY());
-
- mStartRect.set(appTarget.windowConfiguration.getMaxBounds());
+ mStartRect.set(mBackTarget.windowConfiguration.getMaxBounds());
// inset bottom in case of taskbar being present
if (!predictiveBackThreeButtonNav() || mLauncher.getDeviceProfile().isTaskbarPresent
|| DisplayController.getNavigationMode(mLauncher) == NavigationMode.NO_BUTTON) {
- mStartRect.inset(0, 0, 0, appTarget.contentInsets.bottom);
+ mStartRect.inset(0, 0, 0, mBackTarget.contentInsets.bottom);
}
mLauncherTargetView = mQuickstepTransitionManager.findLauncherView(
diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index 0a77688..4c56f35 100644
--- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -308,10 +308,10 @@
return null;
}
- return mContainer.getFirstMatchForAppClose(StableViewInfo.fromLaunchCookies(launchCookies),
+ return mContainer.getFirstHomeElementForAppClose(
+ StableViewInfo.fromLaunchCookies(launchCookies),
sourceTaskView.getFirstTask().key.getComponent().getPackageName(),
- UserHandle.of(sourceTaskView.getFirstTask().key.userId),
- false /* supportsAllAppsState */);
+ UserHandle.of(sourceTaskView.getFirstTask().key.userId));
}
@Override
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
index afdb403..20dfb10 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
@@ -22,16 +22,16 @@
import android.os.SystemClock
import android.os.Trace
import android.util.Log
+import android.view.Display.DEFAULT_DISPLAY
import android.view.View
import android.window.TransitionInfo
import androidx.annotation.BinderThread
import androidx.annotation.UiThread
import androidx.annotation.VisibleForTesting
+import com.android.app.tracing.traceSection
import com.android.internal.jank.Cuj
import com.android.launcher3.Flags.enableAltTabKqsOnConnectedDisplays
-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
@@ -52,6 +52,7 @@
import com.android.quickstep.OverviewCommandHelper.CommandType.SHOW
import com.android.quickstep.OverviewCommandHelper.CommandType.TOGGLE
import com.android.quickstep.fallback.window.RecentsDisplayModel
+import com.android.quickstep.fallback.window.RecentsWindowFlags.Companion.enableOverviewInWindow
import com.android.quickstep.util.ActiveGestureLog
import com.android.quickstep.util.ActiveGestureProtoLogProxy
import com.android.quickstep.views.RecentsView
@@ -91,9 +92,11 @@
*/
private var keyboardTaskFocusIndex = -1
+ // TODO (b/397942185): get per-display interface
private val containerInterface: BaseContainerInterface<*, *>
- get() = overviewComponentObserver.containerInterface
+ get() = overviewComponentObserver.getContainerInterface(DEFAULT_DISPLAY)
+ // TODO (b/397942185): get per-display RecentsView
private val visibleRecentsView: RecentsView<*, *>?
get() = containerInterface.getVisibleRecentsView<RecentsView<*, *>>()
@@ -141,35 +144,38 @@
* completion (returns false).
*/
@UiThread
- private fun processNextCommand() {
- val command: CommandInfo =
- commandQueue.firstOrNull()
- ?: run {
- Log.d(TAG, "no pending commands to be executed.")
- return
- }
-
- command.status = CommandStatus.PROCESSING
- Log.d(TAG, "executing command: $command")
-
- if (enableOverviewCommandHelperTimeout()) {
- coroutineScope.launch(dispatcherProvider.main) {
- withTimeout(QUEUE_WAIT_DURATION_IN_MS) {
- executeCommandSuspended(command)
- ensureActive()
- onCommandFinished(command)
- }
+ private fun processNextCommand() =
+ traceSection("OverviewCommandHelper.processNextCommand") {
+ val command: CommandInfo? = commandQueue.firstOrNull()
+ if (command == null) {
+ Log.d(TAG, "no pending commands to be executed.")
+ return@traceSection
}
- } else {
- val result = executeCommand(command, onCallbackResult = { onCommandFinished(command) })
- Log.d(TAG, "command executed: $command with result: $result")
- if (result) {
- onCommandFinished(command)
+
+ command.status = CommandStatus.PROCESSING
+ Log.d(TAG, "executing command: $command")
+
+ if (enableOverviewCommandHelperTimeout()) {
+ coroutineScope.launch(dispatcherProvider.main) {
+ traceSection("OverviewCommandHelper.executeCommandWithTimeout") {
+ withTimeout(QUEUE_WAIT_DURATION_IN_MS) {
+ executeCommandSuspended(command)
+ ensureActive()
+ onCommandFinished(command)
+ }
+ }
+ }
} else {
- Log.d(TAG, "waiting for command callback: $command")
+ val result =
+ executeCommand(command, onCallbackResult = { onCommandFinished(command) })
+ Log.d(TAG, "command executed: $command with result: $result")
+ if (result) {
+ onCommandFinished(command)
+ } else {
+ Log.d(TAG, "waiting for command callback: $command")
+ }
}
}
- }
/**
* Executes the task and returns true if next task can be executed. If false, then the next task
@@ -296,7 +302,7 @@
val focusedDisplayId = focusState.focusedDisplayId
val focusedDisplayUIController: TaskbarUIController? =
- if (RecentsDisplayModel.enableOverviewInWindow()) {
+ if (enableOverviewInWindow) {
Log.d(
TAG,
"Querying RecentsDisplayModel for TaskbarUIController for display: $focusedDisplayId",
@@ -389,9 +395,7 @@
return false
}
- val recentsInWindowFlagSet =
- enableFallbackOverviewInWindow() || enableLauncherOverviewInWindow()
- if (!recentsInWindowFlagSet) {
+ if (!enableOverviewInWindow) {
containerInterface.getCreatedContainer()?.rootView?.let { view ->
InteractionJankMonitorWrapper.begin(view, Cuj.CUJ_LAUNCHER_QUICK_SWITCH)
}
@@ -399,6 +403,7 @@
val gestureState =
touchInteractionService.createGestureState(
+ focusedDisplayId,
GestureState.DEFAULT_STATE,
GestureState.TrackpadGestureType.NONE,
)
@@ -421,7 +426,7 @@
transitionInfo: TransitionInfo?,
) {
Log.d(TAG, "recents animation started: $command")
- if (recentsInWindowFlagSet) {
+ if (enableOverviewInWindow) {
containerInterface.getCreatedContainer()?.rootView?.let { view ->
InteractionJankMonitorWrapper.begin(view, Cuj.CUJ_LAUNCHER_QUICK_SWITCH)
}
@@ -431,7 +436,11 @@
logShowOverviewFrom(command.type)
containerInterface.runOnInitBackgroundStateUI {
Log.d(TAG, "recents animation started - onInitBackgroundStateUI: $command")
- interactionHandler.onGestureEnded(0f, PointF())
+ interactionHandler.onGestureEnded(
+ 0f,
+ PointF(),
+ /* horizontalTouchSlopPassed= */ false,
+ )
}
command.removeListener(this)
}
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index bc3de41..7d3a1da 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -21,8 +21,10 @@
import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.launcher3.Flags.enableOverviewOnConnectedDisplays;
import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.quickstep.fallback.window.RecentsWindowFlags.enableLauncherOverviewInWindow;
import static com.android.systemui.shared.system.PackageManagerWrapper.ACTION_PREFERRED_ACTIVITY_CHANGED;
import android.content.ActivityNotFoundException;
@@ -40,7 +42,6 @@
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
-import com.android.launcher3.Flags;
import com.android.launcher3.R;
import com.android.launcher3.dagger.ApplicationContext;
import com.android.launcher3.dagger.LauncherAppComponent;
@@ -49,6 +50,7 @@
import com.android.launcher3.util.DaggerSingletonTracker;
import com.android.launcher3.util.SimpleBroadcastReceiver;
import com.android.quickstep.fallback.window.RecentsDisplayModel;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
import com.android.quickstep.util.ActiveGestureProtoLogProxy;
import com.android.systemui.shared.system.PackageManagerWrapper;
@@ -87,7 +89,7 @@
new CopyOnWriteArrayList<>();
private String mUpdateRegisteredPackage;
- private BaseContainerInterface mContainerInterface;
+ private BaseContainerInterface mDefaultDisplayContainerInterface;
private Intent mOverviewIntent;
private boolean mIsHomeAndOverviewSame;
private boolean mIsDefaultHome;
@@ -175,11 +177,11 @@
// Set assistant visibility to 0 from launcher's perspective, ensures any elements that
// launcher made invisible become visible again before the new activity control helper
// becomes active.
- if (mContainerInterface != null) {
- mContainerInterface.onAssistantVisibilityChanged(0.f);
+ if (mDefaultDisplayContainerInterface != null) {
+ mDefaultDisplayContainerInterface.onAssistantVisibilityChanged(0.f);
}
- if (SEPARATE_RECENTS_ACTIVITY.get() || Flags.enableLauncherOverviewInWindow()) {
+ if (SEPARATE_RECENTS_ACTIVITY.get() || enableLauncherOverviewInWindow.isTrue()) {
mIsDefaultHome = false;
if (defaultHome == null) {
defaultHome = mMyHomeIntent.getComponent();
@@ -193,7 +195,7 @@
if (!mIsHomeDisabled && (defaultHome == null || mIsDefaultHome)) {
// User default home is same as out home app. Use Overview integrated in Launcher.
- mContainerInterface = LauncherActivityInterface.INSTANCE;
+ mDefaultDisplayContainerInterface = LauncherActivityInterface.INSTANCE;
mIsHomeAndOverviewSame = true;
mOverviewIntent = mMyHomeIntent;
mCurrentHomeIntent.setComponent(mMyHomeIntent.getComponent());
@@ -202,12 +204,11 @@
unregisterOtherHomeAppUpdateReceiver();
} else {
// The default home app is a different launcher. Use the fallback Overview instead.
-
- if (Flags.enableLauncherOverviewInWindow() || Flags.enableFallbackOverviewInWindow()) {
- mContainerInterface =
+ if (RecentsWindowFlags.Companion.getEnableOverviewInWindow()) {
+ mDefaultDisplayContainerInterface =
mRecentsDisplayModel.getFallbackWindowInterface(DEFAULT_DISPLAY);
} else {
- mContainerInterface = FallbackActivityInterface.INSTANCE;
+ mDefaultDisplayContainerInterface = FallbackActivityInterface.INSTANCE;
}
mIsHomeAndOverviewSame = false;
mOverviewIntent = mFallbackIntent;
@@ -296,12 +297,16 @@
}
/**
- * Get the current control helper for managing interactions to the overview container.
+ * Get the current control helper for managing interactions to the overview container for
+ * the given displayId.
*
- * @return the current control helper
+ * @param displayId The display id
+ * @return the control helper for the given display
*/
- public BaseContainerInterface<?,?> getContainerInterface() {
- return mContainerInterface;
+ public BaseContainerInterface<?, ?> getContainerInterface(int displayId) {
+ return (enableOverviewOnConnectedDisplays() && displayId != DEFAULT_DISPLAY)
+ ? mRecentsDisplayModel.getFallbackWindowInterface(displayId)
+ : mDefaultDisplayContainerInterface;
}
public void dump(PrintWriter pw) {
diff --git a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
index f47937c..e9f7024 100644
--- a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -1,5 +1,7 @@
package com.android.quickstep;
+import static android.view.Display.DEFAULT_DISPLAY;
+
import static com.android.launcher3.taskbar.TaskbarThresholdUtils.getFromNavThreshold;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
@@ -210,8 +212,9 @@
}
private RecentsViewContainer getRecentsViewContainer() {
+ // TODO (b/400647896): support per-display container in e2e tests
return OverviewComponentObserver.INSTANCE.get(mContext)
- .getContainerInterface().getCreatedContainer();
+ .getContainerInterface(DEFAULT_DISPLAY).getCreatedContainer();
}
@Override
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index f506039..cc5b2da 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -38,9 +38,12 @@
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.statehandlers.DesktopVisibilityController;
+import com.android.launcher3.util.DaggerSingletonTracker;
import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.util.SplitConfigurationOptions;
+import com.android.launcher3.util.window.WindowManagerProxy;
import com.android.quickstep.util.DesktopTask;
+import com.android.quickstep.util.ExternalDisplaysKt;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.util.SingleTask;
import com.android.quickstep.util.SplitTask;
@@ -69,7 +72,10 @@
/**
* Manages the recent task list from the system, caching it as necessary.
*/
-public class RecentTasksList {
+// TODO: b/401602554 - Consider letting [DesktopTasksController] notify [RecentTasksController] of
+// desk changes to trigger [IRecentTasksListener.onRecentTasksChanged()], instead of implementing
+// [DesktopVisibilityListener].
+public class RecentTasksList implements WindowManagerProxy.DesktopVisibilityListener {
private static final TaskLoadResult INVALID_RESULT = new TaskLoadResult(-1, false, 0);
@@ -77,6 +83,7 @@
private final KeyguardManager mKeyguardManager;
private final LooperExecutor mMainThreadExecutor;
private final SystemUiProxy mSysUiProxy;
+ private final DesktopVisibilityController mDesktopVisibilityController;
// The list change id, increments as the task list changes in the system
private int mChangeId;
@@ -94,13 +101,16 @@
public RecentTasksList(Context context, LooperExecutor mainThreadExecutor,
KeyguardManager keyguardManager, SystemUiProxy sysUiProxy,
- TopTaskTracker topTaskTracker) {
+ TopTaskTracker topTaskTracker,
+ DesktopVisibilityController desktopVisibilityController,
+ DaggerSingletonTracker tracker) {
mContext = context;
mMainThreadExecutor = mainThreadExecutor;
mKeyguardManager = keyguardManager;
mChangeId = 1;
mSysUiProxy = sysUiProxy;
- sysUiProxy.registerRecentTasksListener(new IRecentTasksListener.Stub() {
+ mDesktopVisibilityController = desktopVisibilityController;
+ final IRecentTasksListener recentTasksListener = new IRecentTasksListener.Stub() {
@Override
public void onRecentTasksChanged() throws RemoteException {
mMainThreadExecutor.execute(RecentTasksList.this::onRecentTasksChanged);
@@ -146,7 +156,19 @@
topTaskTracker.onVisibleTasksChanged(visibleTasks);
});
}
- });
+ };
+
+ mSysUiProxy.registerRecentTasksListener(recentTasksListener);
+ tracker.addCloseable(
+ () -> mSysUiProxy.unregisterRecentTasksListener(recentTasksListener));
+
+ if (DesktopModeStatus.enableMultipleDesktops(mContext)) {
+ mDesktopVisibilityController.registerDesktopVisibilityListener(
+ this);
+ tracker.addCloseable(
+ () -> mDesktopVisibilityController.unregisterDesktopVisibilityListener(this));
+ }
+
// We may receive onRunningTaskAppeared events later for tasks which have already been
// included in the list returned by mSysUiProxy.getRunningTasks(), or may receive
// onRunningTaskVanished for tasks not included in the returned list. These cases will be
@@ -285,6 +307,27 @@
return mRunningTasks;
}
+ @Override
+ public void onDeskAdded(int displayId, int deskId) {
+ onRecentTasksChanged();
+ }
+
+ @Override
+ public void onDeskRemoved(int displayId, int deskId) {
+ onRecentTasksChanged();
+ }
+
+ @Override
+ public void onActiveDeskChanged(int displayId, int newActiveDesk, int oldActiveDesk) {
+ // Should desk activation changes lead to the invalidation of the loaded tasks? The cases
+ // are:
+ // - Switching from one active desk to another.
+ // - Switching from out of a desk session into an active desk.
+ // - Switching from an active desk to a non-desk session.
+ // These changes don't affect the list of desks, nor their contents, so let's ignore them
+ // for now.
+ }
+
private void onRunningTaskAppeared(RunningTaskInfo taskInfo) {
// Make sure this task is not already in the list
for (RunningTaskInfo existingTask : mRunningTasks) {
@@ -460,7 +503,8 @@
Map<Integer, List<Task>> perDisplayTasks = new HashMap<>();
for (TaskInfo taskInfo : recentTaskInfo.getTaskInfoList()) {
Task task = createTask(taskInfo, minimizedTaskIds);
- List<Task> tasks = perDisplayTasks.computeIfAbsent(taskInfo.displayId,
+ List<Task> tasks = perDisplayTasks.computeIfAbsent(
+ ExternalDisplaysKt.getDisplayId(task),
k -> new ArrayList<>());
tasks.add(task);
}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
index d7152b5..ecde37b 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -34,9 +34,9 @@
import androidx.annotation.NonNull;
import androidx.annotation.UiThread;
-import com.android.launcher3.Flags;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.Preconditions;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
import com.android.quickstep.util.ActiveGestureProtoLogProxy;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
@@ -113,8 +113,8 @@
boolean isOpeningHome = Arrays.stream(appTargets).filter(app -> app.mode == MODE_OPENING
&& app.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME)
.count() > 0;
- if (appCount == 0 && (!(Flags.enableFallbackOverviewInWindow()
- || Flags.enableLauncherOverviewInWindow()) || isOpeningHome)) {
+ if (appCount == 0 && (!RecentsWindowFlags.Companion.getEnableOverviewInWindow()
+ || isOpeningHome)) {
ActiveGestureProtoLogProxy.logOnRecentsAnimationStartCancelled();
// Edge case, if there are no closing app targets, then Launcher has nothing to handle
notifyAnimationCanceled();
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index 74f24ee..6710096 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -325,13 +325,6 @@
}
/**
- * @return the display id for the display that Launcher is running on.
- */
- public int getDisplayId() {
- return DEFAULT_DISPLAY;
- }
-
- /**
* @return whether the user has completed setup wizard
*/
public boolean isUserSetupComplete() {
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
index cf7e499..0deb1ca 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
@@ -59,6 +59,10 @@
if (!DesktopModeStatus.canEnterDesktopMode(context)) {
return false;
}
+ // TODO: b/400866688 - Check if we need to update this such that for an empty desk, we
+ // receive a list of apps that contain only the Launcher and the `DesktopWallpaperActivity`
+ // and both are fullscreen windowing mode. A desk can also have transparent modals and
+ // immersive apps which may not have a "freeform" windowing mode.
for (RemoteAnimationTarget target : apps) {
if (target.windowConfiguration.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
return true;
diff --git a/quickstep/src/com/android/quickstep/RecentsFilterState.java b/quickstep/src/com/android/quickstep/RecentsFilterState.java
index c4b0f25..1808a97 100644
--- a/quickstep/src/com/android/quickstep/RecentsFilterState.java
+++ b/quickstep/src/com/android/quickstep/RecentsFilterState.java
@@ -18,6 +18,7 @@
import androidx.annotation.Nullable;
+import com.android.quickstep.util.DesksUtils;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.views.TaskViewType;
import com.android.systemui.shared.recents.model.Task;
@@ -117,37 +118,43 @@
* Returns a predicate for filtering out GroupTasks by package name.
*
* @param packageName package name to filter GroupTasks by
- * if null, Predicate filters out desktop tasks with no non-minimized tasks.
+ * if null, Predicate filters out desktop tasks with no non-minimized tasks,
+ * unless the multiple desks feature is enabled, which allows empty desks.
*/
public static Predicate<GroupTask> getFilter(@Nullable String packageName) {
if (packageName == null) {
- return getEmptyDesktopTaskFilter();
+ return getDesktopTaskFilter();
}
return (groupTask) -> (groupTask.containsPackage(packageName)
- && !isDestopTaskWithMinimizedTasksOnly(groupTask));
+ && shouldKeepGroupTask(groupTask));
}
/**
- * Returns a predicate that filters out desk tasks that contain no non-minimized desktop tasks.
+ * Returns a predicate that filters out desk tasks that contain no non-minimized desktop tasks,
+ * unless the multiple desks feature is enabled, which allows empty desks.
*/
- public static Predicate<GroupTask> getEmptyDesktopTaskFilter() {
- return (groupTask -> !isDestopTaskWithMinimizedTasksOnly(groupTask));
+ public static Predicate<GroupTask> getDesktopTaskFilter() {
+ return (groupTask -> shouldKeepGroupTask(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.
+ * Returns true if the given `groupTask` should be kept, and false if it should be filtered out.
+ * Desks will be filtered out if they are empty unless the multiple desks feature is enabled.
*
* @param groupTask The group task to check.
*/
- static boolean isDestopTaskWithMinimizedTasksOnly(GroupTask groupTask) {
+ private static boolean shouldKeepGroupTask(GroupTask groupTask) {
if (groupTask.taskViewType != TaskViewType.DESKTOP) {
- return false;
+ return true;
}
+
+ if (DesksUtils.areMultiDesksFlagsEnabled()) {
+ return true;
+ }
+
return groupTask.getTasks().stream()
- .filter(task -> !task.isMinimized)
- .toList().isEmpty();
+ .anyMatch(task -> !task.isMinimized);
}
/**
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 1d83d42..e1adf3d 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -43,6 +43,7 @@
import com.android.launcher3.graphics.ThemeManager;
import com.android.launcher3.graphics.ThemeManager.ThemeChangeListener;
import com.android.launcher3.icons.IconProvider;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.util.DaggerSingletonObject;
import com.android.launcher3.util.DaggerSingletonTracker;
import com.android.launcher3.util.DisplayController;
@@ -61,6 +62,8 @@
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
+import dagger.Lazy;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -72,8 +75,6 @@
import javax.inject.Inject;
-import dagger.Lazy;
-
/**
* Singleton class to load and manage recents model.
*/
@@ -104,12 +105,14 @@
DisplayController displayController,
LockedUserState lockedUserState,
Lazy<ThemeManager> themeManagerLazy,
+ DesktopVisibilityController desktopVisibilityController,
DaggerSingletonTracker tracker
) {
// Lazily inject the ThemeManager and access themeManager once the device is
// unlocked. See b/393248495 for details.
this(context, new IconProvider(context), systemUiProxy, topTaskTracker,
- displayController, lockedUserState,themeManagerLazy, tracker);
+ displayController, lockedUserState, themeManagerLazy, desktopVisibilityController,
+ tracker);
}
@SuppressLint("VisibleForTests")
@@ -120,6 +123,7 @@
DisplayController displayController,
LockedUserState lockedUserState,
Lazy<ThemeManager> themeManagerLazy,
+ DesktopVisibilityController desktopVisibilityController,
DaggerSingletonTracker tracker) {
this(context,
new RecentTasksList(
@@ -127,7 +131,7 @@
MAIN_EXECUTOR,
context.getSystemService(KeyguardManager.class),
systemUiProxy,
- topTaskTracker),
+ topTaskTracker, desktopVisibilityController, tracker),
new TaskIconCache(context, RECENTS_MODEL_EXECUTOR, iconProvider, displayController),
new TaskThumbnailCache(context, RECENTS_MODEL_EXECUTOR),
iconProvider,
@@ -205,7 +209,7 @@
@Override
public int getTasks(@Nullable Consumer<List<GroupTask>> callback) {
return mTaskList.getTasks(false /* loadKeysOnly */, callback,
- RecentsFilterState.getEmptyDesktopTaskFilter());
+ RecentsFilterState.getDesktopTaskFilter());
}
/**
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.kt b/quickstep/src/com/android/quickstep/SystemUiProxy.kt
index 0f6649b..cd87d93 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.kt
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.kt
@@ -683,6 +683,20 @@
fun showExpandedView() =
executeWithErrorLog({ "Failed call showExpandedView" }) { bubbles?.showExpandedView() }
+ /** Tells SysUI to show the bubble drop target. */
+ @JvmOverloads
+ fun showBubbleDropTarget(show: Boolean, bubbleBarLocation: BubbleBarLocation? = null) =
+ executeWithErrorLog({ "Failed call showDropTarget" }) {
+ bubbles?.showDropTarget(show, bubbleBarLocation)
+ }
+
+ /** Tells SysUI to move the dragged bubble to full screen. */
+ fun moveDraggedBubbleToFullscreen(key: String, dropLocation: Point) {
+ executeWithErrorLog({ "Failed to call moveDraggedBubbleToFullscreen"}) {
+ bubbles?.moveDraggedBubbleToFullscreen(key, dropLocation)
+ }
+ }
+
//
// Splitscreen
//
@@ -1082,18 +1096,26 @@
// Desktop Mode
//
/** Calls shell to create a new desk (if possible) on the display whose ID is `displayId`. */
- fun createDesktop(displayId: Int) =
+ fun createDesk(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?) =
+ fun activateDesk(deskId: Int, transition: RemoteTransition?) =
executeWithErrorLog({ "Failed call activateDesk" }) {
desktopMode?.activateDesk(deskId, transition)
}
+ /** Calls shell to remove the desk whose ID is `deskId`. */
+ fun removeDesk(deskId: Int) =
+ executeWithErrorLog({ "Failed call removeDesk" }) { desktopMode?.removeDesk(deskId) }
+
+ /** Calls shell to remove all the available desks on all displays. */
+ fun removeAllDesks() =
+ executeWithErrorLog({ "Failed call removeAllDesks" }) { desktopMode?.removeAllDesks() }
+
/** Call shell to show all apps active on the desktop */
fun showDesktopApps(displayId: Int, transition: RemoteTransition?) =
executeWithErrorLog({ "Failed call showDesktopApps" }) {
@@ -1145,9 +1167,9 @@
}
/** Call shell to remove the desktop that is on given `displayId` */
- fun removeDesktop(displayId: Int) =
- executeWithErrorLog({ "Failed call removeDesktop" }) {
- desktopMode?.removeDesktop(displayId)
+ fun removeDefaultDeskInDisplay(displayId: Int) =
+ executeWithErrorLog({ "Failed call removeDefaultDeskInDisplay" }) {
+ desktopMode?.removeDefaultDeskInDisplay(displayId)
}
/** Call shell to move a task with given `taskId` to external display. */
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index cb11afa..1c7f23c 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -42,12 +42,12 @@
import androidx.annotation.UiThread;
import com.android.internal.util.ArrayUtils;
-import com.android.launcher3.Flags;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.taskbar.TaskbarUIController;
import com.android.launcher3.util.DisplayController;
import com.android.quickstep.fallback.window.RecentsDisplayModel;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
import com.android.quickstep.fallback.window.RecentsWindowManager;
import com.android.quickstep.util.ActiveGestureProtoLogProxy;
import com.android.quickstep.util.SystemUiFlagUtils;
@@ -63,7 +63,6 @@
public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener {
public static final boolean SHELL_TRANSITIONS_ROTATION =
SystemProperties.getBoolean("persist.wm.debug.shell_transit_rotate", false);
-
private final Context mCtx;
private RecentsAnimationController mController;
private RecentsAnimationCallbacks mCallbacks;
@@ -313,12 +312,11 @@
}
if(containerInterface.getCreatedContainer() instanceof RecentsWindowManager
- && (Flags.enableFallbackOverviewInWindow()
- || Flags.enableLauncherOverviewInWindow())) {
+ && RecentsWindowFlags.Companion.getEnableOverviewInWindow()) {
mRecentsAnimationStartPending = getSystemUiProxy().startRecentsActivity(intent, options,
mCallbacks, gestureState.useSyntheticRecentsTransition());
RecentsDisplayModel.getINSTANCE().get(mCtx)
- .getRecentsWindowManager(mDeviceState.getDisplayId())
+ .getRecentsWindowManager(gestureState.getDisplayId())
.startRecentsWindow(mCallbacks);
} else {
mRecentsAnimationStartPending = getSystemUiProxy().startRecentsActivity(intent,
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index a594e49..df66a5e 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -42,7 +42,7 @@
import com.android.launcher3.util.ResourceBasedOverride;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.Snackbar;
-import com.android.quickstep.task.util.TaskOverlayHelper;
+import com.android.quickstep.recents.domain.usecase.ThumbnailPosition;
import com.android.quickstep.util.RecentsOrientedState;
import com.android.quickstep.views.DesktopTaskView;
import com.android.quickstep.views.GroupedTaskView;
@@ -52,6 +52,7 @@
import com.android.quickstep.views.TaskContainer;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.ThumbnailData;
import java.util.ArrayList;
import java.util.List;
@@ -134,43 +135,50 @@
private T mActionsView;
protected ImageActionsApi mImageApi;
- protected TaskOverlayHelper mHelper;
+ private ThumbnailData mThumbnailData = null;
protected TaskOverlay(TaskContainer taskContainer) {
mApplicationContext = taskContainer.getTaskView().getContext().getApplicationContext();
mTaskContainer = taskContainer;
- if (enableRefactorTaskThumbnail()) {
- mHelper = new TaskOverlayHelper(mTaskContainer.getTask(), this);
- }
mImageApi = new ImageActionsApi(mApplicationContext, this::getThumbnail);
}
- /**
- * Initialize the overlay when a Task is bound to the TaskView.
- */
- public void init() {
- if (enableRefactorTaskThumbnail()) {
- mHelper.init();
- }
- }
-
- /**
- * Destroy the overlay when the TaskView is recycled.
- */
- public void destroy() {
- if (enableRefactorTaskThumbnail()) {
- mHelper.destroy();
- }
+ public void setThumbnailState(@Nullable ThumbnailData thumbnailData) {
+ mThumbnailData = thumbnailData;
}
protected @Nullable Bitmap getThumbnail() {
- return enableRefactorTaskThumbnail() ? mHelper.getEnabledState().getThumbnail()
- : mTaskContainer.getThumbnailViewDeprecated().getThumbnail();
+ if (enableRefactorTaskThumbnail()) {
+ return mThumbnailData == null ? null : mThumbnailData.getThumbnail();
+ } else {
+ return mTaskContainer.getThumbnailViewDeprecated().getThumbnail();
+ }
+ }
+ /**
+ * Returns whether the snapshot is real. If the device is locked for the user of the task,
+ * the snapshot used will be an app-theme generated snapshot instead of a real snapshot.
+ */
+ protected boolean isRealSnapshot() {
+ if (enableRefactorTaskThumbnail()) {
+ if (mThumbnailData == null) return false;
+
+ return mThumbnailData.isRealSnapshot && !mTaskContainer.getTask().isLocked;
+ } else {
+ return mTaskContainer.getThumbnailViewDeprecated().isRealSnapshot();
+ }
}
- protected boolean isRealSnapshot() {
- return enableRefactorTaskThumbnail() ? mHelper.getEnabledState().isRealSnapshot()
- : mTaskContainer.getThumbnailViewDeprecated().isRealSnapshot();
+ /**
+ * Returns whether the snapshot is rotated compared to the current task orientation.
+ */
+ public boolean isThumbnailRotationDifferentFromTask() {
+ if (enableRefactorTaskThumbnail()) {
+ ThumbnailPosition thumbnailPosition = mTaskContainer.getThumbnailPosition();
+ return thumbnailPosition != null && thumbnailPosition.isRotated();
+ }
+
+ return mTaskContainer.getThumbnailViewDeprecated()
+ .isThumbnailRotationDifferentFromTask();
}
protected T getActionsView() {
@@ -316,9 +324,16 @@
// inverse tells us where the view would be in the bitmaps coordinates. The insets are
// the difference between the bitmap bounds and the projected view bounds.
Matrix boundsToBitmapSpace = new Matrix();
- Matrix thumbnailMatrix = enableRefactorTaskThumbnail()
- ? mHelper.getThumbnailMatrix()
- : mTaskContainer.getThumbnailViewDeprecated().getThumbnailMatrix();
+ Matrix thumbnailMatrix;
+ if (enableRefactorTaskThumbnail()) {
+ if (mTaskContainer.getThumbnailPosition() != null) {
+ thumbnailMatrix = mTaskContainer.getThumbnailPosition().getMatrix();
+ } else {
+ thumbnailMatrix = Matrix.IDENTITY_MATRIX;
+ }
+ } else {
+ thumbnailMatrix = mTaskContainer.getThumbnailViewDeprecated().getThumbnailMatrix();
+ }
thumbnailMatrix.invert(boundsToBitmapSpace);
RectF boundsInBitmapSpace = new RectF();
boundsToBitmapSpace.mapRect(boundsInBitmapSpace, viewRect);
@@ -359,13 +374,10 @@
private class ScreenshotSystemShortcut extends SystemShortcut {
- private final RecentsViewContainer mContainer;
-
ScreenshotSystemShortcut(RecentsViewContainer container, ItemInfo itemInfo,
View originalView) {
super(R.drawable.ic_screenshot, R.string.action_screenshot, container, itemInfo,
originalView);
- mContainer = container;
}
@Override
diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index f92581e..bc46ace 100644
--- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -20,7 +20,9 @@
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.view.Surface.ROTATION_0;
+import static com.android.launcher3.Flags.enableGridOnlyOverview;
import static com.android.launcher3.Flags.enableRefactorTaskThumbnail;
+import static com.android.launcher3.Flags.enableShowEnabledShortcutsInAccessibilityMenu;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_CLOSE_APP_TAP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_FREE_FORM_TAP;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
@@ -42,7 +44,6 @@
import androidx.annotation.Nullable;
import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Flags;
import com.android.launcher3.R;
import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
import com.android.launcher3.model.WellbeingModel;
@@ -345,15 +346,20 @@
boolean isTaskSplitNotSupported = !task.isDockable ||
(intentFlags & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0;
boolean hideForExistingMultiWindow = container.getDeviceProfile().isMultiWindowMode;
- boolean isLargeTile = deviceProfile.isTablet && taskView.isLargeTile();
- boolean isTaskInExpectedScrollPosition =
- recentsView.isTaskInExpectedScrollPosition(taskView);
- if (notEnoughTasksToSplit || isTaskSplitNotSupported || hideForExistingMultiWindow
- || (isLargeTile && isTaskInExpectedScrollPosition)) {
+ if (notEnoughTasksToSplit || isTaskSplitNotSupported || hideForExistingMultiWindow) {
return null;
}
+ if (!enableShowEnabledShortcutsInAccessibilityMenu()) {
+ boolean isLargeTile = deviceProfile.isTablet && taskView.isLargeTile();
+ boolean isTaskInExpectedScrollPosition =
+ recentsView.isTaskInExpectedScrollPosition(taskView);
+ if (isLargeTile && isTaskInExpectedScrollPosition) {
+ return null;
+ }
+ }
+
return orientationHandler.getSplitPositionOptions(deviceProfile)
.stream()
.map((Function<SplitPositionOption, SystemShortcut>) option ->
@@ -500,16 +506,23 @@
@Override
public List<SystemShortcut> getShortcuts(RecentsViewContainer container,
TaskContainer taskContainer) {
- boolean isTablet = container.getDeviceProfile().isTablet;
- boolean isGridOnlyOverview = isTablet && Flags.enableGridOnlyOverview();
- // Extra conditions if it's not grid-only overview
- if (!isGridOnlyOverview) {
- RecentsOrientedState orientedState = taskContainer.getTaskView().getOrientedState();
- boolean isFakeLandscape = !orientedState.isRecentsActivityRotationAllowed()
- && orientedState.getTouchRotation() != ROTATION_0;
- if (!isFakeLandscape) {
+ if (enableShowEnabledShortcutsInAccessibilityMenu()) {
+ if (!taskContainer.getOverlay().isRealSnapshot()) {
return null;
}
+ } else {
+ boolean isTablet = container.getDeviceProfile().isTablet;
+ boolean isGridOnlyOverview = isTablet && enableGridOnlyOverview();
+ // Extra conditions if it's not grid-only overview
+ if (!isGridOnlyOverview) {
+ RecentsOrientedState orientedState = taskContainer.getTaskView()
+ .getOrientedState();
+ boolean isFakeLandscape = !orientedState.isRecentsActivityRotationAllowed()
+ && orientedState.getTouchRotation() != ROTATION_0;
+ if (!isFakeLandscape) {
+ return null;
+ }
+ }
}
SystemShortcut screenshotShortcut = taskContainer.getOverlay().getScreenshotShortcut(
@@ -527,10 +540,39 @@
@Override
public List<SystemShortcut> getShortcuts(RecentsViewContainer container,
TaskContainer taskContainer) {
- boolean isTablet = container.getDeviceProfile().isTablet;
- boolean isGridOnlyOverview = isTablet && Flags.enableGridOnlyOverview();
- if (!isGridOnlyOverview) {
- return null;
+ if (enableShowEnabledShortcutsInAccessibilityMenu()) {
+ if (!taskContainer.getOverlay().isRealSnapshot()) {
+ return null;
+ }
+
+ // Modal only works with grid size tiles with enableGridOnlyOverview enabled on
+ // tablets / foldables. With enableGridOnlyOverview off, for large tiles it works,
+ // but the tile needs to be in the center of Recents / Overview.
+ boolean isTablet = container.getDeviceProfile().isTablet;
+ RecentsView recentsView = container.getOverviewPanel();
+ boolean isLargeTileInCenterOfOverview = taskContainer.getTaskView().isLargeTile()
+ && recentsView.isFocusedTaskInExpectedScrollPosition();
+ if (isTablet
+ && !isLargeTileInCenterOfOverview
+ && !enableGridOnlyOverview()) {
+ return null;
+ }
+
+ boolean isFakeLandscape = !taskContainer.getTaskView().getPagedOrientationHandler()
+ .isLayoutNaturalToLauncher();
+ if (isFakeLandscape) {
+ return null;
+ }
+
+ if (taskContainer.getOverlay().isThumbnailRotationDifferentFromTask()) {
+ return null;
+ }
+ } else {
+ boolean isTablet = container.getDeviceProfile().isTablet;
+ boolean isGridOnlyOverview = isTablet && enableGridOnlyOverview();
+ if (!isGridOnlyOverview) {
+ return null;
+ }
}
SystemShortcut modalStateSystemShortcut =
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 37c2d1c..d161d45 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.enableDesktopExplodedView;
import static com.android.launcher3.Flags.enableGridOnlyOverview;
import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
@@ -201,6 +202,9 @@
recentsView.getSizeStrategy(), targets, forDesktop);
if (forDesktop) {
remoteTargetHandles = gluer.assignTargetsForDesktop(targets, transitionInfo);
+ if (enableDesktopExplodedView()) {
+ ((DesktopTaskView) taskView).setRemoteTargetHandles(remoteTargetHandles);
+ }
} else if (taskView.containsMultipleTasks()) {
remoteTargetHandles = gluer.assignTargetsForSplitScreen(targets,
((GroupedTaskView) taskView).getSplitBoundsConfig());
@@ -429,9 +433,7 @@
out.addListener(new AnimationSuccessListener() {
@Override
public void onAnimationStart(Animator animation) {
- for (RemoteTargetHandle remoteTargetHandle : remoteTargetHandles) {
- remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(false);
- }
+ recentsView.setDrawBelowRecents(false, remoteTargetHandles);
}
@Override
diff --git a/quickstep/src/com/android/quickstep/TopTaskTracker.java b/quickstep/src/com/android/quickstep/TopTaskTracker.java
index b3d9da3..8116a88 100644
--- a/quickstep/src/com/android/quickstep/TopTaskTracker.java
+++ b/quickstep/src/com/android/quickstep/TopTaskTracker.java
@@ -46,6 +46,7 @@
import com.android.launcher3.util.SplitConfigurationOptions.StageType;
import com.android.launcher3.util.TraceHelper;
import com.android.quickstep.dagger.QuickstepBaseAppComponent;
+import com.android.quickstep.util.DesksUtils;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.Task.TaskKey;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -341,7 +342,8 @@
ArrayList<TaskInfo> tasks = new ArrayList<>(mOrderedTaskList);
// Strip the pinned task and recents task
- tasks.removeIf(t -> t.taskId == mPinnedTaskId || isRecentsTask(t));
+ tasks.removeIf(t -> t.taskId == mPinnedTaskId || isRecentsTask(t)
+ || DesksUtils.isDesktopWallpaperTask(t));
return new CachedTaskInfo(tasks);
}
}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index ffc0770..2bc5626 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -15,6 +15,7 @@
*/
package com.android.quickstep;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_MOVE;
@@ -34,12 +35,14 @@
import static com.android.quickstep.GestureState.DEFAULT_STATE;
import static com.android.quickstep.GestureState.TrackpadGestureType.getTrackpadGestureType;
import static com.android.quickstep.InputConsumer.TYPE_CURSOR_HOVER;
+import static com.android.quickstep.InputConsumer.createNoOpInputConsumer;
import static com.android.quickstep.InputConsumerUtils.newConsumer;
import static com.android.quickstep.InputConsumerUtils.tryCreateAssistantInputConsumer;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import android.app.PendingIntent;
import android.app.Service;
+import android.content.Context;
import android.content.IIntentReceiver;
import android.content.IIntentSender;
import android.content.Intent;
@@ -57,6 +60,7 @@
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.MotionEvent;
+import android.window.DesktopModeFlags;
import androidx.annotation.BinderThread;
import androidx.annotation.NonNull;
@@ -90,10 +94,11 @@
import com.android.quickstep.OverviewCommandHelper.CommandType;
import com.android.quickstep.OverviewComponentObserver.OverviewChangeListener;
import com.android.quickstep.fallback.window.RecentsDisplayModel;
+import com.android.quickstep.fallback.window.RecentsDisplayModel.RecentsDisplayResource;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
import com.android.quickstep.fallback.window.RecentsWindowSwipeHandler;
import com.android.quickstep.inputconsumers.BubbleBarInputConsumer;
import com.android.quickstep.inputconsumers.OneHandedModeInputConsumer;
-import com.android.quickstep.inputconsumers.ResetGestureInputConsumer;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.ActiveGestureLog.CompoundString;
import com.android.quickstep.util.ActiveGestureProtoLogProxy;
@@ -125,6 +130,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
+import java.util.Locale;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -140,6 +146,9 @@
private static final ConstantItem<Boolean> HAS_ENABLED_QUICKSTEP_ONCE = backedUpItem(
"launcher.has_enabled_quickstep_once", false, EncryptionType.ENCRYPTED);
+ private static final DesktopModeFlags.DesktopModeFlag ENABLE_GESTURE_NAV_ON_CONNECTED_DISPLAYS =
+ new DesktopModeFlags.DesktopModeFlag(Flags::enableGestureNavOnConnectedDisplays, false);
+
private final TISBinder mTISBinder = new TISBinder(this);
/**
@@ -294,8 +303,9 @@
@Override
public void enterStageSplitFromRunningApp(boolean leftOrTop) {
executeForTouchInteractionService(tis -> {
+ // TODO (b/397942185): support external displays
RecentsViewContainer container = tis.mOverviewComponentObserver
- .getContainerInterface().getCreatedContainer();
+ .getContainerInterface(DEFAULT_DISPLAY).getCreatedContainer();
if (container != null) {
container.enterStageSplitFromRunningApp(leftOrTop);
}
@@ -549,12 +559,13 @@
private RecentsAnimationDeviceState mDeviceState;
private TaskAnimationManager mTaskAnimationManager;
- private @NonNull InputConsumer mUncheckedConsumer = InputConsumer.NO_OP;
- private @NonNull InputConsumer mConsumer = InputConsumer.NO_OP;
+ private @NonNull InputConsumer mUncheckedConsumer = InputConsumer.DEFAULT_NO_OP;
+ private @NonNull InputConsumer mConsumer = InputConsumer.DEFAULT_NO_OP;
private Choreographer mMainChoreographer;
- private @Nullable ResetGestureInputConsumer mResetGestureInputConsumer;
+ private boolean mUserUnlocked = false;
private GestureState mGestureState = DEFAULT_STATE;
+ private InputMonitorDisplayModel mInputMonitorDisplayModel;
private InputMonitorCompat mInputMonitorCompat;
private InputEventReceiver mInputEventReceiver;
@@ -569,6 +580,8 @@
private DisplayController.DisplayInfoChangeListener mDisplayInfoChangeListener;
+ private RecentsDisplayModel mRecentsDisplayModel;
+
@Override
public void onCreate() {
super.onCreate();
@@ -579,6 +592,7 @@
mMainChoreographer = Choreographer.getInstance();
mDeviceState = RecentsAnimationDeviceState.INSTANCE.get(this);
mRotationTouchHelper = RotationTouchHelper.INSTANCE.get(this);
+ mRecentsDisplayModel = RecentsDisplayModel.getINSTANCE().get(this);
mAllAppsActionManager = new AllAppsActionManager(
this, UI_HELPER_EXECUTOR, this::createAllAppsPendingIntent);
mTrackpadsConnected = new ActiveTrackpadList(this, () -> {
@@ -591,7 +605,7 @@
});
mTaskbarManager = new TaskbarManager(this, mAllAppsActionManager, mNavCallbacks,
- RecentsDisplayModel.getINSTANCE().get(this));
+ mRecentsDisplayModel);
mDesktopAppLaunchTransitionManager =
new DesktopAppLaunchTransitionManager(this, SystemUiProxy.INSTANCE.get(this));
mDesktopAppLaunchTransitionManager.registerTransitions();
@@ -604,9 +618,34 @@
ScreenOnTracker.INSTANCE.get(this).addListener(mScreenOnListener);
}
+ @Nullable
+ private InputEventReceiver getInputEventReceiver(int displayId) {
+ if (ENABLE_GESTURE_NAV_ON_CONNECTED_DISPLAYS.isTrue()) {
+ InputMonitorResource inputMonitorResource = mInputMonitorDisplayModel == null
+ ? null : mInputMonitorDisplayModel.getDisplayResource(displayId);
+ return inputMonitorResource == null ? null : inputMonitorResource.inputEventReceiver;
+ }
+ return mInputEventReceiver;
+ }
+
+ @Nullable
+ private InputMonitorCompat getInputMonitorCompat(int displayId) {
+ if (ENABLE_GESTURE_NAV_ON_CONNECTED_DISPLAYS.isTrue()) {
+ InputMonitorResource inputMonitorResource = mInputMonitorDisplayModel == null
+ ? null : mInputMonitorDisplayModel.getDisplayResource(displayId);
+ return inputMonitorResource == null ? null : inputMonitorResource.inputMonitorCompat;
+ }
+ return mInputMonitorCompat;
+ }
+
private void disposeEventHandlers(String reason) {
Log.d(TAG, "disposeEventHandlers: Reason: " + reason
+ " instance=" + System.identityHashCode(this));
+ if (ENABLE_GESTURE_NAV_ON_CONNECTED_DISPLAYS.isTrue()) {
+ if (mInputMonitorDisplayModel == null) return;
+ mInputMonitorDisplayModel.destroy();
+ return;
+ }
if (mInputEventReceiver != null) {
mInputEventReceiver.dispose();
mInputEventReceiver = null;
@@ -625,10 +664,13 @@
&& (mTrackpadsConnected.isEmpty())) {
return;
}
-
- mInputMonitorCompat = new InputMonitorCompat("swipe-up", mDeviceState.getDisplayId());
- mInputEventReceiver = mInputMonitorCompat.getInputReceiver(Looper.getMainLooper(),
- mMainChoreographer, this::onInputEvent);
+ if (ENABLE_GESTURE_NAV_ON_CONNECTED_DISPLAYS.isTrue()) {
+ mInputMonitorDisplayModel = new InputMonitorDisplayModel(this);
+ } else {
+ mInputMonitorCompat = new InputMonitorCompat("swipe-up", Display.DEFAULT_DISPLAY);
+ mInputEventReceiver = mInputMonitorCompat.getInputReceiver(Looper.getMainLooper(),
+ mMainChoreographer, this::onInputEvent);
+ }
mRotationTouchHelper.updateGestureTouchRegions();
}
@@ -648,11 +690,9 @@
mTaskAnimationManager = new TaskAnimationManager(this, mDeviceState);
mOverviewComponentObserver = OverviewComponentObserver.INSTANCE.get(this);
mOverviewCommandHelper = new OverviewCommandHelper(this,
- mOverviewComponentObserver, mTaskAnimationManager,
- RecentsDisplayModel.getINSTANCE().get(this),
+ mOverviewComponentObserver, mTaskAnimationManager, mRecentsDisplayModel,
SystemUiProxy.INSTANCE.get(this).getFocusState(), mTaskbarManager);
- mResetGestureInputConsumer = new ResetGestureInputConsumer(
- mTaskAnimationManager, mTaskbarManager::getCurrentActivityContext);
+ mUserUnlocked = true;
mInputConsumer.registerInputConsumer();
for (int displayId : mDeviceState.getDisplaysWithSysUIState()) {
onSystemUiFlagsChanged(mDeviceState.getSystemUiStateFlags(displayId), displayId);
@@ -694,8 +734,11 @@
private void onOverviewTargetChanged(boolean isHomeAndOverviewSame) {
mAllAppsActionManager.setHomeAndOverviewSame(isHomeAndOverviewSame);
+ // TODO (b/399089118): how will this work with per-display Taskbars? Is using the
+ // default-display container ok?
RecentsViewContainer newOverviewContainer =
- mOverviewComponentObserver.getContainerInterface().getCreatedContainer();
+ mOverviewComponentObserver.getContainerInterface(
+ DEFAULT_DISPLAY).getCreatedContainer();
if (newOverviewContainer != null) {
if (newOverviewContainer instanceof StatefulActivity activity) {
// This will also call setRecentsViewContainer() internally.
@@ -737,7 +780,8 @@
@UiThread
private void onAssistantVisibilityChanged() {
if (LockedUserState.get(this).isUserUnlocked()) {
- mOverviewComponentObserver.getContainerInterface().onAssistantVisibilityChanged(
+ mOverviewComponentObserver.getContainerInterface(
+ DEFAULT_DISPLAY).onAssistantVisibilityChanged(
mDeviceState.getAssistantVisibility());
}
}
@@ -787,8 +831,9 @@
}
private void onInputEvent(InputEvent ev) {
+ int displayId = ev.getDisplayId();
if (!(ev instanceof MotionEvent)) {
- ActiveGestureProtoLogProxy.logUnknownInputEvent(ev.toString());
+ ActiveGestureProtoLogProxy.logUnknownInputEvent(displayId, ev.toString());
return;
}
MotionEvent event = (MotionEvent) ev;
@@ -797,19 +842,19 @@
TestProtocol.SEQUENCE_TIS, "TouchInteractionService.onInputEvent", event);
if (!LockedUserState.get(this).isUserUnlocked()) {
- ActiveGestureProtoLogProxy.logOnInputEventUserLocked();
+ ActiveGestureProtoLogProxy.logOnInputEventUserLocked(displayId);
return;
}
NavigationMode currentNavMode = mDeviceState.getMode();
if (mGestureStartNavMode != null && mGestureStartNavMode != currentNavMode) {
ActiveGestureProtoLogProxy.logOnInputEventNavModeSwitched(
- mGestureStartNavMode.name(), currentNavMode.name());
+ displayId, mGestureStartNavMode.name(), currentNavMode.name());
event.setAction(ACTION_CANCEL);
} else if (mDeviceState.isButtonNavMode()
&& !mDeviceState.supportsAssistantGestureInButtonNav()
&& !isTrackpadMotionEvent(event)) {
- ActiveGestureProtoLogProxy.logOnInputEventThreeButtonNav();
+ ActiveGestureProtoLogProxy.logOnInputEventThreeButtonNav(displayId);
return;
}
@@ -825,12 +870,15 @@
}
if (mTaskAnimationManager.shouldIgnoreMotionEvents()) {
if (action == ACTION_DOWN || isHoverActionWithoutConsumer) {
- ActiveGestureProtoLogProxy.logOnInputIgnoringFollowingEvents();
+ ActiveGestureProtoLogProxy.logOnInputIgnoringFollowingEvents(displayId);
}
return;
}
}
+ InputMonitorCompat inputMonitorCompat = getInputMonitorCompat(displayId);
+ InputEventReceiver inputEventReceiver = getInputEventReceiver(displayId);
+
if (action == ACTION_DOWN || isHoverActionWithoutConsumer) {
mGestureStartNavMode = currentNavMode;
} else if (action == ACTION_UP || action == ACTION_CANCEL) {
@@ -857,14 +905,18 @@
if (mDeviceState.canTriggerAssistantAction(event)) {
reasonString.append(" and event can trigger assistant action, "
+ "consuming gesture for assistant action");
- mGestureState =
- createGestureState(mGestureState, getTrackpadGestureType(event));
+ mGestureState = createGestureState(
+ displayId, mGestureState, getTrackpadGestureType(event));
mUncheckedConsumer = tryCreateAssistantInputConsumer(
- this, mDeviceState, mInputMonitorCompat, mGestureState, event);
+ this,
+ mDeviceState,
+ inputMonitorCompat,
+ mGestureState,
+ event);
} else {
reasonString.append(" but event cannot trigger Assistant, "
+ "consuming gesture as no-op");
- mUncheckedConsumer = InputConsumer.NO_OP;
+ mUncheckedConsumer = createNoOpInputConsumer(displayId);
}
} else if ((!isOneHandedModeActive && isInSwipeUpTouchRegion)
|| isHoverActionWithoutConsumer || isOnBubbles) {
@@ -875,22 +927,22 @@
// Clone the previous gesture state since onConsumerAboutToBeSwitched might trigger
// onConsumerInactive and wipe the previous gesture state
GestureState prevGestureState = new GestureState(mGestureState);
- GestureState newGestureState = createGestureState(mGestureState,
- getTrackpadGestureType(event));
+ GestureState newGestureState = createGestureState(
+ displayId, mGestureState, getTrackpadGestureType(event));
mConsumer.onConsumerAboutToBeSwitched();
mGestureState = newGestureState;
mConsumer = newConsumer(
this,
- mResetGestureInputConsumer,
+ mUserUnlocked,
mOverviewComponentObserver,
mDeviceState,
prevGestureState,
mGestureState,
mTaskAnimationManager,
- mInputMonitorCompat,
+ inputMonitorCompat,
getSwipeUpHandlerFactory(),
this::onConsumerInactive,
- mInputEventReceiver,
+ inputEventReceiver,
mTaskbarManager,
mSwipeUpProxyProvider,
mOverviewCommandHelper,
@@ -903,51 +955,58 @@
+ "consuming gesture for assistant action"
: "event is a trackpad multi-finger swipe and event can trigger assistant "
+ "action, consuming gesture for assistant action");
- mGestureState = createGestureState(mGestureState, getTrackpadGestureType(event));
+ mGestureState = createGestureState(
+ displayId, mGestureState, getTrackpadGestureType(event));
// Do not change mConsumer as if there is an ongoing QuickSwitch gesture, we
// should not interrupt it. QuickSwitch assumes that interruption can only
// happen if the next gesture is also quick switch.
mUncheckedConsumer = tryCreateAssistantInputConsumer(
- this, mDeviceState, mInputMonitorCompat, mGestureState, event);
+ this, mDeviceState, inputMonitorCompat, mGestureState, event);
} else if (mDeviceState.canTriggerOneHandedAction(event)) {
reasonString.append("event can trigger one-handed action, "
+ "consuming gesture for one-handed action");
// Consume gesture event for triggering one handed feature.
- mUncheckedConsumer = new OneHandedModeInputConsumer(this, mDeviceState,
- InputConsumer.NO_OP, mInputMonitorCompat);
+ mUncheckedConsumer = new OneHandedModeInputConsumer(
+ this,
+ displayId,
+ mDeviceState,
+ InputConsumer.createNoOpInputConsumer(displayId), inputMonitorCompat);
} else {
- mUncheckedConsumer = InputConsumer.NO_OP;
+ mUncheckedConsumer = InputConsumer.createNoOpInputConsumer(displayId);
}
} else {
// Other events
- if (mUncheckedConsumer != InputConsumer.NO_OP) {
+ if (mUncheckedConsumer.getType() != InputConsumer.TYPE_NO_OP) {
// Only transform the event if we are handling it in a proper consumer
mRotationTouchHelper.setOrientationTransformIfNeeded(event);
}
}
- if (mUncheckedConsumer != InputConsumer.NO_OP) {
+ if (mUncheckedConsumer.getType() != InputConsumer.TYPE_NO_OP) {
switch (action) {
case ACTION_DOWN:
- ActiveGestureProtoLogProxy.logOnInputEventActionDown(reasonString);
+ ActiveGestureProtoLogProxy.logOnInputEventActionDown(displayId, reasonString);
// fall through
case ACTION_UP:
ActiveGestureProtoLogProxy.logOnInputEventActionUp(
(int) event.getRawX(),
(int) event.getRawY(),
action,
- MotionEvent.classificationToString(event.getClassification()));
+ MotionEvent.classificationToString(event.getClassification()),
+ displayId);
break;
case ACTION_MOVE:
ActiveGestureProtoLogProxy.logOnInputEventActionMove(
MotionEvent.actionToString(action),
MotionEvent.classificationToString(event.getClassification()),
- event.getPointerCount());
+ event.getPointerCount(),
+ displayId);
break;
default: {
ActiveGestureProtoLogProxy.logOnInputEventGenericAction(
MotionEvent.actionToString(action),
- MotionEvent.classificationToString(event.getClassification()));
+ MotionEvent.classificationToString(event.getClassification()),
+ displayId);
}
}
}
@@ -971,7 +1030,7 @@
}
if (cleanUpConsumer) {
- reset();
+ reset(displayId);
}
traceToken.close();
}
@@ -990,13 +1049,15 @@
return event.isHoverEvent() && event.getSource() == InputDevice.SOURCE_MOUSE;
}
- public GestureState createGestureState(GestureState previousGestureState,
+ public GestureState createGestureState(
+ int displayId,
+ GestureState previousGestureState,
GestureState.TrackpadGestureType trackpadGestureType) {
final GestureState gestureState;
TopTaskTracker.CachedTaskInfo taskInfo;
if (mTaskAnimationManager.isRecentsAnimationRunning()) {
- gestureState = new GestureState(mOverviewComponentObserver,
- ActiveGestureLog.INSTANCE.getLogId());
+ gestureState = new GestureState(
+ mOverviewComponentObserver, displayId, ActiveGestureLog.INSTANCE.getLogId());
TopTaskTracker.CachedTaskInfo previousTaskInfo = previousGestureState.getRunningTask();
// previousTaskInfo can be null iff previousGestureState == GestureState.DEFAULT_STATE
taskInfo = previousTaskInfo != null
@@ -1007,7 +1068,9 @@
gestureState.updatePreviouslyAppearedTaskIds(
previousGestureState.getPreviouslyAppearedTaskIds());
} else {
- gestureState = new GestureState(mOverviewComponentObserver,
+ gestureState = new GestureState(
+ mOverviewComponentObserver,
+ displayId,
ActiveGestureLog.INSTANCE.incrementLogId());
taskInfo = TopTaskTracker.INSTANCE.get(this).getCachedTopTask(false);
gestureState.updateRunningTask(taskInfo);
@@ -1021,10 +1084,9 @@
}
public AbsSwipeUpHandler.Factory getSwipeUpHandlerFactory() {
- boolean recentsInWindow =
- Flags.enableFallbackOverviewInWindow() || Flags.enableLauncherOverviewInWindow();
return mOverviewComponentObserver.isHomeAndOverviewSame()
- ? mLauncherSwipeHandlerFactory : (recentsInWindow
+ ? mLauncherSwipeHandlerFactory
+ : (RecentsWindowFlags.Companion.getEnableOverviewInWindow()
? mRecentsWindowSwipeHandlerFactory : mFallbackSwipeHandlerFactory);
}
@@ -1035,40 +1097,23 @@
*/
private void onConsumerInactive(InputConsumer caller) {
if (mConsumer != null && mConsumer.getActiveConsumerInHierarchy() == caller) {
- reset();
+ reset(caller.getDisplayId());
}
}
- private void reset() {
- mConsumer = mUncheckedConsumer = getDefaultInputConsumer();
+ private void reset(int displayId) {
+ mConsumer = mUncheckedConsumer = InputConsumerUtils.getDefaultInputConsumer(
+ displayId,
+ mUserUnlocked,
+ mTaskAnimationManager,
+ mTaskbarManager,
+ CompoundString.NO_OP);
mGestureState = DEFAULT_STATE;
// By default, use batching of the input events, but check receiver before using in the rare
// case that the monitor was disposed before the swipe settled
- if (mInputEventReceiver != null) {
- mInputEventReceiver.setBatchingEnabled(true);
- }
- }
-
- private @NonNull InputConsumer getDefaultInputConsumer() {
- return getDefaultInputConsumer(CompoundString.NO_OP);
- }
-
- /**
- * Returns the {@link ResetGestureInputConsumer} if user is unlocked, else NO_OP.
- */
- private @NonNull InputConsumer getDefaultInputConsumer(@NonNull CompoundString reasonString) {
- if (mResetGestureInputConsumer != null) {
- reasonString.append(
- "%smResetGestureInputConsumer initialized, using ResetGestureInputConsumer",
- SUBSTRING_PREFIX);
- return mResetGestureInputConsumer;
- } else {
- reasonString.append(
- "%smResetGestureInputConsumer not initialized, using no-op input consumer",
- SUBSTRING_PREFIX);
- // mResetGestureInputConsumer isn't initialized until onUserUnlocked(), so reset to
- // NO_OP until then (we never want these to be null).
- return InputConsumer.NO_OP;
+ InputEventReceiver inputEventReceiver = getInputEventReceiver(displayId);
+ if (inputEventReceiver != null) {
+ inputEventReceiver.setBatchingEnabled(true);
}
}
@@ -1077,8 +1122,10 @@
if (!LockedUserState.get(this).isUserUnlocked()) {
return;
}
+ // TODO (b/399094853): handle config updates for all connected displays (relevant only for
+ // gestures on external displays)
final BaseContainerInterface containerInterface =
- mOverviewComponentObserver.getContainerInterface();
+ mOverviewComponentObserver.getContainerInterface(DEFAULT_DISPLAY);
final RecentsViewContainer container = containerInterface.getCreatedContainer();
if (container == null || container.isStarted()) {
// We only care about the existing background activity.
@@ -1125,23 +1172,33 @@
pw.println("Input state:");
pw.println("\tmInputMonitorCompat=" + mInputMonitorCompat);
pw.println("\tmInputEventReceiver=" + mInputEventReceiver);
+ if (mInputMonitorDisplayModel == null) {
+ pw.println("\tmInputMonitorDisplayModel=null");
+ } else {
+ mInputMonitorDisplayModel.dump("\t", pw);
+ }
DisplayController.INSTANCE.get(this).dump(pw);
- pw.println("TouchState:");
- RecentsViewContainer createdOverviewContainer = mOverviewComponentObserver == null ? null
- : mOverviewComponentObserver.getContainerInterface().getCreatedContainer();
- boolean resumed = mOverviewComponentObserver != null
- && mOverviewComponentObserver.getContainerInterface().isResumed();
- pw.println("\tcreatedOverviewActivity=" + createdOverviewContainer);
- pw.println("\tresumed=" + resumed);
+ for (RecentsDisplayResource resource : mRecentsDisplayModel.getActiveDisplayResources()) {
+ int displayId = resource.getDisplayId();
+ pw.println(String.format(Locale.ENGLISH, "TouchState (displayId %d):", displayId));
+ RecentsViewContainer createdOverviewContainer =
+ mOverviewComponentObserver == null ? null
+ : mOverviewComponentObserver.getContainerInterface(
+ displayId).getCreatedContainer();
+ boolean resumed = mOverviewComponentObserver != null
+ && mOverviewComponentObserver.getContainerInterface(displayId).isResumed();
+ pw.println("\tcreatedOverviewActivity=" + createdOverviewContainer);
+ pw.println("\tresumed=" + resumed);
+ if (createdOverviewContainer != null) {
+ createdOverviewContainer.getDeviceProfile().dump(this, "", pw);
+ }
+ }
pw.println("\tmConsumer=" + mConsumer.getName());
ActiveGestureLog.INSTANCE.dump("", pw);
RecentsModel.INSTANCE.get(this).dump("", pw);
if (mTaskAnimationManager != null) {
mTaskAnimationManager.dump("", pw);
}
- if (createdOverviewContainer != null) {
- createdOverviewContainer.getDeviceProfile().dump(this, "", pw);
- }
mTaskbarManager.dumpLogs("", pw);
DesktopVisibilityController.INSTANCE.get(this).dumpLogs("", pw);
pw.println("ContextualSearchStateManager:");
@@ -1171,4 +1228,53 @@
gestureState, touchTimeMs, mTaskAnimationManager.isRecentsAnimationRunning(),
mInputConsumer, MSDLPlayerWrapper.INSTANCE.get(this));
}
+
+ /**
+ * Helper class that keeps track of external displays and prepares input monitors for each.
+ */
+ private class InputMonitorDisplayModel extends DisplayModel<InputMonitorResource> {
+
+ private InputMonitorDisplayModel(Context context) {
+ super(context);
+ registerDisplayListener();
+ }
+
+ @NonNull
+ @Override
+ public InputMonitorResource createDisplayResource(@NonNull Display display) {
+ return new InputMonitorResource(display.getDisplayId());
+ }
+ }
+
+ private class InputMonitorResource extends DisplayModel.DisplayResource {
+
+ private final int displayId;
+
+ private final InputMonitorCompat inputMonitorCompat;
+ private final InputEventReceiver inputEventReceiver;
+
+ private InputMonitorResource(int displayId) {
+ this.displayId = displayId;
+ inputMonitorCompat = new InputMonitorCompat("swipe-up", displayId);
+ inputEventReceiver = inputMonitorCompat.getInputReceiver(
+ Looper.getMainLooper(),
+ TouchInteractionService.this.mMainChoreographer,
+ TouchInteractionService.this::onInputEvent);
+ }
+
+ @Override
+ public void cleanup() {
+ inputEventReceiver.dispose();
+ inputMonitorCompat.dispose();
+ }
+
+ @Override
+ public void dump(String prefix , PrintWriter writer) {
+ writer.println(prefix + "InputMonitorResource:");
+
+ writer.println(prefix + "\tdisplayId=" + displayId);
+ writer.println(prefix + "\tinputMonitorCompat=" + inputMonitorCompat);
+ writer.println(prefix + "\tinputEventReceiver=" + inputEventReceiver);
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
index 2631efe..554cea2 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
@@ -132,7 +132,8 @@
getOverviewInterpolator(state));
}
if (enableDesktopExplodedView()) {
- setter.setFloat(mRecentsView, DESK_EXPLODE_PROGRESS, showAsGrid ? 1f : 0f,
+ setter.setFloat(mRecentsView, DESK_EXPLODE_PROGRESS,
+ state.showExplodedDesktopView() ? 1f : 0f,
getOverviewInterpolator(state));
}
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index f426bf5..29b6626 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -30,7 +30,6 @@
import androidx.annotation.Nullable;
import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.Flags;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.desktop.DesktopRecentsTransitionController;
@@ -46,6 +45,7 @@
import com.android.quickstep.GestureState;
import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
import com.android.quickstep.fallback.window.RecentsDisplayModel;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.util.SingleTask;
import com.android.quickstep.util.SplitSelectStateController;
@@ -80,9 +80,10 @@
@Override
public BaseContainerInterface<RecentsState, ?> getContainerInterface(int displayId) {
- return (Flags.enableFallbackOverviewInWindow() || Flags.enableLauncherOverviewInWindow())
+ return RecentsWindowFlags.Companion.getEnableOverviewInWindow()
? RecentsDisplayModel.getINSTANCE().get(mContext)
- .getFallbackWindowInterface(displayId) : FallbackActivityInterface.INSTANCE;
+ .getFallbackWindowInterface(displayId)
+ : FallbackActivityInterface.INSTANCE;
}
@Override
@@ -136,7 +137,8 @@
if (tv != null) {
PendingAnimation pa = new PendingAnimation(TASK_DISMISS_DURATION);
createTaskDismissAnimation(pa, tv, true, false,
- TASK_DISMISS_DURATION, false /* dismissingForSplitSelection*/);
+ TASK_DISMISS_DURATION, false /* dismissingForSplitSelection*/,
+ false /* isExpressiveDismiss */);
pa.addEndListener(e -> setCurrentTask(-1));
AnimatorPlaybackController controller = pa.createPlaybackController();
controller.dispatchOnStart();
@@ -289,10 +291,8 @@
}
// disabling this so app icons aren't drawn on top of recent tasks.
- if (isOverlayEnabled && !(Flags.enableFallbackOverviewInWindow()
- || Flags.enableLauncherOverviewInWindow())) {
- runActionOnRemoteHandles(remoteTargetHandle ->
- remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true));
+ if (isOverlayEnabled && !RecentsWindowFlags.Companion.getEnableOverviewInWindow()) {
+ mBlurUtils.setDrawLiveTileBelowRecents(true);
}
}
@@ -302,6 +302,7 @@
if (enabled) {
RecentsState state = mContainer.getStateManager().getState();
setDisallowScrollToClearAll(!state.hasClearAllButton());
+ setDisallowScrollToAddDesk(!state.hasAddDeskButton());
}
}
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsState.java b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
index f722c5d..2c1a4eb 100644
--- a/quickstep/src/com/android/quickstep/fallback/RecentsState.java
+++ b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
@@ -15,6 +15,7 @@
*/
package com.android.quickstep.fallback;
+import static com.android.launcher3.Flags.enableDesktopExplodedView;
import static com.android.launcher3.Flags.enableDesktopWindowingCarouselDetach;
import static com.android.launcher3.LauncherState.FLAG_CLOSE_POPUPS;
import static com.android.launcher3.uioverrides.states.BackgroundAppState.getOverviewScaleAndOffsetForBackgroundState;
@@ -46,16 +47,18 @@
private static final int FLAG_TASK_THUMBNAIL_SPLASH = BaseState.getFlag(8);
private static final int FLAG_DETACH_DESKTOP_CAROUSEL = BaseState.getFlag(9);
private static final int FLAG_ADD_DESK_BUTTON = BaseState.getFlag(10);
+ private static final int FLAG_SHOW_EXPLODED_DESKTOP_VIEW = BaseState.getFlag(11);
private static final RecentsState[] sAllStates = new RecentsState[6];
public static final RecentsState DEFAULT = new RecentsState(0,
FLAG_DISABLE_RESTORE | FLAG_CLEAR_ALL_BUTTON | FLAG_OVERVIEW_ACTIONS | FLAG_SHOW_AS_GRID
| FLAG_SCRIM | FLAG_LIVE_TILE | FLAG_RECENTS_VIEW_VISIBLE
- | FLAG_ADD_DESK_BUTTON);
+ | FLAG_ADD_DESK_BUTTON | FLAG_SHOW_EXPLODED_DESKTOP_VIEW);
public static final RecentsState MODAL_TASK = new ModalState(1,
FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_ACTIONS | FLAG_MODAL
- | FLAG_SHOW_AS_GRID | FLAG_SCRIM | FLAG_LIVE_TILE | FLAG_RECENTS_VIEW_VISIBLE);
+ | FLAG_SHOW_AS_GRID | FLAG_SCRIM | FLAG_LIVE_TILE | FLAG_RECENTS_VIEW_VISIBLE
+ | FLAG_SHOW_EXPLODED_DESKTOP_VIEW);
public static final RecentsState BACKGROUND_APP = new BackgroundAppState(2,
FLAG_DISABLE_RESTORE | FLAG_NON_INTERACTIVE | FLAG_FULL_SCREEN
| FLAG_RECENTS_VIEW_VISIBLE | FLAG_TASK_THUMBNAIL_SPLASH
@@ -64,7 +67,7 @@
public static final RecentsState BG_LAUNCHER = new LauncherState(4, 0);
public static final RecentsState OVERVIEW_SPLIT_SELECT = new RecentsState(5,
FLAG_SHOW_AS_GRID | FLAG_SCRIM | FLAG_RECENTS_VIEW_VISIBLE | FLAG_CLOSE_POPUPS
- | FLAG_DISABLE_RESTORE);
+ | FLAG_DISABLE_RESTORE | FLAG_SHOW_EXPLODED_DESKTOP_VIEW);
/** Returns the corresponding RecentsState from ordinal provided */
public static RecentsState stateFromOrdinal(int ordinal) {
@@ -174,6 +177,11 @@
return hasFlag(FLAG_DETACH_DESKTOP_CAROUSEL) && enableDesktopWindowingCarouselDetach();
}
+ @Override
+ public boolean showExplodedDesktopView() {
+ return hasFlag(FLAG_SHOW_EXPLODED_DESKTOP_VIEW) && enableDesktopExplodedView();
+ }
+
/**
* True if the state has overview panel visible.
*/
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt b/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt
index 95a3ec2..12dc177 100644
--- a/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt
@@ -17,19 +17,19 @@
package com.android.quickstep.fallback.window
import android.content.Context
-import android.util.Log
import android.view.Display
-import com.android.launcher3.Flags
+import androidx.core.util.valueIterator
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
import com.android.quickstep.fallback.window.RecentsDisplayModel.RecentsDisplayResource
+import com.android.quickstep.fallback.window.RecentsWindowFlags.Companion.enableOverviewInWindow
+import java.io.PrintWriter
import javax.inject.Inject
@LauncherAppSingleton
@@ -50,50 +50,21 @@
DaggerSingletonObject<RecentsDisplayModel>(
QuickstepBaseAppComponent::getRecentsDisplayModel
)
-
- @JvmStatic
- fun enableOverviewInWindow() =
- Flags.enableFallbackOverviewInWindow() || Flags.enableLauncherOverviewInWindow()
}
init {
- if (enableOverviewInWindow()) {
- 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) }
+ if (enableOverviewInWindow) {
+ registerDisplayListener()
tracker.addCloseable { destroy() }
}
}
- override fun createDisplayResource(displayId: Int) {
- 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),
- wallpaperColorHints.hints,
- )
+ override fun createDisplayResource(display: Display): RecentsDisplayResource {
+ return RecentsDisplayResource(
+ display.displayId,
+ context.createDisplayContext(display),
+ wallpaperColorHints.hints,
+ )
}
fun getRecentsWindowManager(displayId: Int): RecentsWindowManager? {
@@ -104,9 +75,15 @@
return getDisplayResource(displayId)?.fallbackWindowInterface
}
+ val activeDisplayResources: Iterable<RecentsDisplayResource>
+ get() =
+ object : Iterable<RecentsDisplayResource> {
+ override fun iterator() = displayResourceArray.valueIterator()
+ }
+
data class RecentsDisplayResource(
- var displayId: Int,
- var displayContext: Context,
+ val displayId: Int,
+ val displayContext: Context,
val wallpaperColorHints: Int,
) : DisplayResource() {
val recentsWindowManager = RecentsWindowManager(displayContext, wallpaperColorHints)
@@ -116,5 +93,15 @@
override fun cleanup() {
recentsWindowManager.destroy()
}
+
+ override fun dump(prefix: String, writer: PrintWriter) {
+ writer.println("${prefix}RecentsDisplayResource:")
+
+ writer.println("${prefix}\tdisplayId=${displayId}")
+ writer.println("${prefix}\tdisplayContext=${displayContext}")
+ writer.println("${prefix}\twallpaperColorHints=${wallpaperColorHints}")
+ writer.println("${prefix}\trecentsWindowManager=${recentsWindowManager}")
+ writer.println("${prefix}\tfallbackWindowInterface=${fallbackWindowInterface}")
+ }
}
}
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowContext.kt b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowContext.kt
index cd48136..d70d7eb 100644
--- a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowContext.kt
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowContext.kt
@@ -18,17 +18,15 @@
import android.content.Context
import android.graphics.PixelFormat
-import android.view.ContextThemeWrapper
+import android.view.Display
import android.view.ViewGroup
import android.view.WindowManager
import android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_CONSUME_IME_INSETS
import com.android.launcher3.DeviceProfile
import com.android.launcher3.InvariantDeviceProfile
+import com.android.launcher3.util.BaseContext
import com.android.launcher3.util.Themes
-import com.android.launcher3.views.ActivityContext
-import com.android.launcher3.views.BaseDragLayer
-import com.android.quickstep.fallback.RecentsDragLayer
/**
* Window context for the Overview overlays.
@@ -36,18 +34,14 @@
* <p>
* Overlays have their own window and need a window context.
*/
-open class RecentsWindowContext(windowContext: Context, wallpaperColorHints: Int) :
- ContextThemeWrapper(
- windowContext,
- Themes.getActivityThemeRes(windowContext, wallpaperColorHints),
- ),
- ActivityContext {
+abstract class RecentsWindowContext(windowContext: Context, wallpaperColorHints: Int) :
+ BaseContext(
+ base = windowContext,
+ themeResId = Themes.getActivityThemeRes(windowContext, wallpaperColorHints),
+ destroyOnDetach = false,
+ ) {
private var deviceProfile: DeviceProfile? = null
- private var dragLayer: RecentsDragLayer<RecentsWindowManager> = RecentsDragLayer(this, null)
- private val deviceProfileChangeListeners:
- MutableList<DeviceProfile.OnDeviceProfileChangeListener> =
- ArrayList()
private val windowTitle: String = "RecentsWindow"
@@ -57,12 +51,11 @@
windowTitle,
)
- override fun getDragLayer(): BaseDragLayer<RecentsWindowManager> {
- return dragLayer
- }
-
fun initDeviceProfile() {
- deviceProfile = InvariantDeviceProfile.INSTANCE[this].getDeviceProfile(this)
+ deviceProfile =
+ if (displayId == Display.DEFAULT_DISPLAY)
+ InvariantDeviceProfile.INSTANCE[this].getDeviceProfile(this)
+ else InvariantDeviceProfile.INSTANCE[this].createDeviceProfileForSecondaryDisplay(this)
}
override fun getDeviceProfile(): DeviceProfile {
@@ -72,11 +65,6 @@
return deviceProfile!!
}
- override fun getOnDeviceProfileChangeListeners():
- List<DeviceProfile.OnDeviceProfileChangeListener> {
- return deviceProfileChangeListeners
- }
-
/**
* Creates LayoutParams for adding a view directly to WindowManager as a new window.
*
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowFlags.kt b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowFlags.kt
new file mode 100644
index 0000000..9953154
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowFlags.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.fallback.window
+
+import android.window.DesktopModeFlags.DesktopModeFlag
+import com.android.launcher3.Flags
+
+class RecentsWindowFlags {
+ companion object {
+ @JvmField
+ val enableLauncherOverviewInWindow: DesktopModeFlag =
+ DesktopModeFlag(Flags::enableLauncherOverviewInWindow, false)
+
+ @JvmField
+ val enableFallbackOverviewInWindow: DesktopModeFlag =
+ DesktopModeFlag(Flags::enableFallbackOverviewInWindow, false)
+
+ @JvmStatic
+ val enableOverviewInWindow
+ get() = enableLauncherOverviewInWindow.isTrue || enableFallbackOverviewInWindow.isTrue
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt
index 1f4961a..1a3a2e3 100644
--- a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt
@@ -50,6 +50,7 @@
import com.android.launcher3.testing.shared.TestProtocol.SEQUENCE_MAIN
import com.android.launcher3.util.ContextTracker
import com.android.launcher3.util.DisplayController
+import com.android.launcher3.util.Executors
import com.android.launcher3.util.RunnableList
import com.android.launcher3.util.SystemUiController
import com.android.launcher3.views.BaseDragLayer
@@ -81,7 +82,6 @@
import com.android.systemui.shared.recents.model.ThumbnailData
import com.android.systemui.shared.system.TaskStackChangeListener
import com.android.systemui.shared.system.TaskStackChangeListeners
-import java.util.function.Predicate
/**
* Class that will manage RecentsView lifecycle within a window and interface correctly where
@@ -135,7 +135,6 @@
// Callback array that corresponds to events defined in @ActivityEvent
private val eventCallbacks =
listOf(RunnableList(), RunnableList(), RunnableList(), RunnableList())
- private var onInitListener: Predicate<Boolean>? = null
private val animationToHomeFactory =
RemoteAnimationFactory {
@@ -215,6 +214,7 @@
override fun destroy() {
super.destroy()
+ Executors.MAIN_EXECUTOR.execute { onViewDestroyed() }
cleanupRecentsWindow()
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(taskStackChangeListener)
callbacks?.removeListener(recentsAnimationListener)
@@ -266,6 +266,7 @@
this.callbacks = callbacks
callbacks?.addListener(recentsAnimationListener)
+ onViewCreated()
}
override fun startHome() {
@@ -336,10 +337,6 @@
return taskbarUIController
}
- fun registerInitListener(onInitListener: Predicate<Boolean>) {
- this.onInitListener = onInitListener
- }
-
override fun collectStateHandlers(out: MutableList<StateManager.StateHandler<RecentsState?>>?) {
out!!.add(FallbackRecentsStateController(this))
}
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowSwipeHandler.java b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowSwipeHandler.java
index 5adc960..1d85feb 100644
--- a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowSwipeHandler.java
@@ -163,7 +163,7 @@
&& endTarget == GestureState.GestureEndTarget.HOME;
if (fromHomeToHome) {
RecentsWindowManager manager =
- mRecentsDisplayModel.getRecentsWindowManager(mDeviceState.getDisplayId());
+ mRecentsDisplayModel.getRecentsWindowManager(mGestureState.getDisplayId());
if (manager != null) {
manager.startHome(/* finishRecentsAnimation= */ false);
}
@@ -228,7 +228,7 @@
recentsCallback = () -> {
callback.run();
RecentsWindowManager manager =
- mRecentsDisplayModel.getRecentsWindowManager(mDeviceState.getDisplayId());
+ mRecentsDisplayModel.getRecentsWindowManager(mGestureState.getDisplayId());
if (manager != null) {
manager.startHome();
}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
index 4e5d037..365c80c 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
@@ -56,9 +56,13 @@
private float mDownY;
private float mTotalY;
- public AccessibilityInputConsumer(Context context, RecentsAnimationDeviceState deviceState,
- InputConsumer delegate, InputMonitorCompat inputMonitor) {
- super(delegate, inputMonitor);
+ public AccessibilityInputConsumer(
+ Context context,
+ int displayId,
+ RecentsAnimationDeviceState deviceState,
+ InputConsumer delegate,
+ InputMonitorCompat inputMonitor) {
+ super(displayId, delegate, inputMonitor);
mContext = context;
mVelocityTracker = VelocityTracker.obtain();
mMinGestureDistance = context.getResources()
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java
index 222ccd3..365014d 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java
@@ -95,7 +95,7 @@
InputMonitorCompat inputMonitor,
RecentsAnimationDeviceState deviceState,
MotionEvent startEvent) {
- super(delegate, inputMonitor);
+ super(gestureState.getDisplayId(), delegate, inputMonitor);
final Resources res = context.getResources();
mContext = context;
mDragDistThreshold = res.getDimension(R.dimen.gestures_assistant_drag_threshold);
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/BubbleBarInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/BubbleBarInputConsumer.java
index b2e7015..86d7190 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/BubbleBarInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/BubbleBarInputConsumer.java
@@ -57,12 +57,19 @@
private final int mTouchSlop;
private final PointF mDownPos = new PointF();
private final PointF mLastPos = new PointF();
+
+ private final int mDisplayId;
+
private long mDownTime;
private final long mTimeForLongPress;
private int mActivePointerId = INVALID_POINTER_ID;
- public BubbleBarInputConsumer(Context context, BubbleControllers bubbleControllers,
+ public BubbleBarInputConsumer(
+ Context context,
+ int displayId,
+ BubbleControllers bubbleControllers,
InputMonitorCompat inputMonitorCompat) {
+ mDisplayId = displayId;
mBubbleStashController = bubbleControllers.bubbleStashController;
mBubbleBarViewController = bubbleControllers.bubbleBarViewController;
mBubbleBarSwipeController = bubbleControllers.bubbleBarSwipeController.orElse(null);
@@ -78,6 +85,11 @@
}
@Override
+ public int getDisplayId() {
+ return mDisplayId;
+ }
+
+ @Override
public void onMotionEvent(MotionEvent ev) {
final int action = ev.getAction();
switch (action) {
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
index 4afd92a..0b1a6c4 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
@@ -17,15 +17,24 @@
protected final InputConsumer mDelegate;
protected final InputMonitorCompat mInputMonitor;
+ private final int mDisplayId;
+
protected int mState;
- public DelegateInputConsumer(InputConsumer delegate, InputMonitorCompat inputMonitor) {
+ public DelegateInputConsumer(
+ int displayId, InputConsumer delegate, InputMonitorCompat inputMonitor) {
+ mDisplayId = displayId;
mDelegate = delegate;
mInputMonitor = inputMonitor;
mState = STATE_INACTIVE;
}
@Override
+ public int getDisplayId() {
+ return mDisplayId;
+ }
+
+ @Override
public InputConsumer getActiveConsumerInHierarchy() {
if (mState == STATE_ACTIVE) {
return this;
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index 503b900..e192702 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -108,8 +108,11 @@
private RecentsAnimationController mRecentsAnimationController;
- public DeviceLockedInputConsumer(Context context, RecentsAnimationDeviceState deviceState,
- TaskAnimationManager taskAnimationManager, GestureState gestureState,
+ public DeviceLockedInputConsumer(
+ Context context,
+ RecentsAnimationDeviceState deviceState,
+ TaskAnimationManager taskAnimationManager,
+ GestureState gestureState,
InputMonitorCompat inputMonitorCompat) {
mContext = context;
mTaskAnimationManager = taskAnimationManager;
@@ -138,6 +141,11 @@
}
@Override
+ public int getDisplayId() {
+ return mGestureState.getDisplayId();
+ }
+
+ @Override
public void onMotionEvent(MotionEvent ev) {
if (mVelocityTracker == null) {
return;
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
index a703c23..baabde8 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
@@ -74,10 +74,14 @@
private MotionEvent mCurrentMotionEvent; // Most recent motion event.
private boolean mDeepPressLogged; // Whether deep press has been logged for the current touch.
- public NavHandleLongPressInputConsumer(Context context, InputConsumer delegate,
- InputMonitorCompat inputMonitor, RecentsAnimationDeviceState deviceState,
- NavHandle navHandle, GestureState gestureState) {
- super(delegate, inputMonitor);
+ public NavHandleLongPressInputConsumer(
+ Context context,
+ InputConsumer delegate,
+ InputMonitorCompat inputMonitor,
+ RecentsAnimationDeviceState deviceState,
+ NavHandle navHandle,
+ GestureState gestureState) {
+ super(gestureState.getDisplayId(), delegate, inputMonitor);
mScreenWidth = DisplayController.INSTANCE.get(context).getInfo().currentSize.x;
mDeepPressEnabled = DeviceConfigWrapper.get().getEnableLpnhDeepPress();
ContextualSearchStateManager contextualSearchStateManager =
@@ -254,13 +258,15 @@
Log.d(TAG, "cancelLongPress: " + reason);
}
// Log LPNH abandon latency if we didn't trigger but were still prepared to.
- long latencyMs = mCurrentMotionEvent.getEventTime() - mCurrentDownEvent.getEventTime();
- if (mState != STATE_ACTIVE && MAIN_EXECUTOR.getHandler().hasCallbacks(mTriggerLongPress)
- && latencyMs >= MIN_TIME_TO_LOG_ABANDON_MS) {
- mStatsLogManager.latencyLogger()
- .withInstanceId(new InstanceIdSequence().newInstanceId())
- .withLatency(latencyMs)
- .log(LAUNCHER_LATENCY_CONTEXTUAL_SEARCH_LPNH_ABANDON);
+ if (mCurrentMotionEvent != null && mCurrentDownEvent != null) {
+ long latencyMs = mCurrentMotionEvent.getEventTime() - mCurrentDownEvent.getEventTime();
+ if (mState != STATE_ACTIVE && MAIN_EXECUTOR.getHandler().hasCallbacks(mTriggerLongPress)
+ && latencyMs >= MIN_TIME_TO_LOG_ABANDON_MS) {
+ mStatsLogManager.latencyLogger()
+ .withInstanceId(new InstanceIdSequence().newInstanceId())
+ .withLatency(latencyMs)
+ .log(LAUNCHER_LATENCY_CONTEXTUAL_SEARCH_LPNH_ABANDON);
+ }
}
mGestureState.setIsInExtendedSlopRegion(false);
MAIN_EXECUTOR.getHandler().removeCallbacks(mTriggerLongPress);
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java
index 83b556d..67cb992 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java
@@ -61,9 +61,13 @@
private boolean mPassedSlop;
private boolean mIsStopGesture;
- public OneHandedModeInputConsumer(Context context, RecentsAnimationDeviceState deviceState,
- InputConsumer delegate, InputMonitorCompat inputMonitor) {
- super(delegate, inputMonitor);
+ public OneHandedModeInputConsumer(
+ Context context,
+ int displayId,
+ RecentsAnimationDeviceState deviceState,
+ InputConsumer delegate,
+ InputMonitorCompat inputMonitor) {
+ super(displayId, delegate, inputMonitor);
mContext = context;
mDeviceState = deviceState;
mDragDistThreshold = context.getResources().getDimensionPixelSize(
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index dd2b2be..2db7573 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -42,7 +42,6 @@
import androidx.annotation.UiThread;
-import com.android.launcher3.Flags;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.testing.TestLogging;
@@ -126,11 +125,17 @@
// The callback called upon finishing the recents transition if it was force-canceled
private Runnable mForceFinishRecentsTransitionCallback;
- public OtherActivityInputConsumer(Context base, RecentsAnimationDeviceState deviceState,
- TaskAnimationManager taskAnimationManager, GestureState gestureState,
- boolean isDeferredDownTarget, Consumer<OtherActivityInputConsumer> onCompleteCallback,
- InputMonitorCompat inputMonitorCompat, InputEventReceiver inputEventReceiver,
- boolean disableHorizontalSwipe, Factory handlerFactory) {
+ public OtherActivityInputConsumer(
+ Context base,
+ RecentsAnimationDeviceState deviceState,
+ TaskAnimationManager taskAnimationManager,
+ GestureState gestureState,
+ boolean isDeferredDownTarget,
+ Consumer<OtherActivityInputConsumer> onCompleteCallback,
+ InputMonitorCompat inputMonitorCompat,
+ InputEventReceiver inputEventReceiver,
+ boolean disableHorizontalSwipe,
+ Factory handlerFactory) {
super(base);
mDeviceState = deviceState;
mNavBarPosition = mDeviceState.getNavBarPosition();
@@ -166,6 +171,11 @@
}
@Override
+ public int getDisplayId() {
+ return mGestureState.getDisplayId();
+ }
+
+ @Override
public boolean isConsumerDetachedFromGesture() {
return true;
}
@@ -426,11 +436,7 @@
mTaskAnimationManager.notifyRecentsAnimationState(mInteractionHandler);
notifyGestureStarted(true /*isLikelyToStartNewTask*/);
} else {
- // todo differentiate intent based on if we are on home or in app for overview in window
- boolean useHomeIntentForWindow = Flags.enableFallbackOverviewInWindow()
- || Flags.enableLauncherOverviewInWindow();
- Intent intent = new Intent(useHomeIntentForWindow ? mInteractionHandler.getHomeIntent()
- : mInteractionHandler.getLaunchIntent());
+ Intent intent = new Intent(mInteractionHandler.getLaunchIntent());
intent.putExtra(INTENT_EXTRA_LOG_TRACE_ID, mGestureState.getGestureId());
mActiveCallbacks = mTaskAnimationManager.startRecentsAnimation(mGestureState, intent,
mInteractionHandler);
@@ -472,7 +478,8 @@
: velocityYPxPerMs;
mInteractionHandler.updateDisplacement(getDisplacement(ev) - mStartDisplacement);
mInteractionHandler.onGestureEnded(velocityPxPerMs,
- new PointF(velocityXPxPerMs, velocityYPxPerMs));
+ new PointF(velocityXPxPerMs, velocityYPxPerMs),
+ Math.abs(mDownPos.x - mLastPos.x) > mTouchSlop);
}
} else {
// Since we start touch tracking on DOWN, we may reach this state without actually
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
index a236eca..4658cb0 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
@@ -55,12 +55,16 @@
private final int[] mLocationOnScreen = new int[2];
private final boolean mStartingInActivityBounds;
+
private boolean mTargetHandledTouch;
private boolean mHasSetTouchModeForFirstDPadEvent;
private boolean mIsWaitingForAttachToWindow;
- public OverviewInputConsumer(GestureState gestureState, T container,
- @Nullable InputMonitorCompat inputMonitor, boolean startingInActivityBounds) {
+ public OverviewInputConsumer(
+ GestureState gestureState,
+ T container,
+ @Nullable InputMonitorCompat inputMonitor,
+ boolean startingInActivityBounds) {
mContainer = container;
mInputMonitor = inputMonitor;
mStartingInActivityBounds = startingInActivityBounds;
@@ -77,6 +81,11 @@
}
@Override
+ public int getDisplayId() {
+ return mGestureState.getDisplayId();
+ }
+
+ @Override
public boolean allowInterceptByParent() {
return !mTargetHandledTouch;
}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
index be47df9..7838e86 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
@@ -43,9 +43,12 @@
private final TriggerSwipeUpTouchTracker mTriggerSwipeUpTracker;
private final GestureState mGestureState;
- public OverviewWithoutFocusInputConsumer(Context context,
- RecentsAnimationDeviceState deviceState, GestureState gestureState,
- InputMonitorCompat inputMonitor, boolean disableHorizontalSwipe) {
+ public OverviewWithoutFocusInputConsumer(
+ Context context,
+ RecentsAnimationDeviceState deviceState,
+ GestureState gestureState,
+ InputMonitorCompat inputMonitor,
+ boolean disableHorizontalSwipe) {
mContext = context;
mGestureState = gestureState;
mInputMonitor = inputMonitor;
@@ -59,6 +62,11 @@
}
@Override
+ public int getDisplayId() {
+ return mGestureState.getDisplayId();
+ }
+
+ @Override
public boolean allowInterceptByParent() {
return !mTriggerSwipeUpTracker.interceptedTouch();
}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java
index c91bebe..52aaa03 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java
@@ -89,9 +89,12 @@
private RecentsAnimationController mRecentsAnimationController;
private Boolean mFlingEndsOnHome;
- public ProgressDelegateInputConsumer(Context context,
- TaskAnimationManager taskAnimationManager, GestureState gestureState,
- InputMonitorCompat inputMonitorCompat, AnimatedFloat progress) {
+ public ProgressDelegateInputConsumer(
+ Context context,
+ TaskAnimationManager taskAnimationManager,
+ GestureState gestureState,
+ InputMonitorCompat inputMonitorCompat,
+ AnimatedFloat progress) {
mContext = context;
mTaskAnimationManager = taskAnimationManager;
mGestureState = gestureState;
@@ -118,6 +121,11 @@
}
@Override
+ public int getDisplayId() {
+ return mGestureState.getDisplayId();
+ }
+
+ @Override
public void onMotionEvent(MotionEvent ev) {
if (mFlingEndsOnHome == null) {
mSwipeDetector.onTouchEvent(ev);
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java
deleted file mode 100644
index 349f4d2..0000000
--- a/quickstep/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep.inputconsumers;
-
-import android.view.MotionEvent;
-
-import com.android.launcher3.taskbar.TaskbarActivityContext;
-import com.android.quickstep.InputConsumer;
-import com.android.quickstep.TaskAnimationManager;
-
-import java.util.function.Supplier;
-
-/**
- * A NO_OP input consumer which also resets any pending gesture
- */
-public class ResetGestureInputConsumer implements InputConsumer {
-
- private final TaskAnimationManager mTaskAnimationManager;
- private final Supplier<TaskbarActivityContext> mActivityContextSupplier;
-
- public ResetGestureInputConsumer(
- TaskAnimationManager taskAnimationManager,
- Supplier<TaskbarActivityContext> activityContextSupplier) {
- mTaskAnimationManager = taskAnimationManager;
- mActivityContextSupplier = activityContextSupplier;
- }
-
- @Override
- public int getType() {
- return TYPE_RESET_GESTURE;
- }
-
- @Override
- public void onMotionEvent(MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN
- && mTaskAnimationManager.isRecentsAnimationRunning()) {
- TaskbarActivityContext tac = mActivityContextSupplier.get();
- mTaskAnimationManager.finishRunningRecentsAnimation(
- /* toHome= */ tac != null && !tac.isInApp());
- }
- }
-}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.kt b/quickstep/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.kt
new file mode 100644
index 0000000..96e7943
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.inputconsumers
+
+import android.view.MotionEvent
+import com.android.launcher3.taskbar.TaskbarActivityContext
+import com.android.quickstep.InputConsumer
+import com.android.quickstep.TaskAnimationManager
+import java.util.function.Supplier
+
+/** A NO_OP input consumer which also resets any pending gesture */
+class ResetGestureInputConsumer(
+ private val displayId: Int,
+ private val taskAnimationManager: TaskAnimationManager,
+ private val activityContextSupplier: Supplier<TaskbarActivityContext?>,
+) : InputConsumer {
+ override fun getType() = InputConsumer.TYPE_RESET_GESTURE
+
+ override fun getDisplayId() = displayId
+
+ override fun onMotionEvent(ev: MotionEvent) {
+ if (
+ ev.action == MotionEvent.ACTION_DOWN && taskAnimationManager.isRecentsAnimationRunning
+ ) {
+ val tac = activityContextSupplier.get()
+ taskAnimationManager.finishRunningRecentsAnimation(
+ /* toHome= */ tac != null && !tac.isInApp
+ )
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java
index d73c23f..9dc27de 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java
@@ -36,9 +36,12 @@
private final float mMotionPauseMinDisplacement;
private final MotionPauseDetector mMotionPauseDetector;
+ private final int mDisplayId;
+
private float mTouchDownY;
public ScreenPinnedInputConsumer(Context context, GestureState gestureState) {
+ mDisplayId = gestureState.getDisplayId();
mMotionPauseMinDisplacement = context.getResources().getDimension(
R.dimen.motion_pause_detector_min_displacement_from_app);
mMotionPauseDetector = new MotionPauseDetector(context, true /* makePauseHarderToTrigger*/);
@@ -61,6 +64,11 @@
}
@Override
+ public int getDisplayId() {
+ return mDisplayId;
+ }
+
+ @Override
public void onMotionEvent(MotionEvent ev) {
float y = ev.getY();
switch (ev.getAction()) {
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/SysUiOverlayInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/SysUiOverlayInputConsumer.java
index 871d075..ad1a01b 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/SysUiOverlayInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/SysUiOverlayInputConsumer.java
@@ -47,11 +47,15 @@
private final InputMonitorCompat mInputMonitor;
private final TriggerSwipeUpTouchTracker mTriggerSwipeUpTracker;
+ private final int mDisplayId;
+
public SysUiOverlayInputConsumer(
Context context,
+ int displayId,
RecentsAnimationDeviceState deviceState,
InputMonitorCompat inputMonitor) {
mContext = context;
+ mDisplayId = displayId;
mInputMonitor = inputMonitor;
mTriggerSwipeUpTracker = new TriggerSwipeUpTouchTracker(context, true,
deviceState.getNavBarPosition(), this);
@@ -63,6 +67,11 @@
}
@Override
+ public int getDisplayId() {
+ return mDisplayId;
+ }
+
+ @Override
public boolean allowInterceptByParent() {
return !mTriggerSwipeUpTracker.interceptedTouch();
}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
index 49bff8d..dbe6e14 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
@@ -89,10 +89,14 @@
// Velocity defined as dp per s
private float mTaskbarSlowVelocityYThreshold;
- public TaskbarUnstashInputConsumer(Context context, InputConsumer delegate,
- InputMonitorCompat inputMonitor, TaskbarActivityContext taskbarActivityContext,
- OverviewCommandHelper overviewCommandHelper, GestureState gestureState) {
- super(delegate, inputMonitor);
+ public TaskbarUnstashInputConsumer(
+ Context context,
+ InputConsumer delegate,
+ InputMonitorCompat inputMonitor,
+ TaskbarActivityContext taskbarActivityContext,
+ OverviewCommandHelper overviewCommandHelper,
+ GestureState gestureState) {
+ super(gestureState.getDisplayId(), delegate, inputMonitor);
mTaskbarActivityContext = taskbarActivityContext;
mOverviewCommandHelper = overviewCommandHelper;
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/TrackpadStatusBarInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/TrackpadStatusBarInputConsumer.java
index f3e21e1..a53a395 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/TrackpadStatusBarInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/TrackpadStatusBarInputConsumer.java
@@ -35,9 +35,12 @@
private final PointF mDown = new PointF();
private boolean mHasPassedTouchSlop;
- public TrackpadStatusBarInputConsumer(Context context, InputConsumer delegate,
+ public TrackpadStatusBarInputConsumer(
+ Context context,
+ int displayId,
+ InputConsumer delegate,
InputMonitorCompat inputMonitor) {
- super(delegate, inputMonitor);
+ super(displayId, delegate, inputMonitor);
mSystemUiProxy = SystemUiProxy.INSTANCE.get(context);
mTouchSlop = 2 * ViewConfiguration.get(context).getScaledTouchSlop();
diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index e265e61..c63cddf 100644
--- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
@@ -34,6 +34,7 @@
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.view.Display;
import android.view.View;
import android.view.ViewOutlineProvider;
@@ -84,8 +85,8 @@
SwipeUpGestureTutorialController(TutorialFragment tutorialFragment, TutorialType tutorialType) {
super(tutorialFragment, tutorialType);
- mTaskViewSwipeUpAnimation = new ViewSwipeUpAnimation(mContext,
- new GestureState(OverviewComponentObserver.INSTANCE.get(mContext), -1));
+ mTaskViewSwipeUpAnimation = new ViewSwipeUpAnimation(mContext, new GestureState(
+ OverviewComponentObserver.INSTANCE.get(mContext), Display.DEFAULT_DISPLAY, -1));
DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext)
.getDeviceProfile(mContext)
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
index 22227c9..5995ca2 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
@@ -56,11 +56,12 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorListeners;
-import com.android.launcher3.taskbar.TypefaceUtils;
import com.android.launcher3.views.ClipIconView;
import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureAttemptCallback;
import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureAttemptCallback;
import com.android.systemui.shared.system.QuickStepContract;
+import com.android.wm.shell.shared.TypefaceUtils;
+import com.android.wm.shell.shared.TypefaceUtils.FontFamily;
import com.airbnb.lottie.LottieAnimationView;
import com.airbnb.lottie.LottieComposition;
@@ -109,7 +110,6 @@
final AnimatedTaskView mFakePreviousTaskView;
final View mRippleView;
final RippleDrawable mRippleDrawable;
- final TutorialStepIndicator mTutorialStepView;
final ImageView mFingerDotView;
private final Rect mExitingAppRect = new Rect();
protected View mExitingAppView;
@@ -152,8 +152,6 @@
mRippleView = rootView.findViewById(R.id.gesture_tutorial_ripple_view);
mRippleDrawable = (RippleDrawable) mRippleView.getBackground();
mDoneButton = rootView.findViewById(R.id.gesture_tutorial_fragment_action_button);
- mTutorialStepView =
- rootView.findViewById(R.id.gesture_tutorial_fragment_feedback_tutorial_step);
mFingerDotView = rootView.findViewById(R.id.gesture_tutorial_finger_dot);
mSkipTutorialDialog = createSkipTutorialDialog();
@@ -412,7 +410,7 @@
if (mTutorialFragment.isAtFinalStep()) {
TypefaceUtils.setTypeface(
mDoneButton,
- TypefaceUtils.FONT_FAMILY_LABEL_LARGE_BASELINE
+ FontFamily.GSF_LABEL_LARGE
);
showActionButton();
}
@@ -488,7 +486,6 @@
@CallSuper
void transitToController() {
updateCloseButton();
- updateSubtext();
updateDrawables();
updateLayout();
@@ -521,11 +518,11 @@
TypefaceUtils.setTypeface(
mFeedbackTitleView,
mTutorialFragment.isLargeScreen()
- ? TypefaceUtils.FONT_FAMILY_DISPLAY_MEDIUM_EMPHASIZED
- : TypefaceUtils.FONT_FAMILY_DISPLAY_SMALL_EMPHASIZED);
+ ? FontFamily.GSF_DISPLAY_MEDIUM_EMPHASIZED
+ : FontFamily.GSF_DISPLAY_SMALL_EMPHASIZED);
TypefaceUtils.setTypeface(
mFeedbackSubtitleView,
- TypefaceUtils.FONT_FAMILY_BODY_LARGE_BASELINE
+ FontFamily.GSF_BODY_LARGE
);
}
@@ -605,11 +602,6 @@
}
}
- private void updateSubtext() {
- mTutorialStepView.setTutorialProgress(
- mTutorialFragment.getCurrentStep(), mTutorialFragment.getNumSteps());
- }
-
private void updateHotseatChildViewColor(@Nullable View child) {
if (child == null) return;
child.getBackground().setTint(getHotseatIconColor());
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialStepIndicator.java b/quickstep/src/com/android/quickstep/interaction/TutorialStepIndicator.java
deleted file mode 100644
index f1fc179..0000000
--- a/quickstep/src/com/android/quickstep/interaction/TutorialStepIndicator.java
+++ /dev/null
@@ -1,115 +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.interaction;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.icons.GraphicsUtils;
-
-/** Indicator displaying the current progress through the gesture navigation tutorial. */
-public class TutorialStepIndicator extends LinearLayout {
-
- private static final String LOG_TAG = "TutorialStepIndicator";
-
- private int mCurrentStep = -1;
- private int mTotalSteps = -1;
-
- public TutorialStepIndicator(Context context) {
- super(context);
- }
-
- public TutorialStepIndicator(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public TutorialStepIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
- public TutorialStepIndicator(Context context, AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- }
-
- /**
- * Updates this indicator to display totalSteps indicator pills, with the first currentStep
- * pills highlighted.
- */
- public void setTutorialProgress(int currentStep, int totalSteps) {
- if (currentStep <= 0) {
- Log.w(LOG_TAG, "Current step number invalid: " + currentStep + ". Assuming step 1.");
- currentStep = 1;
- }
- if (totalSteps <= 0) {
- Log.w(LOG_TAG, "Total number of steps invalid: " + totalSteps + ". Assuming 1 step.");
- totalSteps = 1;
- }
- if (currentStep > totalSteps) {
- Log.w(LOG_TAG, "Current step number greater than the total number of steps. Assuming"
- + " final step.");
- currentStep = totalSteps;
- }
- if (totalSteps < 2) {
- setVisibility(GONE);
- return;
- }
- setVisibility(VISIBLE);
- mCurrentStep = currentStep;
- mTotalSteps = totalSteps;
-
- initializeStepIndicators();
- }
-
- private void initializeStepIndicators() {
- for (int i = mTotalSteps; i < getChildCount(); i++) {
- removeViewAt(i);
- }
- int activeStepIndicatorColor = GraphicsUtils.getAttrColor(
- getContext(), android.R.attr.textColorPrimary);
- int inactiveStepIndicatorColor = GraphicsUtils.getAttrColor(
- getContext(), android.R.attr.textColorSecondaryInverse);
- for (int i = 0; i < mTotalSteps; i++) {
- Drawable pageIndicatorPillDrawable =
- getContext().getDrawable(R.drawable.tutorial_step_indicator_pill);
- if (i >= getChildCount()) {
- ImageView pageIndicatorPill = new ImageView(getContext());
- pageIndicatorPill.setImageDrawable(pageIndicatorPillDrawable);
-
- LinearLayout.LayoutParams lp = new LayoutParams(
- LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
-
- lp.setMarginStart(Utilities.dpToPx(3));
- lp.setMarginEnd(Utilities.dpToPx(3));
-
- addView(pageIndicatorPill, lp);
- }
- if (pageIndicatorPillDrawable != null) {
- if (i < mCurrentStep) {
- pageIndicatorPillDrawable.setTint(activeStepIndicatorColor);
- } else {
- pageIndicatorPillDrawable.setTint(inactiveStepIndicatorColor);
- }
- }
- }
- }
-}
diff --git a/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt b/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt
index e3c9b2b..c4e343e 100644
--- a/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt
+++ b/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt
@@ -275,7 +275,7 @@
desiredTaskId: Int,
banner: View,
): Pair<Float, Float> {
- val snapshotParams = thumbnailViews[0].layoutParams as FrameLayout.LayoutParams
+ val snapshotParams = thumbnailViews[0].layoutParams as LinearLayout.LayoutParams
val translationX = banner.height.toFloat()
val translationY: Float
if (splitBounds == null) {
diff --git a/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.kt b/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.kt
index 1883649..15eb69e 100644
--- a/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.kt
+++ b/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.kt
@@ -255,7 +255,7 @@
}
} else {
if (desiredTaskId == splitBounds.leftTopTaskId) {
- val snapshotParams = thumbnailViews[0].layoutParams as FrameLayout.LayoutParams
+ val snapshotParams = thumbnailViews[0].layoutParams as LinearLayout.LayoutParams
val bottomRightTaskPlusDividerPercent =
(splitBounds.rightBottomTaskPercent + splitBounds.dividerPercent)
translationY =
@@ -726,7 +726,11 @@
val secondarySnapshotWidth = groupedTaskViewWidth - primarySnapshotWidth
primaryAppChipView.setSplitTranslationX(-secondarySnapshotWidth.toFloat())
} else {
- secondaryAppChipView.setSplitTranslationX(primarySnapshotWidth.toFloat())
+ val dividerSize =
+ Math.round(groupedTaskViewWidth * splitConfig.dividerPercent)
+ secondaryAppChipView.setSplitTranslationX(
+ primarySnapshotWidth.toFloat() + dividerSize
+ )
}
} else {
primaryAppChipView.setSplitTranslationX(0f)
diff --git a/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt b/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt
index 1f9f752..67358bbb 100644
--- a/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt
+++ b/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt
@@ -26,6 +26,7 @@
import android.view.View
import android.view.View.MeasureSpec
import android.widget.FrameLayout
+import android.widget.LinearLayout
import androidx.core.util.component1
import androidx.core.util.component2
import androidx.core.view.updateLayoutParams
@@ -151,7 +152,7 @@
desiredTaskId: Int,
banner: View,
): Pair<Float, Float> {
- val snapshotParams = thumbnailViews[0].layoutParams as FrameLayout.LayoutParams
+ val snapshotParams = thumbnailViews[0].layoutParams as LinearLayout.LayoutParams
val translationX: Float = (taskViewWidth - banner.height).toFloat()
val translationY: Float
if (splitBounds == null) {
diff --git a/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt b/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
index c6b3d6a..553a620 100644
--- a/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
+++ b/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
@@ -18,7 +18,6 @@
import android.content.Context
import android.util.Log
-import android.view.View
import com.android.launcher3.util.coroutines.DispatcherProvider
import com.android.launcher3.util.coroutines.ProductionDispatchers
import com.android.quickstep.RecentsModel
@@ -32,8 +31,6 @@
import com.android.quickstep.recents.domain.usecase.IsThumbnailValidUseCase
import com.android.quickstep.recents.domain.usecase.OrganizeDesktopTasksUseCase
import com.android.quickstep.recents.viewmodel.RecentsViewData
-import com.android.quickstep.task.viewmodel.TaskOverlayViewModel
-import com.android.systemui.shared.recents.model.Task
import com.android.systemui.shared.recents.utilities.PreviewPositionHelper
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
@@ -49,10 +46,12 @@
}
/**
- * This function initialised the default scope with RecentsView dependencies. These dependencies
- * are used multiple times and should be a singleton to share across Recents classes.
+ * This function initialises the default scope with RecentsView dependencies. Some dependencies
+ * are global while others are per-RecentsView. The scope is used to differentiate between
+ * RecentsViews.
*/
private fun startDefaultScope(appContext: Context) {
+ Log.d(TAG, "startDefaultScope")
createScope(DEFAULT_SCOPE_ID).apply {
set(RecentsViewData::class.java.simpleName, RecentsViewData())
val dispatcherProvider: DispatcherProvider = ProductionDispatchers
@@ -86,6 +85,32 @@
}
}
+ /**
+ * This function initialises a scope associated with the dependencies of a single RecentsView.
+ *
+ * @param viewContext the Context associated with a RecentsView.
+ * @return the scope id associated with the new RecentsDependenciesScope.
+ */
+ fun createRecentsViewScope(viewContext: Context): String {
+ val scopeId = viewContext.hashCode().toString()
+ Log.d(TAG, "createRecentsViewScope $scopeId")
+ val scope =
+ createScope(scopeId).apply {
+ set(RecentsViewData::class.java.simpleName, RecentsViewData())
+ val dispatcherProvider: DispatcherProvider =
+ get<DispatcherProvider>(DEFAULT_SCOPE_ID)
+ val recentsCoroutineScope =
+ CoroutineScope(
+ SupervisorJob() +
+ dispatcherProvider.unconfined +
+ CoroutineName("RecentsView$scopeId")
+ )
+ set(CoroutineScope::class.java.simpleName, recentsCoroutineScope)
+ }
+ scope.linkTo(getScope(DEFAULT_SCOPE_ID))
+ return scopeId
+ }
+
inline fun <reified T> inject(
scopeId: RecentsScopeId = "",
extras: RecentsDependenciesExtras = RecentsDependenciesExtras(),
@@ -170,25 +195,14 @@
log("linked scopes: ${getScope(scopeId).scopeIdsLinked}")
val instance: Any =
when (modelClass) {
- RecentsViewData::class.java -> RecentsViewData()
- TaskOverlayViewModel::class.java -> {
- val task = extras["Task"] as Task
- TaskOverlayViewModel(
- task = task,
- recentsViewData = inject(),
- recentTasksRepository = inject(),
- getThumbnailPositionUseCase = inject(),
- dispatcherProvider = inject(),
- )
- }
IsThumbnailValidUseCase::class.java ->
- IsThumbnailValidUseCase(rotationStateRepository = inject())
- GetTaskUseCase::class.java -> GetTaskUseCase(repository = inject())
+ IsThumbnailValidUseCase(rotationStateRepository = inject(scopeId))
+ GetTaskUseCase::class.java -> GetTaskUseCase(repository = inject(scopeId))
GetSysUiStatusNavFlagsUseCase::class.java -> GetSysUiStatusNavFlagsUseCase()
GetThumbnailPositionUseCase::class.java ->
GetThumbnailPositionUseCase(
- deviceProfileRepository = inject(),
- rotationStateRepository = inject(),
+ deviceProfileRepository = inject(scopeId),
+ rotationStateRepository = inject(scopeId),
previewPositionHelper = PreviewPositionHelper(),
)
OrganizeDesktopTasksUseCase::class.java -> OrganizeDesktopTasksUseCase()
@@ -222,58 +236,58 @@
}
companion object {
- private const val DEFAULT_SCOPE_ID = "RecentsDependencies::GlobalScope"
+ const val DEFAULT_SCOPE_ID = "RecentsDependencies::GlobalScope"
private const val TAG = "RecentsDependencies"
private const val DEBUG = false
- private var activeRecentsCount = 0
- @Volatile private lateinit var instance: RecentsDependencies
+ @Volatile private var instance: RecentsDependencies? = null
- fun initialize(view: View): RecentsDependencies = initialize(view.context)
-
- fun initialize(context: Context): RecentsDependencies {
+ private fun initialize(context: Context): RecentsDependencies {
Log.d(TAG, "initializing")
synchronized(this) {
- activeRecentsCount++
- instance = RecentsDependencies(context.applicationContext)
+ val newInstance = RecentsDependencies(context.applicationContext)
+ instance = newInstance
+ return newInstance
}
- return instance
+ }
+
+ fun maybeInitialize(context: Context): RecentsDependencies {
+ return instance ?: initialize(context)
}
fun getInstance(): RecentsDependencies {
- if (!Companion::instance.isInitialized) {
- throw UninitializedPropertyAccessException(
- "Recents dependencies are not initialized. " +
- "Call `RecentsDependencies.initialize` before using this container."
- )
- }
return instance
+ ?: throw UninitializedPropertyAccessException(
+ "Recents dependencies are not initialized. " +
+ "Call `RecentsDependencies.maybeInitialize` before using this container."
+ )
}
@JvmStatic
- fun destroy() {
- // When Launcher Activity restarts, the old view's RecentsView.onDetachedFromWindow
- // happens after the new view's creation. This means that destroy can be called after a
- // new initialisation. This check prevents a newly initialised tree from being
- // destroyed. Ideally we would have 1 instance of the dependency tree for each
- // RecentsView.
- //
- // This check is sufficient to avoid a leak of the dependency tree after the Activity is
- // destroyed while also allowing Launcher auto-restarts (production behaviour) to easily
- // reinitialise the dependency tree.
- //
- // TODO(b/353917593): Better lifecycle decisions will be implemented in this bug or when
- // replacing with Dagger (b/371370483).
- activeRecentsCount--
- if (activeRecentsCount == 0) {
- instance.scopes.clear()
- Log.d(TAG, "destroyed", Exception("Printing stack trace"))
- } else {
- Log.d(
- TAG,
- "RecentsDependencies was not destroyed. " +
- "There is still an active RecentsView instance.",
- )
+ fun destroy(viewContext: Context) {
+ synchronized(this) {
+ val localInstance = instance ?: return
+ val scopeId = viewContext.hashCode().toString()
+ val scope = localInstance.scopes[scopeId]
+ if (scope == null) {
+ Log.e(
+ TAG,
+ "Trying to destroy an unknown scope. Scopes: ${localInstance.scopes.size}",
+ )
+ return
+ }
+ scope.close()
+ localInstance.scopes.remove(scopeId)
+ if (DEBUG) {
+ Log.d(TAG, "destroyed $scopeId", Exception("Printing stack trace"))
+ } else {
+ Log.d(TAG, "destroyed $scopeId")
+ }
+ if (localInstance.scopes.size == 1) {
+ // Only the default scope left - destroy this too.
+ instance = null
+ Log.d(TAG, "also destroyed default scope")
+ }
}
}
}
diff --git a/quickstep/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapper.kt b/quickstep/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapper.kt
index 619075f..679daf8 100644
--- a/quickstep/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapper.kt
+++ b/quickstep/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapper.kt
@@ -17,18 +17,51 @@
package com.android.quickstep.recents.ui.mapper
import android.view.View.OnClickListener
+import com.android.launcher3.Flags.enableDesktopExplodedView
import com.android.quickstep.recents.ui.viewmodel.TaskData
+import com.android.quickstep.task.thumbnail.TaskHeaderUiState
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.BackgroundOnly
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.LiveTile
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Snapshot
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.SnapshotSplash
-import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.ThumbnailHeader
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Uninitialized
object TaskUiStateMapper {
/**
+ * Converts a [TaskData] object into a [TaskHeaderUiState] for display in the UI.
+ *
+ * This function handles different types of [TaskData] and determines the appropriate UI state
+ * based on the data and provided flags.
+ *
+ * @param taskData The [TaskData] to convert. Can be null or a specific subclass.
+ * @param hasHeader A flag indicating whether the UI should display a header.
+ * @param clickCloseListener A callback when the close button in the UI is clicked.
+ * @return A [TaskHeaderUiState] representing the UI state for the given task data.
+ */
+ fun toTaskHeaderState(
+ taskData: TaskData?,
+ hasHeader: Boolean,
+ clickCloseListener: OnClickListener?,
+ ): TaskHeaderUiState =
+ when {
+ taskData !is TaskData.Data -> TaskHeaderUiState.HideHeader
+ canHeaderBeCreated(taskData, hasHeader, clickCloseListener) -> {
+ TaskHeaderUiState.ShowHeader(
+ TaskHeaderUiState.ThumbnailHeader(
+ // TODO(http://b/353965691): figure out what to do when `icon` or
+ // `titleDescription` is null.
+ taskData.icon!!,
+ taskData.titleDescription!!,
+ clickCloseListener!!,
+ )
+ )
+ }
+ else -> TaskHeaderUiState.HideHeader
+ }
+
+ /**
* Converts a [TaskData] object into a [TaskThumbnailUiState] for display in the UI.
*
* This function handles different types of [TaskData] and determines the appropriate UI state
@@ -36,46 +69,24 @@
*
* @param taskData The [TaskData] to convert. Can be null or a specific subclass.
* @param isLiveTile A flag indicating whether the task data represents live tile.
- * @param hasHeader A flag indicating whether the UI should display a header.
- * @param clickCloseListener A callback when the close button in the UI is clicked.
* @return A [TaskThumbnailUiState] representing the UI state for the given task data.
*/
- fun toTaskThumbnailUiState(
- taskData: TaskData?,
- isLiveTile: Boolean,
- hasHeader: Boolean,
- clickCloseListener: OnClickListener?,
- ): TaskThumbnailUiState =
+ fun toTaskThumbnailUiState(taskData: TaskData?, isLiveTile: Boolean): TaskThumbnailUiState =
when {
taskData !is TaskData.Data -> Uninitialized
- isLiveTile -> createLiveTileState(taskData, hasHeader, clickCloseListener)
+ isLiveTile -> LiveTile
isBackgroundOnly(taskData) -> BackgroundOnly(taskData.backgroundColor)
isSnapshotSplash(taskData) ->
SnapshotSplash(
- createSnapshotState(taskData, hasHeader, clickCloseListener),
+ Snapshot(
+ taskData.thumbnailData?.thumbnail!!,
+ taskData.thumbnailData.rotation,
+ taskData.backgroundColor,
+ ),
taskData.icon,
)
- else -> Uninitialized
- }
- private fun createSnapshotState(
- taskData: TaskData.Data,
- hasHeader: Boolean,
- clickCloseListener: OnClickListener?,
- ): Snapshot =
- if (canHeaderBeCreated(taskData, hasHeader, clickCloseListener)) {
- Snapshot.WithHeader(
- taskData.thumbnailData?.thumbnail!!,
- taskData.thumbnailData.rotation,
- taskData.backgroundColor,
- ThumbnailHeader(taskData.icon!!, taskData.titleDescription!!, clickCloseListener!!),
- )
- } else {
- Snapshot.WithoutHeader(
- taskData.thumbnailData?.thumbnail!!,
- taskData.thumbnailData.rotation,
- taskData.backgroundColor,
- )
+ else -> Uninitialized
}
private fun isBackgroundOnly(taskData: TaskData.Data) =
@@ -89,21 +100,9 @@
hasHeader: Boolean,
clickCloseListener: OnClickListener?,
) =
- hasHeader &&
+ enableDesktopExplodedView() &&
+ hasHeader &&
taskData.icon != null &&
taskData.titleDescription != null &&
clickCloseListener != null
-
- private fun createLiveTileState(
- taskData: TaskData.Data,
- hasHeader: Boolean,
- clickCloseListener: OnClickListener?,
- ) =
- if (canHeaderBeCreated(taskData, hasHeader, clickCloseListener)) {
- // TODO(http://b/353965691): figure out what to do when `icon` or `titleDescription` is
- // null.
- LiveTile.WithHeader(
- ThumbnailHeader(taskData.icon!!, taskData.titleDescription!!, clickCloseListener!!)
- )
- } else LiveTile.WithoutHeader
}
diff --git a/quickstep/src/com/android/quickstep/recents/ui/viewmodel/TaskTileUiState.kt b/quickstep/src/com/android/quickstep/recents/ui/viewmodel/TaskTileUiState.kt
index 118a931..8a6a805 100644
--- a/quickstep/src/com/android/quickstep/recents/ui/viewmodel/TaskTileUiState.kt
+++ b/quickstep/src/com/android/quickstep/recents/ui/viewmodel/TaskTileUiState.kt
@@ -36,6 +36,7 @@
val isLiveTile: Boolean,
val hasHeader: Boolean,
val sysUiStatusNavFlags: Int,
+ val taskOverlayEnabled: Boolean,
)
sealed class 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
index 3c4a384..09e2071 100644
--- a/quickstep/src/com/android/quickstep/recents/ui/viewmodel/TaskViewModel.kt
+++ b/quickstep/src/com/android/quickstep/recents/ui/viewmodel/TaskViewModel.kt
@@ -29,6 +29,7 @@
import com.android.quickstep.recents.domain.usecase.ThumbnailPosition
import com.android.quickstep.recents.viewmodel.RecentsViewData
import com.android.quickstep.views.TaskViewType
+import com.android.quickstep.views.TaskViewType.SINGLE
import com.android.systemui.shared.recents.model.ThumbnailData
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
@@ -74,8 +75,18 @@
)
}
+ private val overlayEnabled =
+ combine(recentsViewData.overlayEnabled, recentsViewData.settledFullyVisibleTaskIds) {
+ isOverlayEnabled,
+ settledFullyVisibleTaskIds ->
+ taskViewType == SINGLE &&
+ isOverlayEnabled &&
+ settledFullyVisibleTaskIds.any { it in taskIds.value }
+ }
+ .distinctUntilChanged()
+
val state: Flow<TaskTileUiState> =
- combine(taskData, isLiveTile) { tasks, isLiveTile -> mapToTaskTile(tasks, isLiveTile) }
+ combine(taskData, isLiveTile, overlayEnabled, ::mapToTaskTile)
.distinctUntilChanged()
.flowOn(dispatcherProvider.background)
@@ -99,13 +110,18 @@
isRtl = isRtl,
)
- private fun mapToTaskTile(tasks: List<TaskData>, isLiveTile: Boolean): TaskTileUiState {
+ private fun mapToTaskTile(
+ tasks: List<TaskData>,
+ isLiveTile: Boolean,
+ overlayEnabled: Boolean,
+ ): TaskTileUiState {
val firstThumbnailData = (tasks.firstOrNull() as? TaskData.Data)?.thumbnailData
return TaskTileUiState(
tasks = tasks,
isLiveTile = isLiveTile,
hasHeader = taskViewType == TaskViewType.DESKTOP,
sysUiStatusNavFlags = getSysUiStatusNavFlagsUseCase(firstThumbnailData),
+ taskOverlayEnabled = overlayEnabled,
)
}
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskContentView.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskContentView.kt
new file mode 100644
index 0000000..a40929c
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskContentView.kt
@@ -0,0 +1,158 @@
+/*
+ * 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.task.thumbnail
+
+import android.content.Context
+import android.graphics.Outline
+import android.graphics.Path
+import android.graphics.Rect
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewOutlineProvider
+import android.widget.LinearLayout
+import androidx.core.view.isInvisible
+import com.android.launcher3.Flags.enableRefactorTaskThumbnail
+import com.android.launcher3.R
+import com.android.launcher3.util.ViewPool
+import com.android.quickstep.views.TaskHeaderView
+import com.android.quickstep.views.TaskThumbnailViewDeprecated
+
+/**
+ * TaskContentView is a wrapper around the TaskHeaderView and TaskThumbnailView. It is a sibling to
+ * DWB, AiAi (TaskOverlay).
+ */
+class TaskContentView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
+ LinearLayout(context, attrs), ViewPool.Reusable {
+
+ private var taskHeaderView: TaskHeaderView? = null
+ private var taskThumbnailView: TaskThumbnailView? = null
+ private var taskThumbnailViewDeprecated: TaskThumbnailViewDeprecated? = null
+ private var onSizeChanged: ((width: Int, height: Int) -> Unit)? = null
+ private val outlinePath = Path()
+
+ /**
+ * Sets the outline bounds of the view. Default to use view's bound as outline when set to null.
+ */
+ var outlineBounds: Rect? = null
+ set(value) {
+ field = value
+ invalidateOutline()
+ }
+
+ private val bounds = Rect()
+
+ var cornerRadius: Float = 0f
+ set(value) {
+ field = value
+ invalidateOutline()
+ }
+
+ override fun onFinishInflate() {
+ super.onFinishInflate()
+ createTaskThumbnailView()
+ }
+
+ override fun setScaleX(scaleX: Float) {
+ super.setScaleX(scaleX)
+ taskThumbnailView?.parentScaleXUpdated(scaleX)
+ }
+
+ override fun setScaleY(scaleY: Float) {
+ super.setScaleY(scaleY)
+ taskThumbnailView?.parentScaleYUpdated(scaleY)
+ }
+
+ override fun onAttachedToWindow() {
+ super.onAttachedToWindow()
+ clipToOutline = true
+ outlineProvider =
+ object : ViewOutlineProvider() {
+ override fun getOutline(view: View, outline: Outline) {
+ val outlineRect = outlineBounds ?: bounds
+ outlinePath.apply {
+ rewind()
+ addRoundRect(
+ outlineRect.left.toFloat(),
+ outlineRect.top.toFloat(),
+ outlineRect.right.toFloat(),
+ outlineRect.bottom.toFloat(),
+ cornerRadius / scaleX,
+ cornerRadius / scaleY,
+ Path.Direction.CW,
+ )
+ }
+ outline.setPath(outlinePath)
+ }
+ }
+ }
+
+ override fun onRecycle() {
+ taskHeaderView?.isInvisible = true
+ onSizeChanged = null
+ outlineBounds = null
+ taskThumbnailView?.onRecycle()
+ taskThumbnailViewDeprecated?.onRecycle()
+ }
+
+ fun doOnSizeChange(action: (width: Int, height: Int) -> Unit) {
+ onSizeChanged = action
+ }
+
+ override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
+ super.onSizeChanged(w, h, oldw, oldh)
+ onSizeChanged?.invoke(width, height)
+ bounds.set(0, 0, w, h)
+ invalidateOutline()
+ }
+
+ private fun createHeaderView(taskHeaderState: TaskHeaderUiState) {
+ if (taskHeaderView == null && taskHeaderState is TaskHeaderUiState.ShowHeader) {
+ taskHeaderView =
+ LayoutInflater.from(context).inflate(R.layout.task_header_view, this, false)
+ as TaskHeaderView
+ addView(taskHeaderView, 0)
+ }
+ }
+
+ private fun createTaskThumbnailView() {
+ if (taskThumbnailView == null) {
+ if (enableRefactorTaskThumbnail()) {
+ taskThumbnailView =
+ LayoutInflater.from(context).inflate(R.layout.task_thumbnail, this, false)
+ as TaskThumbnailView
+ addView(taskThumbnailView)
+ } else {
+ taskThumbnailViewDeprecated =
+ LayoutInflater.from(context)
+ .inflate(R.layout.task_thumbnail_deprecated, this, false)
+ as TaskThumbnailViewDeprecated
+ addView(taskThumbnailViewDeprecated)
+ }
+ }
+ }
+
+ fun setState(
+ taskHeaderState: TaskHeaderUiState,
+ taskThumbnailUiState: TaskThumbnailUiState,
+ taskId: Int?,
+ ) {
+ createHeaderView(taskHeaderState)
+ taskHeaderView?.setState(taskHeaderState)
+ taskThumbnailView?.setState(taskThumbnailUiState, taskId)
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskHeaderUiState.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskHeaderUiState.kt
new file mode 100644
index 0000000..09fb540
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskHeaderUiState.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.quickstep.task.thumbnail
+
+import android.graphics.drawable.Drawable
+import android.view.View
+
+sealed class TaskHeaderUiState {
+ data class ShowHeader(val header: ThumbnailHeader) : TaskHeaderUiState()
+
+ data object HideHeader : TaskHeaderUiState()
+
+ data class ThumbnailHeader(
+ val icon: Drawable,
+ val title: String,
+ val clickCloseListener: View.OnClickListener,
+ )
+}
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskOverlayUiState.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskOverlayUiState.kt
deleted file mode 100644
index 5fb5b90..0000000
--- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskOverlayUiState.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.quickstep.task.thumbnail
-
-import android.graphics.Bitmap
-
-/** Ui state for [com.android.quickstep.TaskOverlayFactory.TaskOverlay] */
-sealed class TaskOverlayUiState {
- data object Disabled : TaskOverlayUiState()
-
- data class Enabled(val isRealSnapshot: Boolean, val thumbnail: Bitmap?) : TaskOverlayUiState()
-}
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailUiState.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailUiState.kt
index db593d3..a5c9ac0 100644
--- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailUiState.kt
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailUiState.kt
@@ -19,7 +19,6 @@
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.view.Surface
-import android.view.View.OnClickListener
import androidx.annotation.ColorInt
sealed class TaskThumbnailUiState {
@@ -27,37 +26,14 @@
data class BackgroundOnly(@ColorInt val backgroundColor: Int) : TaskThumbnailUiState()
+ data object LiveTile : TaskThumbnailUiState()
+
data class SnapshotSplash(val snapshot: Snapshot, val splash: Drawable?) :
TaskThumbnailUiState()
- 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,
- val clickCloseListener: OnClickListener,
+ data class Snapshot(
+ val bitmap: Bitmap,
+ @Surface.Rotation val thumbnailRotation: Int,
+ @ColorInt val backgroundColor: Int,
)
}
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
index 0edbacc..78a16f1 100644
--- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
@@ -19,32 +19,24 @@
import android.content.Context
import android.graphics.Color
import android.graphics.Matrix
-import android.graphics.Outline
-import android.graphics.Path
-import android.graphics.Rect
import android.graphics.drawable.ShapeDrawable
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.LauncherAnimUtils.VIEW_ALPHA
import com.android.launcher3.R
import com.android.launcher3.util.MultiPropertyFactory
-import com.android.launcher3.util.ViewPool
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.BackgroundOnly
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.LiveTile
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Snapshot
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.SnapshotSplash
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Uninitialized
import com.android.quickstep.views.FixedSizeImageView
-import com.android.quickstep.views.TaskThumbnailViewHeader
-class TaskThumbnailView : FrameLayout, ViewPool.Reusable {
+class TaskThumbnailView : FrameLayout {
private val scrimView: View by lazy { findViewById(R.id.task_thumbnail_scrim) }
private val liveTileView: LiveTileView by lazy { findViewById(R.id.task_thumbnail_live_tile) }
private val thumbnailView: FixedSizeImageView by lazy { findViewById(R.id.task_thumbnail) }
@@ -53,30 +45,9 @@
private val dimAlpha: MultiPropertyFactory<View> by lazy {
MultiPropertyFactory(scrimView, VIEW_ALPHA, ScrimViewAlpha.entries.size, ::maxOf)
}
- private val outlinePath = Path()
- private var onSizeChanged: ((width: Int, height: Int) -> Unit)? = null
-
- private var taskThumbnailViewHeader: TaskThumbnailViewHeader? = null
private var uiState: TaskThumbnailUiState = Uninitialized
- /**
- * Sets the outline bounds of the view. Default to use view's bound as outline when set to null.
- */
- var outlineBounds: Rect? = null
- set(value) {
- field = value
- invalidateOutline()
- }
-
- private val bounds = Rect()
-
- var cornerRadius: Float = 0f
- set(value) {
- field = value
- invalidateOutline()
- }
-
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
@@ -87,39 +58,8 @@
defStyleAttr: Int,
) : super(context, attrs, defStyleAttr)
- override fun onFinishInflate() {
- super.onFinishInflate()
- maybeCreateHeader()
- }
-
- override fun onAttachedToWindow() {
- super.onAttachedToWindow()
- clipToOutline = true
- outlineProvider =
- object : ViewOutlineProvider() {
- override fun getOutline(view: View, outline: Outline) {
- val outlineRect = outlineBounds ?: bounds
- outlinePath.apply {
- rewind()
- addRoundRect(
- outlineRect.left.toFloat(),
- outlineRect.top.toFloat(),
- outlineRect.right.toFloat(),
- outlineRect.bottom.toFloat(),
- cornerRadius / scaleX,
- cornerRadius / scaleY,
- Path.Direction.CW,
- )
- }
- outline.setPath(outlinePath)
- }
- }
- }
-
- override fun onRecycle() {
+ fun onRecycle() {
uiState = Uninitialized
- onSizeChanged = null
- outlineBounds = null
resetViews()
}
@@ -130,7 +70,7 @@
resetViews()
when (state) {
is Uninitialized -> {}
- is LiveTile -> drawLiveWindow(state)
+ is LiveTile -> drawLiveWindow()
is SnapshotSplash -> drawSnapshotSplash(state)
is BackgroundOnly -> drawBackground(state.backgroundColor)
}
@@ -155,25 +95,12 @@
splashIcon.alpha = value
}
- fun doOnSizeChange(action: (width: Int, height: Int) -> Unit) {
- onSizeChanged = action
- }
-
- override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
- super.onSizeChanged(w, h, oldw, oldh)
- onSizeChanged?.invoke(width, height)
- bounds.set(0, 0, w, h)
- invalidateOutline()
- }
-
- override fun setScaleX(scaleX: Float) {
- super.setScaleX(scaleX)
+ fun parentScaleXUpdated(scaleX: Float) {
// Splash icon should ignore scale on TTV
splashIcon.scaleX = 1 / scaleX
}
- override fun setScaleY(scaleY: Float) {
- super.setScaleY(scaleY)
+ fun parentScaleYUpdated(scaleY: Float) {
// Splash icon should ignore scale on TTV
splashIcon.scaleY = 1 / scaleY
}
@@ -187,20 +114,14 @@
splashIcon.setImageDrawable(null)
scrimView.alpha = 0f
setBackgroundColor(Color.BLACK)
- taskThumbnailViewHeader?.isInvisible = true
}
private fun drawBackground(@ColorInt background: Int) {
setBackgroundColor(background)
}
- private fun drawLiveWindow(liveTile: LiveTile) {
+ private fun drawLiveWindow() {
liveTileView.isInvisible = false
-
- if (liveTile is LiveTile.WithHeader) {
- taskThumbnailViewHeader?.isInvisible = false
- taskThumbnailViewHeader?.setHeader(liveTile.header)
- }
}
private fun drawSnapshotSplash(snapshotSplash: SnapshotSplash) {
@@ -212,11 +133,6 @@
}
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
@@ -232,16 +148,6 @@
Log.d(TAG, "[TaskThumbnailView@${Integer.toHexString(hashCode())}] $message")
}
- private fun maybeCreateHeader() {
- if (enableDesktopExplodedView() && taskThumbnailViewHeader == null) {
- taskThumbnailViewHeader =
- LayoutInflater.from(context)
- .inflate(R.layout.task_thumbnail_view_header, this, false)
- as TaskThumbnailViewHeader
- addView(taskThumbnailViewHeader)
- }
- }
-
private companion object {
const val TAG = "TaskThumbnailView"
private const val MAX_SCRIM_ALPHA = 0.4f
diff --git a/quickstep/src/com/android/quickstep/task/util/TaskOverlayHelper.kt b/quickstep/src/com/android/quickstep/task/util/TaskOverlayHelper.kt
deleted file mode 100644
index f51660b..0000000
--- a/quickstep/src/com/android/quickstep/task/util/TaskOverlayHelper.kt
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.quickstep.task.util
-
-import android.util.Log
-import android.view.View.OnLayoutChangeListener
-import com.android.launcher3.util.coroutines.DispatcherProvider
-import com.android.quickstep.TaskOverlayFactory
-import com.android.quickstep.recents.di.RecentsDependencies
-import com.android.quickstep.recents.di.get
-import com.android.quickstep.task.thumbnail.TaskOverlayUiState
-import com.android.quickstep.task.thumbnail.TaskOverlayUiState.Disabled
-import com.android.quickstep.task.thumbnail.TaskOverlayUiState.Enabled
-import com.android.quickstep.task.viewmodel.TaskOverlayViewModel
-import com.android.systemui.shared.recents.model.Task
-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
-
-/**
- * Helper for [TaskOverlayFactory.TaskOverlay] to interact with [TaskOverlayViewModel], this helper
- * should merge with [TaskOverlayFactory.TaskOverlay] when it's migrated to MVVM.
- */
-class TaskOverlayHelper(val task: Task, val overlay: TaskOverlayFactory.TaskOverlay<*>) {
- private val recentsCoroutineScope: CoroutineScope = RecentsDependencies.get()
- private val dispatcherProvider: DispatcherProvider = RecentsDependencies.get()
- private lateinit var overlayInitializedScope: CoroutineScope
- private var uiState: TaskOverlayUiState = Disabled
-
- private lateinit var viewModel: TaskOverlayViewModel
-
- // TODO(b/331753115): TaskOverlay should listen for state changes and react.
- val enabledState: Enabled
- get() = uiState as Enabled
-
- private val snapshotLayoutChangeListener = OnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
- (uiState as? Enabled)?.let { initOverlay(it) }
- }
-
- fun getThumbnailMatrix() = getThumbnailPositionState().matrix
-
- private fun getThumbnailPositionState() =
- viewModel.getThumbnailPositionState(
- overlay.snapshotView.width,
- overlay.snapshotView.height,
- overlay.snapshotView.isLayoutRtl,
- )
-
- fun init() {
- overlayInitializedScope =
- CoroutineScope(
- SupervisorJob() + Dispatchers.Main.immediate + CoroutineName("TaskOverlayHelper")
- )
- viewModel =
- TaskOverlayViewModel(
- task = task,
- recentsViewData = RecentsDependencies.get(),
- getThumbnailPositionUseCase = RecentsDependencies.get(),
- recentTasksRepository = RecentsDependencies.get(),
- dispatcherProvider = RecentsDependencies.get(),
- )
- viewModel.overlayState
- .dropWhile { it == Disabled }
- .flowOn(dispatcherProvider.background)
- .onEach {
- uiState = it
- if (it is Enabled) {
- initOverlay(it)
- } else {
- reset()
- }
- }
- .launchIn(overlayInitializedScope)
- overlay.snapshotView.addOnLayoutChangeListener(snapshotLayoutChangeListener)
- }
-
- private fun initOverlay(enabledState: Enabled) {
- if (DEBUG) {
- Log.d(TAG, "initOverlay - taskId: ${task.key.id}, thumbnail: ${enabledState.thumbnail}")
- }
- with(getThumbnailPositionState()) {
- overlay.initOverlay(task, enabledState.thumbnail, matrix, isRotated)
- }
- }
-
- private fun reset() {
- if (DEBUG) {
- Log.d(TAG, "reset - taskId: ${task.key.id}")
- }
- overlay.reset()
- }
-
- fun destroy() {
- recentsCoroutineScope.launch(dispatcherProvider.background) {
- overlayInitializedScope.cancel("TaskOverlay being destroyed")
- }
- uiState = Disabled
- overlay.snapshotView.removeOnLayoutChangeListener(snapshotLayoutChangeListener)
- reset()
- }
-
- companion object {
- private const val TAG = "TaskOverlayHelper"
- private const val DEBUG = false
- }
-}
diff --git a/quickstep/src/com/android/quickstep/task/viewmodel/TaskOverlayViewModel.kt b/quickstep/src/com/android/quickstep/task/viewmodel/TaskOverlayViewModel.kt
deleted file mode 100644
index 9bff3ac..0000000
--- a/quickstep/src/com/android/quickstep/task/viewmodel/TaskOverlayViewModel.kt
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.quickstep.task.viewmodel
-
-import android.graphics.Matrix
-import com.android.launcher3.util.coroutines.DispatcherProvider
-import com.android.quickstep.recents.data.RecentTasksRepository
-import com.android.quickstep.recents.domain.usecase.GetThumbnailPositionUseCase
-import com.android.quickstep.recents.viewmodel.RecentsViewData
-import com.android.quickstep.task.thumbnail.TaskOverlayUiState.Disabled
-import com.android.quickstep.task.thumbnail.TaskOverlayUiState.Enabled
-import com.android.systemui.shared.recents.model.Task
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flowOn
-import kotlinx.coroutines.flow.map
-
-/** View model for TaskOverlay */
-class TaskOverlayViewModel(
- private val task: Task,
- recentsViewData: RecentsViewData,
- private val getThumbnailPositionUseCase: GetThumbnailPositionUseCase,
- private val recentTasksRepository: RecentTasksRepository,
- dispatcherProvider: DispatcherProvider,
-) {
- val overlayState =
- combine(
- recentsViewData.overlayEnabled,
- recentsViewData.settledFullyVisibleTaskIds
- .map { it.contains(task.key.id) }
- .distinctUntilChanged(),
- recentTasksRepository.getThumbnailById(task.key.id),
- ) { isOverlayEnabled, isFullyVisible, thumbnailData ->
- if (isOverlayEnabled && isFullyVisible) {
- Enabled(
- isRealSnapshot = (thumbnailData?.isRealSnapshot ?: false) && !task.isLocked,
- thumbnailData?.thumbnail,
- )
- } else {
- Disabled
- }
- }
- .distinctUntilChanged()
- .flowOn(dispatcherProvider.background)
-
- fun getThumbnailPositionState(width: Int, height: Int, isRtl: Boolean): ThumbnailPositionState {
- val thumbnailPositionState =
- getThumbnailPositionUseCase(
- thumbnailData = recentTasksRepository.getCurrentThumbnailById(task.key.id),
- width = width,
- height = height,
- isRtl = isRtl,
- )
- return ThumbnailPositionState(
- thumbnailPositionState.matrix,
- thumbnailPositionState.isRotated,
- )
- }
-
- data class ThumbnailPositionState(val matrix: Matrix, val isRotated: Boolean)
-}
diff --git a/quickstep/src/com/android/quickstep/util/ActivityPreloadUtil.kt b/quickstep/src/com/android/quickstep/util/ActivityPreloadUtil.kt
index 47b39db..df26b6b 100644
--- a/quickstep/src/com/android/quickstep/util/ActivityPreloadUtil.kt
+++ b/quickstep/src/com/android/quickstep/util/ActivityPreloadUtil.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.content.Intent
import android.os.Trace
+import android.view.Display.DEFAULT_DISPLAY
import com.android.launcher3.provider.RestoreDbTask
import com.android.launcher3.util.Executors
import com.android.launcher3.util.LockedUserState
@@ -59,7 +60,12 @@
// 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
+ if (
+ fromInit &&
+ overviewCompObserver.getContainerInterface(DEFAULT_DISPLAY).createdContainer !=
+ null
+ )
+ return
ActiveGestureProtoLogProxy.logPreloadRecentsAnimation()
val overviewIntent = Intent(overviewCompObserver.overviewIntentIgnoreSysUiState)
diff --git a/quickstep/src/com/android/quickstep/util/AnimUtils.java b/quickstep/src/com/android/quickstep/util/AnimUtils.java
index fda0c29..3492788 100644
--- a/quickstep/src/com/android/quickstep/util/AnimUtils.java
+++ b/quickstep/src/com/android/quickstep/util/AnimUtils.java
@@ -21,16 +21,22 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import android.animation.AnimatorSet;
-import android.annotation.NonNull;
+import android.os.BinderUtils;
import android.os.Bundle;
+import android.os.IBinder;
import android.os.IRemoteCallback;
import android.view.animation.Interpolator;
+import androidx.annotation.NonNull;
+import androidx.lifecycle.DefaultLifecycleObserver;
+import androidx.lifecycle.LifecycleOwner;
+
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.util.RunnableList;
+import com.android.launcher3.views.ActivityContext;
import com.android.quickstep.views.RecentsViewContainer;
/**
@@ -95,14 +101,30 @@
}
/**
- * Returns a IRemoteCallback which completes the provided list as a result
+ * Returns a IRemoteCallback which completes the provided list as a result or when the owner
+ * is destroyed
*/
- public static IRemoteCallback completeRunnableListCallback(RunnableList list) {
+ public static IRemoteCallback completeRunnableListCallback(
+ RunnableList list, ActivityContext owner) {
+ DefaultLifecycleObserver destroyObserver = new DefaultLifecycleObserver() {
+ @Override
+ public void onDestroy(@NonNull LifecycleOwner owner) {
+ list.executeAllAndClear();
+ }
+ };
+ MAIN_EXECUTOR.execute(() -> owner.getLifecycle().addObserver(destroyObserver));
+ list.add(() -> owner.getLifecycle().removeObserver(destroyObserver));
+
return new IRemoteCallback.Stub() {
@Override
public void sendResult(Bundle bundle) {
MAIN_EXECUTOR.execute(list::executeAllAndDestroy);
}
+
+ @Override
+ public IBinder asBinder() {
+ return BinderUtils.wrapLifecycle(this, owner.getOwnerCleanupSet());
+ }
};
}
diff --git a/quickstep/src/com/android/quickstep/util/BaseDepthController.java b/quickstep/src/com/android/quickstep/util/BaseDepthController.java
index 5d6bb1d..89f1e33 100644
--- a/quickstep/src/com/android/quickstep/util/BaseDepthController.java
+++ b/quickstep/src/com/android/quickstep/util/BaseDepthController.java
@@ -15,6 +15,7 @@
*/
package com.android.quickstep.util;
+import static com.android.launcher3.Flags.enableOverviewBackgroundWallpaperBlur;
import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
import android.app.WallpaperManager;
@@ -24,6 +25,9 @@
import android.view.AttachedSurfaceControl;
import android.view.SurfaceControl;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.Flags;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -74,11 +78,14 @@
/**
* Ratio from 0 to 1, where 0 is fully zoomed out, and 1 is zoomed in.
+ *
* @see android.service.wallpaper.WallpaperService.Engine#onZoomChanged(float)
*/
private float mDepth;
- protected SurfaceControl mSurface;
+ protected SurfaceControl mBaseSurface;
+
+ protected SurfaceControl mBaseSurfaceOverride;
// Hints that there is potentially content behind Launcher and that we shouldn't optimize by
// marking the launcher surface as opaque. Only used in certain Launcher states.
@@ -99,15 +106,29 @@
protected boolean mWaitingOnSurfaceValidity;
+ private SurfaceControl mBlurSurface = null;
+
public BaseDepthController(Launcher activity) {
mLauncher = activity;
- mMaxBlurRadius = activity.getResources().getInteger(R.integer.max_depth_blur_radius);
+ if (Flags.allAppsBlur()) {
+ mMaxBlurRadius = activity.getResources().getDimensionPixelSize(
+ R.dimen.max_depth_blur_radius_enhanced);
+ } else {
+ mMaxBlurRadius = activity.getResources().getInteger(R.integer.max_depth_blur_radius);
+ }
mWallpaperManager = activity.getSystemService(WallpaperManager.class);
MultiPropertyFactory<BaseDepthController> depthProperty =
new MultiPropertyFactory<>(this, DEPTH, DEPTH_INDEX_COUNT, Float::max);
stateDepth = depthProperty.get(DEPTH_INDEX_STATE_TRANSITION);
widgetDepth = depthProperty.get(DEPTH_INDEX_WIDGET);
+ if (enableOverviewBackgroundWallpaperBlur()) {
+ mBlurSurface = new SurfaceControl.Builder()
+ .setName("Overview Blur")
+ .setHidden(false)
+ .build();
+ }
+
}
protected void setCrossWindowBlursEnabled(boolean isEnabled) {
@@ -145,11 +166,11 @@
if (!BlurUtils.supportsBlursOnWindows()) {
return;
}
- if (mSurface == null) {
+ if (mBaseSurface == null) {
Log.d(TAG, "mSurface is null and mCurrentBlur is: " + mCurrentBlur);
return;
}
- if (!mSurface.isValid()) {
+ if (!mBaseSurface.isValid()) {
Log.d(TAG, "mSurface is not valid");
mWaitingOnSurfaceValidity = true;
onInvalidSurface();
@@ -168,10 +189,21 @@
mCurrentBlur = !mCrossWindowBlursEnabled || hasOpaqueBg || mPauseBlurs
? 0 : (int) (blurAmount * mMaxBlurRadius);
- SurfaceControl.Transaction transaction = new SurfaceControl.Transaction()
- .setBackgroundBlurRadius(mSurface, mCurrentBlur)
- .setOpaque(mSurface, isSurfaceOpaque);
-
+ SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+ if (enableOverviewBackgroundWallpaperBlur() && mBlurSurface != null) {
+ // Reparent to launcher for full screen blur.
+ transaction.setBackgroundBlurRadius(mBlurSurface, mCurrentBlur)
+ .reparent(mBlurSurface, mBaseSurface);
+ // Set mBlurSurface to be 1 layer behind mBaseSurface or mBaseSurfaceOverride.
+ if (mBaseSurfaceOverride != null && mBaseSurfaceOverride.isValid()) {
+ transaction.setRelativeLayer(mBlurSurface, mBaseSurfaceOverride, -1);
+ } else {
+ transaction.setRelativeLayer(mBlurSurface, mBaseSurface, -1);
+ }
+ } else {
+ transaction.setBackgroundBlurRadius(mBaseSurface, mCurrentBlur);
+ }
+ transaction.setOpaque(mBaseSurface, isSurfaceOpaque);
// Set early wake-up flags when we know we're executing an expensive operation, this way
// SurfaceFlinger will adjust its internal offsets to avoid jank.
boolean wantsEarlyWakeUp = depth > 0 && depth < 1;
@@ -203,13 +235,26 @@
}
/**
+ * Sets the lowest surface that should not be blurred.
+ * <p>
+ * Blur is applied to below {@link #mBaseSurfaceOverride}. When set to {@code null}, blur is
+ * applied
+ * to below {@link #mBaseSurface}.
+ * </p>
+ */
+ public void setBaseSurfaceOverride(@Nullable SurfaceControl baseSurfaceOverride) {
+ this.mBaseSurfaceOverride = baseSurfaceOverride;
+ applyDepthAndBlur();
+ }
+
+ /**
* Sets the specified app target surface to apply the blur to.
*/
- protected void setSurface(SurfaceControl surface) {
- if (mSurface != surface || mWaitingOnSurfaceValidity) {
- mSurface = surface;
+ protected void setBaseSurface(SurfaceControl baseSurface) {
+ if (mBaseSurface != baseSurface || mWaitingOnSurfaceValidity) {
+ mBaseSurface = baseSurface;
Log.d(TAG, "setSurface:\n\tmWaitingOnSurfaceValidity: " + mWaitingOnSurfaceValidity
- + "\n\tmSurface: " + mSurface);
+ + "\n\tmBaseSurface: " + mBaseSurface);
applyDepthAndBlur();
}
}
diff --git a/quickstep/src/com/android/quickstep/util/BorderAnimator.kt b/quickstep/src/com/android/quickstep/util/BorderAnimator.kt
index 7e51fcf..d1d47b9 100644
--- a/quickstep/src/com/android/quickstep/util/BorderAnimator.kt
+++ b/quickstep/src/com/android/quickstep/util/BorderAnimator.kt
@@ -114,6 +114,8 @@
*
* @param borderRadiusPx the radius of the border's corners, in pixels
* @param borderWidthPx the width of the border, in pixels
+ * @param borderStrokePx the stroke width used to paint the border, in pixels. If smaller
+ * than border width, it gets drawn at the outside edge of the border.
* @param boundsBuilder callback to update the border bounds
* @param targetView the view that will be drawing the border
* @param contentView the view around which the border will be drawn. this view will be
@@ -128,6 +130,7 @@
fun createScalingBorderAnimator(
@Px borderRadiusPx: Int,
@Px borderWidthPx: Int,
+ @Px borderStrokePx: Int,
boundsBuilder: (rect: Rect?) -> Unit,
targetView: View,
contentView: View,
@@ -139,7 +142,13 @@
return BorderAnimator(
borderRadiusPx,
borderColor,
- ScalingParams(borderWidthPx, boundsBuilder, targetView, contentView),
+ ScalingParams(
+ borderWidthPx,
+ borderStrokePx,
+ boundsBuilder,
+ targetView,
+ contentView,
+ ),
appearanceDurationMs,
disappearanceDurationMs,
interpolator,
@@ -151,7 +160,7 @@
val interpolatedProgress = interpolator.getInterpolation(borderAnimationProgress.value)
borderAnimationParams.animationProgress = interpolatedProgress
borderPaint.alpha = (255 * interpolatedProgress).roundToInt()
- borderPaint.strokeWidth = borderAnimationParams.borderWidth
+ borderPaint.strokeWidth = borderAnimationParams.borderStroke
borderAnimationParams.targetView.invalidate()
}
@@ -170,7 +179,7 @@
/* bottom= */ borderBounds.bottom - alignmentAdjustment,
/* rx= */ radius,
/* ry= */ radius,
- /* paint= */ borderPaint
+ /* paint= */ borderPaint,
)
}
}
@@ -212,6 +221,7 @@
/** Params for handling different target view layout situations. */
private abstract class BorderAnimationParams(
@field:Px @param:Px val borderWidthPx: Int,
+ @field:Px @param:Px val borderStrokePx: Int,
private val boundsBuilder: (rect: Rect) -> Unit,
val targetView: View,
) {
@@ -222,12 +232,12 @@
abstract val alignmentAdjustmentInset: Int
abstract val radiusAdjustment: Float
- val borderWidth: Float
- get() = borderWidthPx * animationProgress
+ val borderStroke: Float
+ get() = borderStrokePx * animationProgress
val alignmentAdjustment: Float
// Outset the border by half the width to create an outwards-growth animation
- get() = -borderWidth / 2f + alignmentAdjustmentInset
+ get() = -borderStroke / 2f + alignmentAdjustmentInset
open fun onShowBorder() {
if (layoutChangeListener == null) {
@@ -253,7 +263,7 @@
@Px borderWidthPx: Int,
boundsBuilder: (Rect) -> Unit,
targetView: View,
- ) : BorderAnimationParams(borderWidthPx, boundsBuilder, targetView) {
+ ) : BorderAnimationParams(borderWidthPx, borderWidthPx, boundsBuilder, targetView) {
override val alignmentAdjustmentInset = 0
override val radiusAdjustment: Float
get() = -alignmentAdjustment
@@ -265,12 +275,13 @@
*/
private class ScalingParams(
@Px borderWidthPx: Int,
+ @Px borderStrokePx: Int,
boundsBuilder: (rect: Rect?) -> Unit,
targetView: View,
private val contentView: View,
- ) : BorderAnimationParams(borderWidthPx, boundsBuilder, targetView) {
+ ) : BorderAnimationParams(borderWidthPx, borderStrokePx, boundsBuilder, targetView) {
// Inset the border since we are scaling the container up
- override val alignmentAdjustmentInset = borderWidthPx
+ override val alignmentAdjustmentInset = borderStrokePx
override val radiusAdjustment: Float
// Increase the radius since we are scaling the container up
get() = alignmentAdjustment
diff --git a/quickstep/src/com/android/quickstep/util/ContextualSearchInvoker.kt b/quickstep/src/com/android/quickstep/util/ContextualSearchInvoker.kt
index d00a39c..3bc9adc 100644
--- a/quickstep/src/com/android/quickstep/util/ContextualSearchInvoker.kt
+++ b/quickstep/src/com/android/quickstep/util/ContextualSearchInvoker.kt
@@ -21,6 +21,7 @@
import android.app.contextualsearch.ContextualSearchManager.FEATURE_CONTEXTUAL_SEARCH
import android.content.Context
import android.util.Log
+import android.view.Display.DEFAULT_DISPLAY
import androidx.annotation.VisibleForTesting
import com.android.internal.app.AssistUtils
import com.android.launcher3.logging.StatsLogManager
@@ -222,7 +223,8 @@
@VisibleForTesting
fun getRecentsContainerInterface(): BaseContainerInterface<*, *>? {
- return OverviewComponentObserver.INSTANCE.get(context).containerInterface
+ return OverviewComponentObserver.INSTANCE.get(context)
+ .getContainerInterface(DEFAULT_DISPLAY)
}
/**
diff --git a/quickstep/src/com/android/quickstep/util/DesksUtils.kt b/quickstep/src/com/android/quickstep/util/DesksUtils.kt
new file mode 100644
index 0000000..a10f771
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/DesksUtils.kt
@@ -0,0 +1,52 @@
+/*
+ * 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.app.TaskInfo
+import android.content.ComponentName
+import android.content.res.Resources
+import android.util.Log
+import android.window.DesktopExperienceFlags
+import com.android.systemui.shared.recents.model.Task
+
+class DesksUtils {
+ companion object {
+ val sysUiPackage =
+ Resources.getSystem().getString(com.android.internal.R.string.config_systemUi)
+
+ @JvmStatic
+ fun areMultiDesksFlagsEnabled() =
+ DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue &&
+ DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_FRONTEND.isTrue
+
+ /** Returns true if this [task] contains the [DesktopWallpaperActivity]. */
+ @JvmStatic
+ fun isDesktopWallpaperTask(task: Task) =
+ task.key.component?.let(::isDesktopWallpaperComponent) == true
+
+ @JvmStatic
+ fun isDesktopWallpaperTask(taskInfo: TaskInfo): Boolean {
+ Log.d("b/403118101", "isDesktopWallpaperTask: $taskInfo")
+ return taskInfo.baseIntent.component?.let(::isDesktopWallpaperComponent) == true
+ }
+
+ @JvmStatic
+ fun isDesktopWallpaperComponent(component: ComponentName) =
+ component.className.contains("DesktopWallpaperActivity") &&
+ component.packageName.contains(sysUiPackage)
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/GroupTask.kt b/quickstep/src/com/android/quickstep/util/GroupTask.kt
index 49c37dc..add8821 100644
--- a/quickstep/src/com/android/quickstep/util/GroupTask.kt
+++ b/quickstep/src/com/android/quickstep/util/GroupTask.kt
@@ -15,6 +15,9 @@
*/
package com.android.quickstep.util
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_TASK
+import com.android.launcher3.model.data.TaskItemInfo
+import com.android.launcher3.model.data.WorkspaceItemInfo
import com.android.launcher3.util.SplitConfigurationOptions
import com.android.quickstep.views.TaskViewType
import com.android.systemui.shared.recents.model.Task
@@ -68,6 +71,21 @@
if (o !is SingleTask) return false
return super.equals(o)
}
+
+ companion object {
+ /** Creates a [TaskItemInfo] using the information of the SingleTask */
+ fun createTaskItemInfo(task: SingleTask): TaskItemInfo {
+ // TODO: b/344657629 - Support GroupTask in addition to SingleTask.
+ val wii =
+ WorkspaceItemInfo().apply {
+ title = task.task.title
+ intent = task.task.key.baseIntent
+ itemType = ITEM_TYPE_TASK
+ contentDescription = task.task.titleDescription
+ }
+ return TaskItemInfo(task.task.key.id, wii)
+ }
+ }
}
/**
diff --git a/quickstep/src/com/android/quickstep/util/InputProxyHandlerFactory.java b/quickstep/src/com/android/quickstep/util/InputProxyHandlerFactory.java
index 843619d..9aded89 100644
--- a/quickstep/src/com/android/quickstep/util/InputProxyHandlerFactory.java
+++ b/quickstep/src/com/android/quickstep/util/InputProxyHandlerFactory.java
@@ -35,8 +35,8 @@
private final GestureState mGestureState;
@UiThread
- public InputProxyHandlerFactory(BaseContainerInterface activityInterface,
- GestureState gestureState) {
+ public InputProxyHandlerFactory(
+ BaseContainerInterface activityInterface, GestureState gestureState) {
mContainerInterface = activityInterface;
mGestureState = gestureState;
}
@@ -47,7 +47,8 @@
@Override
public InputConsumer get() {
RecentsViewContainer container = mContainerInterface.getCreatedContainer();
- return container == null ? InputConsumer.NO_OP
+ return container == null
+ ? InputConsumer.createNoOpInputConsumer(mGestureState.getDisplayId())
: new OverviewInputConsumer(mGestureState, container, null, true);
}
}
diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
index a5be89a..9cdde01 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
@@ -22,7 +22,9 @@
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
+import static com.android.launcher3.Flags.enableOverviewOnConnectedDisplays;
import static com.android.launcher3.LauncherPrefs.ALLOW_ROTATION;
+import static com.android.launcher3.LauncherPrefs.FIXED_LANDSCAPE_MODE;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.SettingsCache.ROTATION_SETTING_URI;
import static com.android.quickstep.BaseActivityInterface.getTaskDimension;
@@ -43,6 +45,7 @@
import androidx.annotation.NonNull;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Flags;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherPrefChangeListener;
import com.android.launcher3.LauncherPrefs;
@@ -53,6 +56,7 @@
import com.android.quickstep.BaseContainerInterface;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskAnimationManager;
+import com.android.quickstep.fallback.window.RecentsDisplayModel;
import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
import java.lang.annotation.Retention;
@@ -105,9 +109,12 @@
// Ignore shared prefs for home rotation rotation, allowing it in if the activity supports it
private static final int FLAG_IGNORE_ALLOW_HOME_ROTATION_PREF = 1 << 9;
+ // Shared prefs for fixed 90 degree rotation, activities should rotate if they support it
+ private static final int FLAG_HOME_FIXED_LANDSCAPE_PREFS = 1 << 10;
+
private static final int MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE =
FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_ACTIVITY
- | FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_DENSITY;
+ | FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_DENSITY;
// State for which rotation watcher will be enabled. We skip it when home rotation or
// multi-window is enabled as in that case, activity itself rotates.
@@ -225,7 +232,7 @@
private boolean updateHandler() {
mRecentsActivityRotation = inferRecentsActivityRotation(mDisplayRotation);
- if (mRecentsActivityRotation == mTouchRotation || isRecentsActivityRotationAllowed()) {
+ if (mRecentsActivityRotation == mTouchRotation || shouldUseRealOrientation()) {
mOrientationHandler = RecentsPagedOrientationHandler.PORTRAIT;
} else if (mTouchRotation == ROTATION_90) {
mOrientationHandler = RecentsPagedOrientationHandler.LANDSCAPE;
@@ -247,9 +254,13 @@
return mStateId != oldStateId;
}
+ private boolean shouldUseRealOrientation() {
+ return isRecentsActivityRotationAllowed() || isLauncherFixedLandscape();
+ }
+
@SurfaceRotation
private int inferRecentsActivityRotation(@SurfaceRotation int displayRotation) {
- if (isRecentsActivityRotationAllowed()) {
+ if (shouldUseRealOrientation()) {
return mRecentsRotation < 0 ? displayRotation : mRecentsRotation;
} else {
return ROTATION_0;
@@ -286,6 +297,9 @@
if (LauncherPrefs.ALLOW_ROTATION.getSharedPrefKey().equals(s)) {
updateHomeRotationSetting();
}
+ if (LauncherPrefs.FIXED_LANDSCAPE_MODE.getSharedPrefKey().equals(s)) {
+ updateFixedLandscapeSetting();
+ }
}
private void updateAutoRotateSetting() {
@@ -293,6 +307,15 @@
mSettingsCache.getValue(ROTATION_SETTING_URI, 1));
}
+ private void updateFixedLandscapeSetting() {
+ if (Flags.oneGridSpecs()) {
+ setFlag(
+ FLAG_HOME_FIXED_LANDSCAPE_PREFS,
+ LauncherPrefs.get(mContext).get(FIXED_LANDSCAPE_MODE)
+ );
+ }
+ }
+
private void updateHomeRotationSetting() {
boolean homeRotationEnabled = LauncherPrefs.get(mContext).get(ALLOW_ROTATION);
setFlag(FLAG_HOME_ROTATION_ALLOWED_IN_PREFS, homeRotationEnabled);
@@ -305,6 +328,7 @@
// initialize external flags
updateAutoRotateSetting();
updateHomeRotationSetting();
+ updateFixedLandscapeSetting();
}
private void initMultipleOrientationListeners() {
@@ -381,15 +405,19 @@
setFlag(FLAG_IGNORE_ALLOW_HOME_ROTATION_PREF, true);
}
+ public boolean isLauncherFixedLandscape() {
+ return (mFlags & FLAG_HOME_FIXED_LANDSCAPE_PREFS) == FLAG_HOME_FIXED_LANDSCAPE_PREFS;
+ }
+
public boolean isRecentsActivityRotationAllowed() {
// Activity rotation is allowed if the multi-simulated-rotation is not supported
// (fallback recents or tablets) or activity rotation is enabled by various settings.
return ((mFlags & MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE)
!= MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE)
|| (mFlags & (FLAG_IGNORE_ALLOW_HOME_ROTATION_PREF
- | FLAG_HOME_ROTATION_ALLOWED_IN_PREFS
- | FLAG_MULTIWINDOW_ROTATION_ALLOWED
- | FLAG_HOME_ROTATION_FORCE_ENABLED_FOR_TESTING)) != 0;
+ | FLAG_HOME_ROTATION_ALLOWED_IN_PREFS
+ | FLAG_MULTIWINDOW_ROTATION_ALLOWED
+ | FLAG_HOME_ROTATION_FORCE_ENABLED_FOR_TESTING)) != 0;
}
/**
@@ -571,19 +599,25 @@
/**
* Returns the device profile based on expected launcher rotation
*/
- public DeviceProfile getLauncherDeviceProfile() {
- InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(mContext);
- Point currentSize = DisplayController.INSTANCE.get(mContext).getInfo().currentSize;
-
- int width, height;
- if ((mRecentsActivityRotation == ROTATION_90 || mRecentsActivityRotation == ROTATION_270)) {
- width = Math.max(currentSize.x, currentSize.y);
- height = Math.min(currentSize.x, currentSize.y);
+ public DeviceProfile getLauncherDeviceProfile(int displayId) {
+ if (enableOverviewOnConnectedDisplays()) {
+ return RecentsDisplayModel.getINSTANCE().get(mContext).getRecentsWindowManager(
+ displayId).getDeviceProfile();
} else {
- width = Math.min(currentSize.x, currentSize.y);
- height = Math.max(currentSize.x, currentSize.y);
+ InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(mContext);
+ Point currentSize = DisplayController.INSTANCE.get(mContext).getInfo().currentSize;
+
+ int width, height;
+ if ((mRecentsActivityRotation == ROTATION_90
+ || mRecentsActivityRotation == ROTATION_270)) {
+ width = Math.max(currentSize.x, currentSize.y);
+ height = Math.min(currentSize.x, currentSize.y);
+ } else {
+ width = Math.min(currentSize.x, currentSize.y);
+ height = Math.max(currentSize.x, currentSize.y);
+ }
+ return idp.getBestMatch(width, height, mRecentsActivityRotation);
}
- return idp.getBestMatch(width, height, mRecentsActivityRotation);
}
private static String nameAndAddress(Object obj) {
diff --git a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
index 96a5733..d6e553d 100644
--- a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
+++ b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
@@ -192,7 +192,7 @@
taskViewHeight: Int,
isPrimaryTaskSplitting: Boolean,
) {
- val snapshot = taskContainer.snapshotView
+ val taskContentView = taskContainer.taskContentView
val iconView: View = taskContainer.iconView.asView()
if (enableRefactorTaskThumbnail()) {
builder.add(
@@ -241,7 +241,11 @@
val centerThumbnailTranslationX: Float = (taskViewWidth - snapshotViewSize.x) / 2f
val finalScaleX: Float = taskViewWidth.toFloat() / snapshotViewSize.x
builder.add(
- ObjectAnimator.ofFloat(snapshot, View.TRANSLATION_X, centerThumbnailTranslationX)
+ ObjectAnimator.ofFloat(
+ taskContentView,
+ View.TRANSLATION_X,
+ centerThumbnailTranslationX,
+ )
)
if (!enableOverviewIconMenu()) {
// icons are anchored from Gravity.END, so need to use negative translation
@@ -250,15 +254,17 @@
ObjectAnimator.ofFloat(iconView, View.TRANSLATION_X, -centerIconTranslationX)
)
}
- builder.add(ObjectAnimator.ofFloat(snapshot, View.SCALE_X, finalScaleX))
+ builder.add(ObjectAnimator.ofFloat(taskContentView, View.SCALE_X, finalScaleX))
// Reset other dimensions
// TODO(b/271468547), can't set Y translate to 0, need to account for top space
- snapshot.scaleY = 1f
+ taskContentView.scaleY = 1f
val translateYResetVal: Float =
if (!isPrimaryTaskSplitting) 0f
else deviceProfile.overviewTaskThumbnailTopMarginPx.toFloat()
- builder.add(ObjectAnimator.ofFloat(snapshot, View.TRANSLATION_Y, translateYResetVal))
+ builder.add(
+ ObjectAnimator.ofFloat(taskContentView, View.TRANSLATION_Y, translateYResetVal)
+ )
} else {
val thumbnailSize = taskViewHeight - deviceProfile.overviewTaskThumbnailTopMarginPx
// Center view first so scaling happens uniformly, alternatively we can move pivotY to 0
@@ -281,18 +287,22 @@
}
val finalScaleY: Float = thumbnailSize.toFloat() / snapshotViewSize.y
builder.add(
- ObjectAnimator.ofFloat(snapshot, View.TRANSLATION_Y, centerThumbnailTranslationY)
+ ObjectAnimator.ofFloat(
+ taskContentView,
+ View.TRANSLATION_Y,
+ centerThumbnailTranslationY,
+ )
)
if (!enableOverviewIconMenu()) {
// icons are anchored from Gravity.END, so need to use negative translation
builder.add(ObjectAnimator.ofFloat(iconView, View.TRANSLATION_X, 0f))
}
- builder.add(ObjectAnimator.ofFloat(snapshot, View.SCALE_Y, finalScaleY))
+ builder.add(ObjectAnimator.ofFloat(taskContentView, View.SCALE_Y, finalScaleY))
// Reset other dimensions
- snapshot.scaleX = 1f
- builder.add(ObjectAnimator.ofFloat(snapshot, View.TRANSLATION_X, 0f))
+ taskContentView.scaleX = 1f
+ builder.add(ObjectAnimator.ofFloat(taskContentView, View.TRANSLATION_X, 0f))
}
}
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index b844079..661fe89 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -378,6 +378,29 @@
}
/**
+ * Calculates the crop rect for desktop tasks given the current matrix.
+ */
+ private void calculateDesktopTaskCropRect() {
+ // The approach here is to map a rect that represents the untransformed thumbnail position
+ // using the current matrix. This will give us a rect that can be intersected with
+ // [mFullTaskSize]. Using the intersection, we then compute how much of the task window that
+ // needs to be cropped (which will be nothing if the window is entirely within the desktop).
+ mTempRectF.set(0, 0, mThumbnailPosition.width(), mThumbnailPosition.height());
+ mMatrix.mapRect(mTempRectF);
+
+ float offsetX = mTempRectF.left;
+ float offsetY = mTempRectF.top;
+ float scale = mThumbnailPosition.width() / mTempRectF.width();
+
+ if (mTempRectF.intersect(mFullTaskSize.left, mFullTaskSize.top, mFullTaskSize.right,
+ mFullTaskSize.bottom)) {
+ mTempRectF.offset(-offsetX, -offsetY);
+ mTempRectF.scale(scale);
+ mTempRectF.round(mTmpCropRect);
+ }
+ }
+
+ /**
* Applies the rotation on the matrix to so that it maps from launcher coordinate space to
* window coordinate space.
*/
@@ -442,7 +465,16 @@
mMatrix.postTranslate(mTaskRect.left, mTaskRect.top);
if (mTaskRectTransform != null) {
mMatrix.postConcat(mTaskRectTransform);
+
+ // Calculate cropping for desktop tasks. The order is important since it uses the
+ // current matrix. Therefore we calculate it here, after applying the task rect
+ // transform, but before applying scaling/translation that affects the whole
+ // recentsview.
+ if (mIsDesktopTask) {
+ calculateDesktopTaskCropRect();
+ }
}
+
mOrientationState.getOrientationHandler().setPrimary(mMatrix, MATRIX_POST_TRANSLATE,
taskPrimaryTranslation.value);
mOrientationState.getOrientationHandler().setSecondary(mMatrix, MATRIX_POST_TRANSLATE,
diff --git a/quickstep/src/com/android/quickstep/views/AddDesktopButton.kt b/quickstep/src/com/android/quickstep/views/AddDesktopButton.kt
index b83acf0..37359a1 100644
--- a/quickstep/src/com/android/quickstep/views/AddDesktopButton.kt
+++ b/quickstep/src/com/android/quickstep/views/AddDesktopButton.kt
@@ -84,9 +84,6 @@
}
}
- fun getScrollAdjustment(showAsGrid: Boolean): Int =
- if (showAsGrid) gridTranslationX.toInt() else 0
-
private fun getBorderBounds(bounds: Rect) {
bounds.set(0, 0, width, height)
val outlinePadding =
diff --git a/quickstep/src/com/android/quickstep/views/BlurUtils.kt b/quickstep/src/com/android/quickstep/views/BlurUtils.kt
new file mode 100644
index 0000000..d6b2a05
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/BlurUtils.kt
@@ -0,0 +1,63 @@
+/*
+ * 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 com.android.launcher3.Flags.enableOverviewBackgroundWallpaperBlur
+import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle
+
+/** Applies blur either behind launcher surface or live tile app. */
+class BlurUtils(private val recentsView: RecentsView<*, *>) {
+
+ fun setDrawLiveTileBelowRecents(drawBelowRecents: Boolean) {
+ val liveTileRemoteTargetHandles =
+ if (
+ recentsView.remoteTargetHandles != null &&
+ recentsView.recentsAnimationController != null
+ )
+ recentsView.remoteTargetHandles
+ else null
+ setDrawBelowRecents(drawBelowRecents, liveTileRemoteTargetHandles)
+ }
+
+ /**
+ * Set surface in [remoteTargetHandles] to be above or below Recents layer, and update the base
+ * layer to apply blur to in BaseDepthController.
+ */
+ fun setDrawBelowRecents(
+ drawBelowRecents: Boolean,
+ remoteTargetHandles: Array<RemoteTargetHandle>? = null,
+ ) {
+ remoteTargetHandles?.forEach { it.taskViewSimulator.setDrawsBelowRecents(drawBelowRecents) }
+ if (enableOverviewBackgroundWallpaperBlur()) {
+ recentsView.depthController?.setBaseSurfaceOverride(
+ // Blurs behind launcher layer.
+ if (!drawBelowRecents || remoteTargetHandles == null) {
+ null
+ } else {
+ // Blurs behind live tile. blur will be applied behind window
+ // which farthest from user in case of desktop and split apps.
+ remoteTargetHandles
+ .maxByOrNull { it.transformParams.targetSet.firstAppTarget.leash.layerId }
+ ?.transformParams
+ ?.targetSet
+ ?.firstAppTarget
+ ?.leash
+ }
+ )
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
index 75f3b69..27657b4 100644
--- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
@@ -35,6 +35,7 @@
import com.android.launcher3.Flags.enableOverviewIconMenu
import com.android.launcher3.Flags.enableRefactorTaskThumbnail
import com.android.launcher3.R
+import com.android.launcher3.statehandlers.DesktopVisibilityController
import com.android.launcher3.testing.TestLogging
import com.android.launcher3.testing.shared.TestProtocol
import com.android.launcher3.util.RunnableList
@@ -54,6 +55,7 @@
import com.android.quickstep.recents.domain.model.DesktopTaskBoundsData
import com.android.quickstep.recents.ui.viewmodel.DesktopTaskViewModel
import com.android.quickstep.recents.ui.viewmodel.TaskData
+import com.android.quickstep.task.thumbnail.TaskContentView
import com.android.quickstep.task.thumbnail.TaskThumbnailView
import com.android.quickstep.util.DesktopTask
import com.android.quickstep.util.RecentsOrientedState
@@ -67,29 +69,18 @@
type = TaskViewType.DESKTOP,
thumbnailFullscreenParams = DesktopFullscreenDrawParams(context),
) {
+ var deskId = DesktopVisibilityController.INACTIVE_DESK_ID
+
private val contentViewFullscreenParams = FullscreenDrawParams(context)
- private val taskThumbnailViewDeprecatedPool =
- if (!enableRefactorTaskThumbnail()) {
- ViewPool<TaskThumbnailViewDeprecated>(
- context,
- this,
- R.layout.task_thumbnail_deprecated,
- VIEW_POOL_MAX_SIZE,
- VIEW_POOL_INITIAL_SIZE,
- )
- } else null
-
- private val taskThumbnailViewPool =
- if (enableRefactorTaskThumbnail()) {
- ViewPool<TaskThumbnailView>(
- context,
- this,
- R.layout.task_thumbnail,
- VIEW_POOL_MAX_SIZE,
- VIEW_POOL_INITIAL_SIZE,
- )
- } else null
+ private val taskContentViewPool =
+ ViewPool<TaskContentView>(
+ context,
+ this,
+ R.layout.task_content_view,
+ VIEW_POOL_MAX_SIZE,
+ VIEW_POOL_INITIAL_SIZE,
+ )
private val tempPointF = PointF()
private val lastComputedTaskSize = Rect()
@@ -239,7 +230,7 @@
// for all cases where the progress is non-zero.
if (explodeProgress == 0.0f || explodeProgress == 1.0f) {
// Reset scaling and translation that may have been applied during animation.
- it.snapshotView.apply {
+ it.taskContentView.apply {
scaleX = 1.0f
scaleY = 1.0f
translationX = 0.0f
@@ -247,7 +238,7 @@
}
// Position the task to the same position as it would be on the desktop
- it.snapshotView.updateLayoutParams<LayoutParams> {
+ it.taskContentView?.updateLayoutParams<LayoutParams> {
gravity = Gravity.LEFT or Gravity.TOP
width = taskWidth.toInt()
height = taskHeight.toInt()
@@ -258,7 +249,7 @@
if (
enableDesktopRecentsTransitionsCornersBugfix() && enableRefactorTaskThumbnail()
) {
- it.thumbnailView.outlineBounds =
+ it.taskContentView?.outlineBounds =
if (intersects(overviewTaskPosition, screenRect))
Rect(overviewTaskPosition).apply {
intersectUnchecked(screenRect)
@@ -275,7 +266,7 @@
} else {
// During the animation, apply translation and scale such that the view is
// transformed to where we want, without triggering layout.
- it.snapshotView.apply {
+ it.taskContentView.apply {
pivotX = 0.0f
pivotY = 0.0f
translationX = taskLeft - left
@@ -293,6 +284,7 @@
orientedState: RecentsOrientedState,
taskOverlayFactory: TaskOverlayFactory,
) {
+ deskId = desktopTask.deskId
// TODO(b/370495260): Minimized tasks should not be filtered with desktop exploded view
// support.
// Minimized tasks should not be shown in Overview.
@@ -308,17 +300,19 @@
val backgroundViewIndex = contentView.indexOfChild(backgroundView)
taskContainers =
tasks.map { task ->
+ val taskContentView = taskContentViewPool.view
+ contentView.addView(taskContentView, backgroundViewIndex + 1)
val snapshotView =
if (enableRefactorTaskThumbnail()) {
- taskThumbnailViewPool!!.view
+ taskContentView.findViewById<TaskThumbnailView>(R.id.snapshot)
} else {
- taskThumbnailViewDeprecatedPool!!.view
+ taskContentView.findViewById<TaskThumbnailViewDeprecated>(R.id.snapshot)
}
- contentView.addView(snapshotView, backgroundViewIndex + 1)
TaskContainer(
this,
task,
+ taskContentView,
snapshotView,
iconView,
TransformingTouchDelegate(iconView.asView()),
@@ -336,18 +330,24 @@
if (enableRefactorTaskThumbnail()) {
viewModel =
- DesktopTaskViewModel(organizeDesktopTasksUseCase = RecentsDependencies.get())
+ DesktopTaskViewModel(organizeDesktopTasksUseCase = RecentsDependencies.get(context))
}
}
override fun onRecycle() {
super.onRecycle()
+ deskId = DesktopVisibilityController.INACTIVE_DESK_ID
explodeProgress = 0.0f
viewModel = null
visibility = VISIBLE
taskContainers.forEach { removeAndRecycleThumbnailView(it) }
}
+ override fun setOrientationState(orientationState: RecentsOrientedState) {
+ super.setOrientationState(orientationState)
+ iconView.setIconOrientation(orientationState, isGridTask)
+ }
+
@SuppressLint("RtlHardcoded")
override fun updateTaskSize(lastComputedTaskSize: Rect, lastComputedGridTaskSize: Rect) {
super.updateTaskSize(lastComputedTaskSize, lastComputedGridTaskSize)
@@ -465,12 +465,8 @@
}
private fun removeAndRecycleThumbnailView(taskContainer: TaskContainer) {
- contentView.removeView(taskContainer.snapshotView)
- if (enableRefactorTaskThumbnail()) {
- taskThumbnailViewPool!!.recycle(taskContainer.thumbnailView)
- } else {
- taskThumbnailViewDeprecatedPool!!.recycle(taskContainer.thumbnailViewDeprecated)
- }
+ contentView.removeView(taskContainer.taskContentView)
+ taskContentViewPool.recycle(taskContainer.taskContentView)
}
private fun updateTaskPositions() {
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt b/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
index a8eee0a..71a4dde 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
@@ -24,7 +24,6 @@
import android.view.ViewStub
import com.android.internal.jank.Cuj
import com.android.launcher3.Flags.enableOverviewIconMenu
-import com.android.launcher3.Flags.enableRefactorTaskThumbnail
import com.android.launcher3.R
import com.android.launcher3.Utilities
import com.android.launcher3.util.RunnableList
@@ -78,8 +77,8 @@
val splitBoundsConfig = splitBoundsConfig ?: return
val inSplitSelection = getThisTaskCurrentlyInSplitSelection() != INVALID_TASK_ID
pagedOrientationHandler.measureGroupedTaskViewThumbnailBounds(
- leftTopTaskContainer.snapshotView,
- rightBottomTaskContainer.snapshotView,
+ leftTopTaskContainer.taskContentView,
+ rightBottomTaskContainer.taskContentView,
widthSize,
heightSize,
splitBoundsConfig,
@@ -95,12 +94,8 @@
override fun inflateViewStubs() {
super.inflateViewStubs()
- findViewById<ViewStub>(R.id.bottomright_snapshot)
- ?.apply {
- layoutResource =
- if (enableRefactorTaskThumbnail()) R.layout.task_thumbnail
- else R.layout.task_thumbnail_deprecated
- }
+ findViewById<ViewStub>(R.id.bottomright_task_content_view)
+ ?.apply { layoutResource = R.layout.task_content_view }
?.inflate()
findViewById<ViewStub>(R.id.bottomRight_icon)
?.apply {
@@ -128,6 +123,7 @@
listOf(
createTaskContainer(
primaryTask,
+ R.id.task_content_view,
R.id.snapshot,
R.id.icon,
R.id.show_windows,
@@ -137,7 +133,8 @@
),
createTaskContainer(
secondaryTask,
- R.id.bottomright_snapshot,
+ R.id.bottomright_task_content_view,
+ R.id.snapshot,
R.id.bottomRight_icon,
R.id.show_windows_right,
R.id.bottomRight_digital_wellbeing_toast,
@@ -240,8 +237,8 @@
leftTopTaskContainer.iconView.asView(),
rightBottomTaskContainer.iconView.asView(),
taskIconHeight,
- leftTopTaskContainer.snapshotView.measuredWidth,
- leftTopTaskContainer.snapshotView.measuredHeight,
+ leftTopTaskContainer.taskContentView.measuredWidth,
+ leftTopTaskContainer.taskContentView.measuredHeight,
measuredHeight,
measuredWidth,
isRtl,
diff --git a/quickstep/src/com/android/quickstep/views/IconAppChipView.kt b/quickstep/src/com/android/quickstep/views/IconAppChipView.kt
index 8d53552..7683a15 100644
--- a/quickstep/src/com/android/quickstep/views/IconAppChipView.kt
+++ b/quickstep/src/com/android/quickstep/views/IconAppChipView.kt
@@ -122,6 +122,9 @@
field = max(value, minMaxWidth)
}
+ var isExpanded: Boolean = false
+ private set
+
override fun onFinishInflate() {
super.onFinishInflate()
iconView = findViewById(R.id.icon_view)
@@ -356,6 +359,7 @@
ObjectAnimator.ofFloat(iconArrowView, SCALE_Y, -1f),
)
animator!!.setDuration(MENU_BACKGROUND_REVEAL_DURATION.toLong())
+ isExpanded = true
} else {
// Clip expanded text with reveal animation so it doesn't go beyond the edge of the menu
val expandedTextClipAnim =
@@ -390,6 +394,7 @@
ObjectAnimator.ofFloat(iconArrowView, SCALE_Y, 1f),
)
animator!!.setDuration(MENU_BACKGROUND_HIDE_DURATION.toLong())
+ isExpanded = false
}
if (!animated) animator!!.duration = 0
@@ -416,6 +421,17 @@
}
}
+ override fun focusSearch(direction: Int): View? {
+ if (mParent == null) return null
+ return when (direction) {
+ FOCUS_RIGHT,
+ FOCUS_DOWN -> mParent.focusSearch(this, View.FOCUS_FORWARD)
+ FOCUS_UP,
+ FOCUS_LEFT -> mParent.focusSearch(this, View.FOCUS_BACKWARD)
+ else -> super.focusSearch(direction)
+ }
+ }
+
override fun asView(): View = this
private companion object {
@@ -428,7 +444,6 @@
private const val INDEX_CONTENT_ALPHA = 0
private const val INDEX_COLOR_FILTER_ALPHA = 1
private const val INDEX_MODAL_ALPHA = 2
-
/** Used to hide the app chip for 90:10 flex split. */
private const val INDEX_MINIMUM_RATIO_ALPHA = 3
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index 0f1c294..74de2ac 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -19,6 +19,7 @@
import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY;
import static com.android.launcher3.LauncherState.CLEAR_ALL_BUTTON;
+import static com.android.launcher3.LauncherState.ADD_DESK_BUTTON;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
@@ -184,10 +185,8 @@
if (finalState.isRecentsViewVisible && finalState != OVERVIEW_MODAL_TASK) {
setTaskBorderEnabled(true);
}
-
if (isOverlayEnabled) {
- runActionOnRemoteHandles(remoteTargetHandle ->
- remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true));
+ mBlurUtils.setDrawLiveTileBelowRecents(true);
}
}
@@ -198,7 +197,10 @@
LauncherState state = getStateManager().getState();
boolean hasClearAllButton = (state.getVisibleElements(mContainer)
& CLEAR_ALL_BUTTON) != 0;
+ boolean hasAddDeskButton = (state.getVisibleElements(mContainer)
+ & ADD_DESK_BUTTON) != 0;
setDisallowScrollToClearAll(!hasClearAllButton);
+ setDisallowScrollToAddDesk(!hasAddDeskButton);
}
}
@@ -276,7 +278,7 @@
GestureState.GestureEndTarget endTarget = mCurrentGestureEndTarget;
if (endTarget == GestureState.GestureEndTarget.LAST_TASK
&& desktopVisibilityController.isInDesktopModeAndNotInOverview(
- mContainer.getDisplayId())) {
+ mContainer.getDisplayId())) {
// Recents gesture was cancelled and we are returning to the previous task.
// After super class has handled clean up, show desktop apps on top again
showDesktopApps = true;
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index 4a2be2a..5f08209 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -40,6 +40,8 @@
import com.android.launcher3.util.NavigationMode;
import com.android.quickstep.TaskOverlayFactory.OverlayUICallbacks;
import com.android.quickstep.util.LayoutUtils;
+import com.android.wm.shell.shared.TypefaceUtils;
+import com.android.wm.shell.shared.TypefaceUtils.FontFamily;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -157,6 +159,7 @@
// Currently, the only grouped task action is "save app pairs".
mActionButtons = findViewById(R.id.action_buttons);
mSaveAppPairButton = findViewById(R.id.action_save_app_pair);
+ TypefaceUtils.setTypeface(mSaveAppPairButton, FontFamily.GSF_LABEL_LARGE);
// Initialize a list to hold alphas for mActionButtons and any group action buttons.
mMultiValueAlphas[ACTIONS_ALPHAS] = new MultiValueAlpha(mActionButtons, NUM_ALPHAS);
mMultiValueAlphas[GROUP_ACTIONS_ALPHAS] =
diff --git a/quickstep/src/com/android/quickstep/views/RecentsDismissUtils.kt b/quickstep/src/com/android/quickstep/views/RecentsDismissUtils.kt
index 3430b39..d39b528 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsDismissUtils.kt
+++ b/quickstep/src/com/android/quickstep/views/RecentsDismissUtils.kt
@@ -21,6 +21,7 @@
import androidx.dynamicanimation.animation.FloatValueHolder
import androidx.dynamicanimation.animation.SpringAnimation
import androidx.dynamicanimation.animation.SpringForce
+import com.android.launcher3.Flags.enableGridOnlyOverview
import com.android.launcher3.R
import com.android.launcher3.Utilities.boundToRange
import com.android.launcher3.touch.SingleAxisSwipeDetector
@@ -31,6 +32,7 @@
import com.google.android.msdl.data.model.MSDLToken
import com.google.android.msdl.domain.InteractionProperties
import kotlin.math.abs
+import kotlin.math.roundToInt
/**
* Helper class for [RecentsView]. This util class contains refactored and extracted functions from
@@ -76,74 +78,104 @@
}
.addEndListener { _, _, _, _ ->
if (isDismissing) {
- recentsView.dismissTaskView(
- draggedTaskView,
- /* animateTaskView = */ false,
- /* removeTask = */ true,
- )
+ if (!recentsView.showAsGrid() || enableGridOnlyOverview()) {
+ runTaskGridReflowSpringAnimation(
+ draggedTaskView,
+ getDismissedTaskGapForReflow(draggedTaskView),
+ )
+ } else {
+ recentsView.dismissTaskView(
+ draggedTaskView,
+ /* animateTaskView = */ false,
+ /* removeTask = */ true,
+ )
+ }
} else {
recentsView.onDismissAnimationEnds()
}
onEndRunnable()
}
if (!isDismissing) {
- addNeighboringSpringAnimationsForDismissCancel(
+ addNeighborSettlingSpringAnimations(
draggedTaskView,
draggedTaskViewSpringAnimation,
+ driverProgressThreshold = 0f,
+ isSpringDirectionVertical = true,
)
}
return draggedTaskViewSpringAnimation
}
- private fun addNeighboringSpringAnimationsForDismissCancel(
+ private fun addNeighborSettlingSpringAnimations(
draggedTaskView: TaskView,
- draggedTaskViewSpringAnimation: SpringAnimation,
+ springAnimationDriver: SpringAnimation,
+ tasksToExclude: List<TaskView> = emptyList(),
+ driverProgressThreshold: Float,
+ isSpringDirectionVertical: Boolean,
) {
// Empty spring animation exists for conditional start, and to drive neighboring springs.
val neighborsToSettle =
SpringAnimation(FloatValueHolder()).setSpring(createExpressiveDismissSpringForce())
- var lastPosition = 0f
- var startSettling = false
- draggedTaskViewSpringAnimation.addUpdateListener { _, value, velocity ->
- // Start the settling animation the first time the dragged task passes the origin (from
- // negative displacement to positive displacement). We do not check for an exact value
- // to compare to, as the update listener does not necessarily hit every value (e.g. a
- // value of zero). Do not check again once it has started settling, as a spring can
- // bounce past the origin multiple times depending on the stiffness and damping ratio.
- if (startSettling) return@addUpdateListener
- if (lastPosition < 0 && value >= 0) {
- startSettling = true
- }
- lastPosition = value
- if (startSettling) {
- neighborsToSettle.setStartVelocity(velocity).animateToFinalPosition(0f)
- playDismissSettlingHaptic(velocity)
- }
- }
+ addThresholdSpringAnimationTrigger(
+ springAnimationDriver,
+ progressThreshold = driverProgressThreshold,
+ neighborsToSettle,
+ )
// Add tasks before dragged index, fanning out from the dragged task.
// The order they are added matters, as each spring drives the next.
var previousNeighbor = neighborsToSettle
- getTasksOffsetPairAdjacentToDraggedTask(draggedTaskView, towardsStart = true).forEach {
- (taskView, offset) ->
- previousNeighbor =
- createNeighboringTaskViewSpringAnimation(
- taskView,
- offset * ADDITIONAL_DISMISS_DAMPING_RATIO,
- previousNeighbor,
- )
- }
+ getTasksOffsetPairAdjacentToDraggedTask(draggedTaskView, towardsStart = true)
+ .filter { (taskView, _) -> !tasksToExclude.contains(taskView) }
+ .forEach { (taskView, offset) ->
+ previousNeighbor =
+ createNeighboringTaskViewSpringAnimation(
+ taskView,
+ offset * ADDITIONAL_DISMISS_DAMPING_RATIO,
+ previousNeighbor,
+ isSpringDirectionVertical,
+ )
+ }
// Add tasks after dragged index, fanning out from the dragged task.
// The order they are added matters, as each spring drives the next.
previousNeighbor = neighborsToSettle
- getTasksOffsetPairAdjacentToDraggedTask(draggedTaskView, towardsStart = false).forEach {
- (taskView, offset) ->
- previousNeighbor =
- createNeighboringTaskViewSpringAnimation(
- taskView,
- offset * ADDITIONAL_DISMISS_DAMPING_RATIO,
- previousNeighbor,
- )
+ getTasksOffsetPairAdjacentToDraggedTask(draggedTaskView, towardsStart = false)
+ .filter { (taskView, _) -> !tasksToExclude.contains(taskView) }
+ .forEach { (taskView, offset) ->
+ previousNeighbor =
+ createNeighboringTaskViewSpringAnimation(
+ taskView,
+ offset * ADDITIONAL_DISMISS_DAMPING_RATIO,
+ previousNeighbor,
+ isSpringDirectionVertical,
+ )
+ }
+ }
+
+ /** As spring passes threshold for the first time, run conditional spring with velocity. */
+ private fun addThresholdSpringAnimationTrigger(
+ springAnimationDriver: SpringAnimation,
+ progressThreshold: Float,
+ conditionalSpring: SpringAnimation,
+ ) {
+ var lastPosition = 0f
+ var startSettling = false
+ springAnimationDriver.addUpdateListener { _, value, velocity ->
+ // We do not compare to the threshold directly, as the update listener
+ // does not necessarily hit every value. Do not check again once it has started
+ // settling, as a spring can bounce past the end value multiple times.
+ if (startSettling) return@addUpdateListener
+ if (
+ lastPosition < progressThreshold && value >= progressThreshold ||
+ lastPosition > progressThreshold && value <= progressThreshold
+ ) {
+ startSettling = true
+ }
+ lastPosition = value
+ if (startSettling) {
+ conditionalSpring.setStartVelocity(velocity).animateToFinalPosition(0f)
+ playDismissSettlingHaptic(velocity)
+ }
}
}
@@ -160,8 +192,8 @@
if (recentsView.showAsGrid()) {
val taskGridNavHelper =
TaskGridNavHelper(
- recentsView.topRowIdArray,
- recentsView.bottomRowIdArray,
+ recentsView.mUtils.getTopRowIdArray(),
+ recentsView.mUtils.getBottomRowIdArray(),
recentsView.mUtils.getLargeTaskViewIds(),
hasAddDesktopButton = false,
)
@@ -199,21 +231,25 @@
taskView: TaskView,
dampingOffsetRatio: Float,
previousNeighborSpringAnimation: SpringAnimation,
+ springingDirectionVertical: Boolean,
): SpringAnimation {
+ val springProperty =
+ if (springingDirectionVertical) taskView.secondaryDismissTranslationProperty
+ else taskView.primaryDismissTranslationProperty
val neighboringTaskViewSpringAnimation =
- SpringAnimation(
- taskView,
- FloatPropertyCompat.createFloatPropertyCompat(
- taskView.secondaryDismissTranslationProperty
- ),
- )
+ SpringAnimation(taskView, FloatPropertyCompat.createFloatPropertyCompat(springProperty))
.setSpring(createExpressiveDismissSpringForce(dampingOffsetRatio))
// Update live tile on spring animation.
if (taskView.isRunningTask && recentsView.enableDrawingLiveTile) {
neighboringTaskViewSpringAnimation.addUpdateListener { _, _, _ ->
recentsView.runActionOnRemoteHandles { remoteTargetHandle ->
- remoteTargetHandle.taskViewSimulator.taskSecondaryTranslation.value =
- taskView.secondaryDismissTranslationProperty.get(taskView)
+ val taskTranslation =
+ if (springingDirectionVertical) {
+ remoteTargetHandle.taskViewSimulator.taskSecondaryTranslation
+ } else {
+ remoteTargetHandle.taskViewSimulator.taskPrimaryTranslation
+ }
+ taskTranslation.value = springProperty.get(taskView)
}
recentsView.redrawLiveTile()
}
@@ -237,6 +273,19 @@
)
}
+ private fun createExpressiveGridReflowSpringForce(
+ finalPosition: Float = Float.MAX_VALUE
+ ): SpringForce {
+ val resourceProvider = DynamicResource.provider(recentsView.mContainer)
+ return SpringForce(finalPosition)
+ .setDampingRatio(
+ resourceProvider.getFloat(R.dimen.expressive_dismiss_task_trans_x_damping_ratio)
+ )
+ .setStiffness(
+ resourceProvider.getFloat(R.dimen.expressive_dismiss_task_trans_x_stiffness)
+ )
+ }
+
/**
* Plays a haptic as the dragged task view settles back into its rest state.
*
@@ -249,7 +298,7 @@
.playToken(
MSDLToken.CANCEL,
InteractionProperties.DynamicVibrationScale(
- boundToRange(velocity / maxDismissSettlingVelocity, 0f, 1f),
+ boundToRange(abs(velocity) / maxDismissSettlingVelocity, 0f, 1f),
VibrationAttributes.Builder()
.setUsage(VibrationAttributes.USAGE_TOUCH)
.setFlags(VibrationAttributes.FLAG_PIPELINED_EFFECT)
@@ -286,6 +335,218 @@
.apply { animateToFinalPosition(RECENTS_SCALE_SPRING_MULTIPLIER * scale) }
}
+ /** Animates with springs the TaskViews beyond the dismissed task to fill the gap it left. */
+ private fun runTaskGridReflowSpringAnimation(
+ dismissedTaskView: TaskView,
+ dismissedTaskGap: Float,
+ ) {
+ // Empty spring animation exists for conditional start, and to drive neighboring springs.
+ val springAnimationDriver =
+ SpringAnimation(FloatValueHolder())
+ .setSpring(createExpressiveGridReflowSpringForce(finalPosition = dismissedTaskGap))
+ val towardsStart = if (recentsView.isRtl) dismissedTaskGap < 0 else dismissedTaskGap > 0
+
+ var tasksToReflow: List<TaskView>
+ // Build the chains of Spring Animations
+ when {
+ !recentsView.showAsGrid() -> {
+ tasksToReflow =
+ getTasksToReflow(
+ recentsView.mUtils.taskViews.toList(),
+ dismissedTaskView,
+ towardsStart,
+ )
+ buildDismissReflowSpringAnimationChain(
+ tasksToReflow,
+ dismissedTaskGap,
+ previousSpring = springAnimationDriver,
+ )
+ }
+ dismissedTaskView.isLargeTile -> {
+ tasksToReflow =
+ getTasksToReflow(
+ recentsView.mUtils.getLargeTaskViews(),
+ dismissedTaskView,
+ towardsStart,
+ )
+ val lastSpringAnimation =
+ buildDismissReflowSpringAnimationChain(
+ tasksToReflow,
+ dismissedTaskGap,
+ previousSpring = springAnimationDriver,
+ )
+ // Add all top and bottom grid tasks when animating towards the end of the grid.
+ if (!towardsStart) {
+ tasksToReflow += recentsView.mUtils.getTopRowTaskViews()
+ tasksToReflow += recentsView.mUtils.getBottomRowTaskViews()
+ buildDismissReflowSpringAnimationChain(
+ recentsView.mUtils.getTopRowTaskViews(),
+ dismissedTaskGap,
+ previousSpring = lastSpringAnimation,
+ )
+ buildDismissReflowSpringAnimationChain(
+ recentsView.mUtils.getBottomRowTaskViews(),
+ dismissedTaskGap,
+ previousSpring = lastSpringAnimation,
+ )
+ }
+ }
+ recentsView.isOnGridBottomRow(dismissedTaskView) -> {
+ tasksToReflow =
+ getTasksToReflow(
+ recentsView.mUtils.getBottomRowTaskViews(),
+ dismissedTaskView,
+ towardsStart,
+ )
+ buildDismissReflowSpringAnimationChain(
+ tasksToReflow,
+ dismissedTaskGap,
+ previousSpring = springAnimationDriver,
+ )
+ }
+ else -> {
+ tasksToReflow =
+ getTasksToReflow(
+ recentsView.mUtils.getTopRowTaskViews(),
+ dismissedTaskView,
+ towardsStart,
+ )
+ buildDismissReflowSpringAnimationChain(
+ tasksToReflow,
+ dismissedTaskGap,
+ previousSpring = springAnimationDriver,
+ )
+ }
+ }
+
+ if (tasksToReflow.isNotEmpty()) {
+ addNeighborSettlingSpringAnimations(
+ dismissedTaskView,
+ springAnimationDriver,
+ tasksToExclude = tasksToReflow,
+ driverProgressThreshold = dismissedTaskGap,
+ isSpringDirectionVertical = false,
+ )
+ }
+
+ // Start animations and remove the dismissed task at the end, dismiss immediately if no
+ // neighboring tasks exist.
+ val runGridEndAnimationAndRelayout = {
+ recentsView.expressiveDismissTaskView(dismissedTaskView)
+ }
+ springAnimationDriver?.apply {
+ addEndListener { _, _, _, _ -> runGridEndAnimationAndRelayout() }
+ animateToFinalPosition(dismissedTaskGap)
+ } ?: runGridEndAnimationAndRelayout()
+ }
+
+ private fun getDismissedTaskGapForReflow(dismissedTaskView: TaskView): Float {
+ val screenStart = recentsView.pagedOrientationHandler.getPrimaryScroll(recentsView)
+ val screenEnd =
+ screenStart + recentsView.pagedOrientationHandler.getMeasuredSize(recentsView)
+ val taskStart =
+ recentsView.pagedOrientationHandler.getChildStart(dismissedTaskView) +
+ dismissedTaskView.getOffsetAdjustment(recentsView.showAsGrid())
+ val taskSize =
+ recentsView.pagedOrientationHandler.getMeasuredSize(dismissedTaskView) *
+ dismissedTaskView.getSizeAdjustment(recentsView.showAsFullscreen())
+ val taskEnd = taskStart + taskSize
+
+ val isDismissedTaskBeyondEndOfScreen =
+ if (recentsView.isRtl) taskEnd > screenEnd else taskStart < screenStart
+ if (
+ dismissedTaskView.isLargeTile &&
+ isDismissedTaskBeyondEndOfScreen &&
+ recentsView.mUtils.getLargeTileCount() == 1
+ ) {
+ return with(recentsView) {
+ pagedOrientationHandler.getPrimaryScroll(this) -
+ getScrollForPage(indexOfChild(mUtils.getFirstNonDesktopTaskView()))
+ }
+ .toFloat()
+ }
+
+ // If current page is beyond last TaskView's index, use last TaskView to calculate offset.
+ val lastTaskViewIndex = recentsView.indexOfChild(recentsView.mUtils.getLastTaskView())
+ val currentPage = recentsView.currentPage.coerceAtMost(lastTaskViewIndex)
+ val dismissHorizontalFactor =
+ when {
+ dismissedTaskView.isGridTask -> 1f
+ currentPage == lastTaskViewIndex -> -1f
+ recentsView.indexOfChild(dismissedTaskView) < currentPage -> -1f
+ else -> 1f
+ } * (if (recentsView.isRtl) 1f else -1f)
+
+ return (recentsView.pagedOrientationHandler.getPrimarySize(dismissedTaskView) +
+ recentsView.pageSpacing) * dismissHorizontalFactor
+ }
+
+ private fun getTasksToReflow(
+ taskViews: List<TaskView>,
+ dismissedTaskView: TaskView,
+ towardsStart: Boolean,
+ ): List<TaskView> {
+ val dismissedTaskViewIndex = taskViews.indexOf(dismissedTaskView)
+ if (dismissedTaskViewIndex == -1) {
+ return emptyList()
+ }
+ return if (towardsStart) {
+ taskViews.take(dismissedTaskViewIndex).reversed()
+ } else {
+ taskViews.takeLast(taskViews.size - dismissedTaskViewIndex - 1)
+ }
+ }
+
+ private fun willTaskBeVisibleAfterDismiss(taskView: TaskView, taskTranslation: Int): Boolean {
+ val screenStart = recentsView.pagedOrientationHandler.getPrimaryScroll(recentsView)
+ val screenEnd =
+ screenStart + recentsView.pagedOrientationHandler.getMeasuredSize(recentsView)
+ return recentsView.isTaskViewWithinBounds(
+ taskView,
+ screenStart,
+ screenEnd,
+ /* taskViewTranslation = */ taskTranslation,
+ )
+ }
+
+ /** Builds a chain of spring animations for task reflow after dismissal */
+ private fun buildDismissReflowSpringAnimationChain(
+ taskViews: Iterable<TaskView>,
+ dismissedTaskGap: Float,
+ previousSpring: SpringAnimation,
+ ): SpringAnimation {
+ var lastTaskViewSpring = previousSpring
+ taskViews
+ .filter { taskView ->
+ willTaskBeVisibleAfterDismiss(taskView, dismissedTaskGap.roundToInt())
+ }
+ .forEach { taskView ->
+ val taskViewSpringAnimation =
+ SpringAnimation(
+ taskView,
+ FloatPropertyCompat.createFloatPropertyCompat(
+ taskView.primaryDismissTranslationProperty
+ ),
+ )
+ .setSpring(createExpressiveGridReflowSpringForce(dismissedTaskGap))
+ // Update live tile on spring animation.
+ if (taskView.isRunningTask && recentsView.enableDrawingLiveTile) {
+ taskViewSpringAnimation.addUpdateListener { _, _, _ ->
+ recentsView.runActionOnRemoteHandles { remoteTargetHandle ->
+ remoteTargetHandle.taskViewSimulator.taskPrimaryTranslation.value =
+ taskView.primaryDismissTranslationProperty.get(taskView)
+ }
+ recentsView.redrawLiveTile()
+ }
+ }
+ lastTaskViewSpring.addUpdateListener { _, value, _ ->
+ taskViewSpringAnimation.animateToFinalPosition(value)
+ }
+ lastTaskViewSpring = taskViewSpringAnimation
+ }
+ return lastTaskViewSpring
+ }
+
private companion object {
// The additional damping to apply to tasks further from the dismissed task.
private const val ADDITIONAL_DISMISS_DAMPING_RATIO = 0.15f
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 44bf82c..cbf4cab 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -17,6 +17,8 @@
package com.android.quickstep.views;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.os.Trace.traceBegin;
+import static android.os.Trace.traceEnd;
import static android.view.Surface.ROTATION_0;
import static android.view.View.MeasureSpec.EXACTLY;
import static android.view.View.MeasureSpec.makeMeasureSpec;
@@ -38,9 +40,9 @@
import static com.android.launcher3.Flags.enableAdditionalHomeAnimations;
import static com.android.launcher3.Flags.enableDesktopExplodedView;
import static com.android.launcher3.Flags.enableDesktopTaskAlphaAnimation;
-import static com.android.launcher3.Flags.enableExpressiveDismissTaskMotion;
import static com.android.launcher3.Flags.enableGridOnlyOverview;
import static com.android.launcher3.Flags.enableLargeDesktopWindowingTile;
+import static com.android.launcher3.Flags.enableOverviewBackgroundWallpaperBlur;
import static com.android.launcher3.Flags.enableRefactorTaskThumbnail;
import static com.android.launcher3.Flags.enableSeparateExternalDisplayTasks;
import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS;
@@ -57,6 +59,7 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_CLEAR_ALL;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_DISMISS_SWIPE_UP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_SWIPE_DOWN;
+import static com.android.launcher3.statehandlers.DesktopVisibilityController.INACTIVE_DESK_ID;
import static com.android.launcher3.testing.shared.TestProtocol.DISMISS_ANIMATION_ENDS_MESSAGE;
import static com.android.launcher3.touch.PagedOrientationHandler.CANVAS_TRANSLATE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
@@ -65,6 +68,7 @@
import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
import static com.android.quickstep.BaseContainerInterface.getTaskDimension;
import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
+import static com.android.quickstep.util.DesksUtils.areMultiDesksFlagsEnabled;
import static com.android.quickstep.util.LogUtils.splitFailureMessage;
import static com.android.quickstep.views.ClearAllButton.DISMISS_ALPHA;
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_ACTIONS_IN_MENU;
@@ -103,6 +107,7 @@
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.SystemClock;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.VibrationEffect;
import android.text.Layout;
@@ -144,7 +149,6 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Flags;
import com.android.launcher3.Insettable;
-import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.PagedView;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -202,6 +206,7 @@
import com.android.quickstep.TaskViewUtils;
import com.android.quickstep.TopTaskTracker;
import com.android.quickstep.ViewUtils;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
import com.android.quickstep.recents.data.RecentTasksRepository;
import com.android.quickstep.recents.data.RecentsDeviceProfileRepository;
@@ -255,11 +260,11 @@
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;
import java.util.stream.Collectors;
-
/**
* A list of recent tasks.
*
@@ -574,8 +579,6 @@
// Keeps track of the previously known visible tasks for purposes of loading/unloading task data
private final SparseBooleanArray mHasVisibleTaskData = new SparseBooleanArray();
- private final InvariantDeviceProfile mIdp;
-
/**
* Getting views should be done via {@link #getTaskViewFromPool(int)}
*/
@@ -586,6 +589,8 @@
private final TaskOverlayFactory mTaskOverlayFactory;
protected boolean mDisallowScrollToClearAll;
+ // True if it is not allowed to scroll to [AddDesktopButton].
+ protected boolean mDisallowScrollToAddDesk;
private boolean mOverlayEnabled;
protected boolean mFreezeViewVisibility;
private boolean mOverviewGridEnabled;
@@ -605,7 +610,7 @@
private float mTaskThumbnailSplashAlpha = 0;
private boolean mBorderEnabled = false;
private boolean mShowAsGridLastOnLayout = false;
- private final IntSet mTopRowIdSet = new IntSet();
+ protected final IntSet mTopRowIdSet = new IntSet();
private int mClearAllShortTotalWidthTranslation = 0;
// The GestureEndTarget that is still in progress.
@@ -866,6 +871,8 @@
private int mTaskViewCount = 0;
+ protected final BlurUtils mBlurUtils = new BlurUtils(this);
+
@Nullable
public TaskView getFirstTaskView() {
return mUtils.getFirstTaskView();
@@ -885,22 +892,23 @@
// Start Recents Dependency graph
if (enableRefactorTaskThumbnail()) {
- RecentsDependencies recentsDependencies = RecentsDependencies.Companion.initialize(
- this);
+ RecentsDependencies recentsDependencies = RecentsDependencies.Companion.maybeInitialize(
+ context);
+ String scopeId = recentsDependencies.createRecentsViewScope(context);
mRecentsViewModel = new RecentsViewModel(
- recentsDependencies.inject(RecentTasksRepository.class),
- recentsDependencies.inject(RecentsViewData.class)
+ recentsDependencies.inject(RecentTasksRepository.class, scopeId),
+ recentsDependencies.inject(RecentsViewData.class, scopeId)
);
mHelper = new RecentsViewModelHelper(
mRecentsViewModel,
- recentsDependencies.inject(CoroutineScope.class),
- recentsDependencies.inject(DispatcherProvider.class)
+ recentsDependencies.inject(CoroutineScope.class, scopeId),
+ recentsDependencies.inject(DispatcherProvider.class, scopeId)
);
- recentsDependencies.provide(RecentsRotationStateRepository.class,
+ recentsDependencies.provide(RecentsRotationStateRepository.class, scopeId,
() -> new RecentsRotationStateRepositoryImpl(mOrientationState));
- recentsDependencies.provide(RecentsDeviceProfileRepository.class,
+ recentsDependencies.provide(RecentsDeviceProfileRepository.class, scopeId,
() -> new RecentsDeviceProfileRepositoryImpl(mContainer));
} else {
mRecentsViewModel = null;
@@ -912,7 +920,6 @@
mFastFlingVelocity = getResources()
.getDimensionPixelSize(R.dimen.recents_fast_fling_velocity);
mModel = RecentsModel.INSTANCE.get(context);
- mIdp = InvariantDeviceProfile.INSTANCE.get(context);
mClearAllButton = (ClearAllButton) LayoutInflater.from(context)
.inflate(R.layout.overview_clear_all_button, this, false);
@@ -1288,12 +1295,13 @@
Log.e(TAG, "Ongoing initializations could not be killed", e);
}
mHelper.onDestroy();
- RecentsDependencies.destroy();
+ RecentsDependencies.destroy(getContext());
}
}
@Override
public void onViewRemoved(View child) {
+ traceBegin(Trace.TRACE_TAG_APP, "RecentsView.onViewRemoved");
super.onViewRemoved(child);
// Clear the task data for the removed child if it was visible unless:
// - It's the initial taskview for entering split screen, we only pretend to dismiss the
@@ -1305,6 +1313,7 @@
clearAndRecycleTaskView((TaskView) child);
}
}
+ traceEnd(Trace.TRACE_TAG_APP);
}
private void clearAndRecycleTaskView(TaskView taskView) {
@@ -1323,6 +1332,7 @@
@Override
public void onViewAdded(View child) {
+ traceBegin(Trace.TRACE_TAG_APP, "RecentsView.onViewAdded");
super.onViewAdded(child);
if (child instanceof TaskView) {
mTaskViewCount++;
@@ -1333,6 +1343,7 @@
child.setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_LTR : View.LAYOUT_DIRECTION_RTL);
mActionsView.updateHiddenFlags(HIDDEN_NO_TASKS, false);
updateEmptyMessage();
+ traceEnd(Trace.TRACE_TAG_APP);
}
@Override
@@ -1511,7 +1522,7 @@
@Nullable
private TaskView getLastGridTaskView() {
- return getLastGridTaskView(getTopRowIdArray(), getBottomRowIdArray());
+ return getLastGridTaskView(mUtils.getTopRowIdArray(), mUtils.getBottomRowIdArray());
}
@Nullable
@@ -1557,7 +1568,7 @@
* @param taskViewTranslation taskView is considered within bounds if either translated or
* original position of taskView is within screen bounds.
*/
- private boolean isTaskViewWithinBounds(TaskView taskView, int screenStart, int screenEnd,
+ protected boolean isTaskViewWithinBounds(TaskView taskView, int screenStart, int screenEnd,
int taskViewTranslation) {
int taskStart = getPagedOrientationHandler().getChildStart(taskView)
+ (int) taskView.getOffsetAdjustment(showAsGrid());
@@ -1592,7 +1603,10 @@
== getPagedOrientationHandler().getPrimaryScroll(this);
}
- private boolean isFocusedTaskInExpectedScrollPosition() {
+ /**
+ * Returns true if the focused TaskView is in expected scroll position.
+ */
+ public boolean isFocusedTaskInExpectedScrollPosition() {
TaskView focusedTask = getFocusedTaskView();
return focusedTask != null && isTaskInExpectedScrollPosition(focusedTask);
}
@@ -1926,12 +1940,19 @@
return;
}
- int[] currentTaskIds;
+ // Start here to avoid early returns and empty cases which have special logic
+ traceBegin(Trace.TRACE_TAG_APP, "RecentsView.applyLoadPlan");
+
TaskView currentTaskView = getTaskViewAt(mCurrentPage);
- if (currentTaskView != null) {
+ int[] currentTaskIds = null;
+ // Track the current DesktopTaskView through [deskId] as a desk can be empty without any
+ // tasks.
+ int currentTaskViewDeskId = INACTIVE_DESK_ID;
+ if (areMultiDesksFlagsEnabled()
+ && currentTaskView instanceof DesktopTaskView desktopTaskView) {
+ currentTaskViewDeskId = desktopTaskView.getDeskId();
+ } else if (currentTaskView != null) {
currentTaskIds = currentTaskView.getTaskIds();
- } else {
- currentTaskIds = new int[0];
}
// Unload existing visible task data
@@ -1943,9 +1964,19 @@
// Save running task ID if it exists before rebinding all taskViews, otherwise the task from
// the runningTaskView currently bound could get assigned to another TaskView
- int[] runningTaskIds = getTaskIdsForTaskViewId(mRunningTaskViewId);
- int[] focusedTaskIds = getTaskIdsForTaskViewId(mFocusedTaskViewId);
+ TaskView runningTaskView = getRunningTaskView();
+ int[] runningTaskIds = null;
+ // Track the running TaskView through [deskId] as a desk can be empty without any tasks.
+ int runningTaskViewDeskId = INACTIVE_DESK_ID;
+ if (areMultiDesksFlagsEnabled()
+ && runningTaskView instanceof DesktopTaskView desktopTaskView) {
+ runningTaskViewDeskId = desktopTaskView.getDeskId();
+ } else if (runningTaskView != null) {
+ runningTaskIds = runningTaskView.getTaskIds();
+ }
+
+ int[] focusedTaskIds = getTaskIdsForTaskViewId(mFocusedTaskViewId);
// Reset the focused task to avoiding initializing TaskViews layout as focused task during
// binding. The focused task view will be updated after all the TaskViews are bound.
setFocusedTaskViewId(INVALID_TASK_ID);
@@ -1957,8 +1988,9 @@
// TaskIds will no longer be valid after remove and re-add, clearing mTopRowIdSet.
mAnyTaskHasBeenDismissed = false;
mTopRowIdSet.clear();
+ traceBegin(Trace.TRACE_TAG_APP, "RecentsView.applyLoadPlan.removeAllViews");
removeAllViews();
-
+ traceEnd(Trace.TRACE_TAG_APP);
// If we are entering Overview as a result of initiating a split from somewhere else
// (e.g. split from Home), we need to make sure the staged app is not drawn as a thumbnail.
int stagedTaskIdToBeRemoved;
@@ -1985,6 +2017,7 @@
// Add `mAddDesktopButton` as the first child.
addView(mAddDesktopButton);
}
+ traceBegin(Trace.TRACE_TAG_APP, "RecentsView.applyLoadPlan.forLoop");
// 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.
@@ -2004,8 +2037,11 @@
// If we need to remove half of a pair of tasks, force a TaskView with Type.SINGLE
// to be a temporary container for the remaining task.
+ traceBegin(Trace.TRACE_TAG_APP, "RecentsView.applyLoadPlan.forLoop.createTaskView");
TaskView taskView = getTaskViewFromPool(
containsStagedTask ? TaskViewType.SINGLE : groupTask.taskViewType);
+ traceEnd(Trace.TRACE_TAG_APP);
+ traceBegin(Trace.TRACE_TAG_APP, "RecentsView.applyLoadPlan.forLoop.bind");
if (taskView instanceof GroupedTaskView groupedTaskView) {
var splitTask = (SplitTask) groupTask;
groupedTaskView.bind(splitTask.getTopLeftTask(),
@@ -2023,13 +2059,18 @@
taskView.bind(((SingleTask) groupTask).getTask(), mOrientationState,
mTaskOverlayFactory);
}
+ traceEnd(Trace.TRACE_TAG_APP);
+ traceBegin(Trace.TRACE_TAG_APP, "RecentsView.applyLoadPlan.forLoop.addTaskView");
addView(taskView);
+ traceEnd(Trace.TRACE_TAG_APP);
// enables instance filtering if the feature flag for it is on
if (FeatureFlags.ENABLE_MULTI_INSTANCE.get()) {
taskView.setUpShowAllInstancesListener();
}
}
+ // For loop end trace
+ traceEnd(Trace.TRACE_TAG_APP);
addView(mClearAllButton);
@@ -2049,27 +2090,29 @@
setFocusedTaskViewId(
newFocusedTaskView != null ? newFocusedTaskView.getTaskViewId() : INVALID_TASK_ID);
+ traceBegin(Trace.TRACE_TAG_APP, "RecentsView.applyLoadPlan.layouts");
updateTaskSize();
updateChildTaskOrientations();
+ traceEnd(Trace.TRACE_TAG_APP);
- TaskView newRunningTaskView = null;
- if (hasAllValidTaskIds(runningTaskIds)) {
+ TaskView newRunningTaskView = mUtils.getDesktopTaskViewForDeskId(runningTaskViewDeskId);
+ if (newRunningTaskView == null) {
// Update mRunningTaskViewId to be the new TaskView that was assigned by binding
// the full list of tasks to taskViews
newRunningTaskView = getTaskViewByTaskIds(runningTaskIds);
- if (newRunningTaskView != null) {
- setRunningTaskViewId(newRunningTaskView.getTaskViewId());
+ }
+ if (newRunningTaskView != null) {
+ setRunningTaskViewId(newRunningTaskView.getTaskViewId());
+ } else {
+ if (mActiveGestureRunningTasks != null) {
+ // This will update mRunningTaskViewId and create a stub view if necessary.
+ // We try to avoid this because it can cause a scroll jump, but it is needed
+ // for cases where the running task isn't included in this load plan (e.g. if
+ // the current running task is excludedFromRecents.)
+ showCurrentTask(mActiveGestureRunningTasks, "applyLoadPlan");
+ newRunningTaskView = getRunningTaskView();
} else {
- if (mActiveGestureRunningTasks != null) {
- // This will update mRunningTaskViewId and create a stub view if necessary.
- // We try to avoid this because it can cause a scroll jump, but it is needed
- // for cases where the running task isn't included in this load plan (e.g. if
- // the current running task is excludedFromRecents.)
- showCurrentTask(mActiveGestureRunningTasks, "applyLoadPlan");
- newRunningTaskView = getRunningTaskView();
- } else {
- setRunningTaskViewId(INVALID_TASK_ID);
- }
+ setRunningTaskViewId(INVALID_TASK_ID);
}
}
@@ -2077,11 +2120,12 @@
if (mNextPage != INVALID_PAGE) {
// Restore mCurrentPage but don't call setCurrentPage() as that clobbers the scroll.
mCurrentPage = previousCurrentPage;
- if (hasAllValidTaskIds(currentTaskIds)) {
+ currentTaskView = mUtils.getDesktopTaskViewForDeskId(currentTaskViewDeskId);
+ if (currentTaskView == null) {
currentTaskView = getTaskViewByTaskIds(currentTaskIds);
- if (currentTaskView != null) {
- targetPage = indexOfChild(currentTaskView);
- }
+ }
+ if (currentTaskView != null) {
+ targetPage = indexOfChild(currentTaskView);
}
} else if (previousFocusedPage != INVALID_PAGE) {
targetPage = previousFocusedPage;
@@ -2104,6 +2148,7 @@
});
}
+ traceBegin(Trace.TRACE_TAG_APP, "RecentsView.applyLoadPlan.cleanupStates");
if (mIgnoreResetTaskId != INVALID_TASK_ID &&
getTaskViewByTaskId(mIgnoreResetTaskId) != ignoreResetTaskView) {
// If the taskView mapping is changing, do not preserve the visuals. Since we are
@@ -2118,6 +2163,10 @@
if (isPageScrollsInitialized()) {
onPageScrollsInitialized();
}
+ traceEnd(Trace.TRACE_TAG_APP);
+
+ // applyLoadPlan end trace
+ traceEnd(Trace.TRACE_TAG_APP);
}
private boolean isModal() {
@@ -2507,6 +2556,11 @@
int minDistanceFromScreenStart = Integer.MAX_VALUE;
int minDistanceFromScreenStartIndex = INVALID_PAGE;
for (int i = 0; i < getChildCount(); ++i) {
+ // Do not set the destination page to the AddDesktopButton, which has the same page
+ // scrolls as the first [TaskView] and shouldn't be scrolled to.
+ if (getChildAt(i) instanceof AddDesktopButton) {
+ continue;
+ }
int distanceFromScreenStart = Math.abs(mPageScrolls[i] - scaledScroll);
if (distanceFromScreenStart < minDistanceFromScreenStart) {
minDistanceFromScreenStart = distanceFromScreenStart;
@@ -2698,9 +2752,7 @@
}
setEnableDrawingLiveTile(false);
}
- runActionOnRemoteHandles(remoteTargetHandle ->
- remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(false));
-
+ mBlurUtils.setDrawLiveTileBelowRecents(false);
// These are relatively expensive and don't need to be done this frame (RecentsView isn't
// visible anyway), so defer by a frame to get off the critical path, e.g. app to home.
post(this::onReset);
@@ -2823,9 +2875,13 @@
/**
* Called when a gesture from an app is starting.
*/
+ // TODO: b/401582344 - Implement a way to exclude the `DesktopWallpaperActivity` from being
+ // considered in Overview.
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)) {
reapplyActiveRotation();
@@ -2905,10 +2961,10 @@
if (!shouldRotateMenuForFakeRotation) {
return;
}
- TaskMenuView tv = (TaskMenuView) getTopOpenViewWithType(mContainer, TYPE_TASK_MENU);
- if (tv != null) {
+ AbstractFloatingView floatingView = getTopOpenViewWithType(mContainer, TYPE_TASK_MENU);
+ if (floatingView instanceof TaskMenuView taskMenuView) {
// Rotation is supported on phone (details at b/254198019#comment4)
- tv.onRotationChanged();
+ taskMenuView.onRotationChanged();
}
}
@@ -2925,12 +2981,15 @@
updateGridProperties();
}
+ BaseState<?> endState = mSizeStrategy.stateFromGestureEndTarget(endTarget);
+ // Starting the desk exploded animation when the gesture from an app is released.
if (enableDesktopExplodedView()) {
if (animatorSet == null) {
- mUtils.setDeskExplodeProgress(1);
+ mUtils.setDeskExplodeProgress(endState.showExplodedDesktopView() ? 1f : 0f);
} else {
animatorSet.play(
- ObjectAnimator.ofFloat(this, DESK_EXPLODE_PROGRESS, 1));
+ ObjectAnimator.ofFloat(this, DESK_EXPLODE_PROGRESS,
+ endState.showExplodedDesktopView() ? 1f : 0f));
}
for (TaskView taskView : getTaskViews()) {
@@ -2940,7 +2999,6 @@
}
}
- BaseState<?> endState = mSizeStrategy.stateFromGestureEndTarget(endTarget);
if (endState.displayOverviewTasksAsGrid(mContainer.getDeviceProfile())) {
TaskView runningTaskView = getRunningTaskView();
float runningTaskGridTranslationX = 0;
@@ -3013,9 +3071,24 @@
startIconFadeInOnGestureComplete();
animateActionsViewIn();
- for (TaskView taskView : getTaskViews()) {
- if (taskView instanceof DesktopTaskView desktopTaskView) {
- desktopTaskView.setRemoteTargetHandles(mRemoteTargetHandles);
+ if (mEnableDrawingLiveTile) {
+ if (enableDesktopExplodedView()) {
+ for (TaskView taskView : getTaskViews()) {
+ if (taskView instanceof DesktopTaskView desktopTaskView) {
+ desktopTaskView.setRemoteTargetHandles(mRemoteTargetHandles);
+ }
+ }
+ }
+ TaskView runningTaskView = getRunningTaskView();
+ if (showAsGrid() && enableGridOnlyOverview() && runningTaskView != null) {
+ runActionOnRemoteHandles(remoteTargetHandle -> {
+ TaskViewSimulator taskViewSimulator = remoteTargetHandle.getTaskViewSimulator();
+ // After settling in Overview, recentsScroll will be used to adjust horizontally
+ // location and taskGridTranslationX doesn't needs to be applied.
+ taskViewSimulator.taskGridTranslationX.value = 0;
+ taskViewSimulator.taskGridTranslationY.value =
+ runningTaskView.getGridTranslationY();
+ });
}
}
@@ -3042,6 +3115,21 @@
}
/**
+ * Creates a `DesktopTaskView` for the currently active desk on this display, which contains the
+ * gievn `runningTasks`.
+ */
+ private DesktopTaskView createDesktopTaskViewForActiveDesk(Task[] runningTasks) {
+ final int activeDeskId = mUtils.getActiveDeskIdOnThisDisplay();
+ final var desktopTaskView = (DesktopTaskView) getTaskViewFromPool(TaskViewType.DESKTOP);
+
+ // TODO: b/401582344 - Implement a way to exclude the `DesktopWallpaperActivity`.
+ desktopTaskView.bind(
+ new DesktopTask(activeDeskId, Arrays.asList(runningTasks)),
+ mOrientationState, mTaskOverlayFactory);
+ return desktopTaskView;
+ }
+
+ /**
* Creates a task view (if necessary) to represent the task with the {@param runningTaskId}.
*
* All subsequent calls to reload will keep the task as the first item until {@link #reset()}
@@ -3055,20 +3143,14 @@
}
int runningTaskViewId = -1;
- boolean needGroupTaskView = runningTasks.length > 1;
- boolean needDesktopTask = hasDesktopTask(runningTasks);
if (shouldAddStubTaskView(runningTasks)) {
boolean wasEmpty = getChildCount() == 0;
// Add an empty view for now until the task plan is loaded and applied
final TaskView taskView;
+ final boolean needGroupTaskView = runningTasks.length > 1;
+ final boolean needDesktopTask = hasDesktopTask(runningTasks);
if (needDesktopTask) {
- final int activeDeskId =
- DesktopVisibilityController.INSTANCE.get(mContext).getActiveDeskId(
- mContainer.getDisplay().getDisplayId());
- taskView = getTaskViewFromPool(TaskViewType.DESKTOP);
- ((DesktopTaskView) taskView).bind(
- new DesktopTask(activeDeskId, Arrays.asList(runningTasks)),
- mOrientationState, mTaskOverlayFactory);
+ taskView = createDesktopTaskViewForActiveDesk(runningTasks);
} else if (needGroupTaskView) {
taskView = getTaskViewFromPool(TaskViewType.GROUPED);
// When we create a placeholder task view mSplitBoundsConfig will be null, but with
@@ -3094,8 +3176,11 @@
measure(makeMeasureSpec(getMeasuredWidth(), EXACTLY),
makeMeasureSpec(getMeasuredHeight(), EXACTLY));
layout(getLeft(), getTop(), getRight(), getBottom());
- } else if (getTaskViewByTaskId(runningTasks[0].key.id) != null) {
- runningTaskViewId = getTaskViewByTaskId(runningTasks[0].key.id).getTaskViewId();
+ } else {
+ var runningTaskView = getTaskViewByTaskId(runningTasks[0].key.id);
+ if (runningTaskView != null) {
+ runningTaskViewId = runningTaskView.getTaskViewId();
+ }
}
boolean runningTaskTileHidden = mRunningTaskTileHidden;
@@ -3134,6 +3219,10 @@
return true;
}
}
+
+ // A running empty desk will have a single running app for the `DesktopWallpaperActivity`.
+ // TODO: b/401582344 - Implement a way to exclude the `DesktopWallpaperActivity`.
+
return false;
}
@@ -3530,19 +3619,6 @@
mAddDesktopButton.setGridTranslationX(translationX);
}
- final TaskView runningTask = getRunningTaskView();
- if (showAsGrid() && enableGridOnlyOverview() && runningTask != null) {
- runActionOnRemoteHandles(
- remoteTargetHandle -> {
- remoteTargetHandle.getTaskViewSimulator().taskGridTranslationX.value =
- runningTask.getGridTranslationX()
- - runningTask.getNonGridTranslationX();
- remoteTargetHandle.getTaskViewSimulator().taskGridTranslationY.value =
- runningTask.getGridTranslationY();
- }
- );
- }
-
mClearAllButton.setGridTranslationPrimary(
clearAllTotalTranslationX - snappedTaskGridTranslationX);
mClearAllButton.setGridScrollOffset(
@@ -3551,7 +3627,7 @@
setGridProgress(mGridProgress);
}
- private boolean isSameGridRow(TaskView taskView1, TaskView taskView2) {
+ protected boolean isSameGridRow(TaskView taskView1, TaskView taskView2) {
if (taskView1 == null || taskView2 == null) {
return false;
}
@@ -3755,11 +3831,13 @@
* @param duration duration of the animation
* @param dismissingForSplitSelection task dismiss animation is used for entering split
* selection state from app icon
+ * @param isExpressiveDismiss runs expressive animations controlled via
+ * {@link RecentsDismissUtils}
*/
public void createTaskDismissAnimation(PendingAnimation anim,
@Nullable TaskView dismissedTaskView,
boolean animateTaskView, boolean shouldRemoveTask, long duration,
- boolean dismissingForSplitSelection) {
+ boolean dismissingForSplitSelection, boolean isExpressiveDismiss) {
if (mPendingAnimation != null) {
mPendingAnimation.createPlaybackController().dispatchOnCancel().dispatchOnEnd();
}
@@ -3877,7 +3955,7 @@
}
}
if (lastGridTaskView != null && (lastGridTaskView.isVisibleToUser() || (
- enableExpressiveDismissTaskMotion() && lastGridTaskView == dismissedTaskView))) {
+ isExpressiveDismiss && lastGridTaskView == dismissedTaskView))) {
// After dismissal, animate translation of the remaining tasks to fill any gap left
// between the end of the grid and the clear all button. Only animate if the clear
// all button is visible or would become visible after dismissal.
@@ -4017,12 +4095,17 @@
lastTaskViewIndex);
int scrollDiff = newScroll[i] - oldScroll[i] + offset;
if (scrollDiff != 0) {
- translateTaskWhenDismissed(
- child,
- Math.abs(i - dismissedIndex),
- scrollDiff,
- anim,
- splitTimings);
+ if (!isExpressiveDismiss) {
+ translateTaskWhenDismissed(
+ child,
+ Math.abs(i - dismissedIndex),
+ scrollDiff,
+ anim,
+ splitTimings);
+ }
+ if (child instanceof TaskView taskView) {
+ mTaskViewsDismissPrimaryTranslations.put(taskView, scrollDiffPerPage);
+ }
needsCurveUpdates = true;
}
} else if (child instanceof TaskView taskView) {
@@ -4107,13 +4190,16 @@
: finalTranslation + (mIsRtl ? -mLastComputedTaskSize.right
: mLastComputedTaskSize.right);
}
- Animator dismissAnimator = ObjectAnimator.ofFloat(taskView,
- taskView.getPrimaryDismissTranslationProperty(),
- startTranslation, finalTranslation);
- dismissAnimator.setInterpolator(
- clampToProgress(dismissInterpolator, animationStartProgress,
- animationEndProgress));
- anim.add(dismissAnimator);
+ // Expressive dismiss will animate the translations of taskViews itself.
+ if (!isExpressiveDismiss) {
+ Animator dismissAnimator = ObjectAnimator.ofFloat(taskView,
+ taskView.getPrimaryDismissTranslationProperty(),
+ startTranslation, finalTranslation);
+ dismissAnimator.setInterpolator(
+ clampToProgress(dismissInterpolator, animationStartProgress,
+ animationEndProgress));
+ anim.add(dismissAnimator);
+ }
mTaskViewsDismissPrimaryTranslations.put(taskView, (int) finalTranslation);
distanceFromDismissedTask++;
}
@@ -4213,8 +4299,8 @@
boolean isSnappedTaskInTopRow = mTopRowIdSet.contains(
snappedTaskViewId);
IntArray taskViewIdArray =
- isSnappedTaskInTopRow ? getTopRowIdArray()
- : getBottomRowIdArray();
+ isSnappedTaskInTopRow ? mUtils.getTopRowIdArray()
+ : mUtils.getBottomRowIdArray();
int snappedIndex = taskViewIdArray.indexOf(snappedTaskViewId);
taskViewIdArray.removeValue(dismissedTaskViewId);
if (finalNextFocusedTaskView != null) {
@@ -4229,8 +4315,8 @@
// dismissed row,
// snap to the same column in the other grid row
IntArray inverseRowTaskViewIdArray =
- isSnappedTaskInTopRow ? getBottomRowIdArray()
- : getTopRowIdArray();
+ isSnappedTaskInTopRow ? mUtils.getBottomRowIdArray()
+ : mUtils.getTopRowIdArray();
if (snappedIndex < inverseRowTaskViewIdArray.size()) {
taskViewIdToSnapTo = inverseRowTaskViewIdArray.get(
snappedIndex);
@@ -4312,8 +4398,8 @@
}
}
- IntArray topRowIdArray = getTopRowIdArray();
- IntArray bottomRowIdArray = getBottomRowIdArray();
+ IntArray topRowIdArray = mUtils.getTopRowIdArray();
+ IntArray bottomRowIdArray = mUtils.getBottomRowIdArray();
if (finalSnapToLastTask) {
// If snapping to last task, find the last task after dismissal.
pageToSnapTo = indexOfChild(
@@ -4436,10 +4522,6 @@
animationEndProgress
)
);
-
- if (view instanceof TaskView) {
- mTaskViewsDismissPrimaryTranslations.put((TaskView) view, scrollDiffPerPage);
- }
if (mEnableDrawingLiveTile && view instanceof TaskView
&& ((TaskView) view).isRunningTask()) {
pendingAnimation.addOnFrameCallback(() -> {
@@ -4484,41 +4566,6 @@
}
/**
- * Returns all the tasks in the top row, without the focused task
- */
- IntArray getTopRowIdArray() {
- if (mTopRowIdSet.isEmpty()) {
- return new IntArray(0);
- }
- IntArray topArray = new IntArray(mTopRowIdSet.size());
- for (TaskView taskView : getTaskViews()) {
- int taskViewId = taskView.getTaskViewId();
- if (mTopRowIdSet.contains(taskViewId)) {
- topArray.add(taskViewId);
- }
- }
- return topArray;
- }
-
- /**
- * Returns all the tasks in the bottom row, without the focused task
- */
- IntArray getBottomRowIdArray() {
- int bottomRowIdArraySize = getBottomRowTaskCountForTablet();
- if (bottomRowIdArraySize <= 0) {
- return new IntArray(0);
- }
- IntArray bottomArray = new IntArray(bottomRowIdArraySize);
- for (TaskView taskView : getTaskViews()) {
- int taskViewId = taskView.getTaskViewId();
- if (!mTopRowIdSet.contains(taskViewId) && !taskView.isLargeTile()) {
- bottomArray.add(taskViewId);
- }
- }
- return bottomArray;
- }
-
- /**
* Iterate the grid by columns instead of by TaskView index, starting after the focused task and
* up to the last balanced column.
*
@@ -4528,8 +4575,8 @@
if (mTopRowIdSet.isEmpty()) return null; // return earlier
TaskView lastVisibleTaskView = null;
- IntArray topRowIdArray = getTopRowIdArray();
- IntArray bottomRowIdArray = getBottomRowIdArray();
+ IntArray topRowIdArray = mUtils.getTopRowIdArray();
+ IntArray bottomRowIdArray = mUtils.getBottomRowIdArray();
int balancedColumns = Math.min(bottomRowIdArray.size(), topRowIdArray.size());
for (int i = 0; i < balancedColumns; i++) {
@@ -4547,24 +4594,33 @@
return lastVisibleTaskView;
}
- private void removeTaskInternal(@NonNull TaskView dismissedTaskView) {
- UI_HELPER_EXECUTOR
- .getHandler()
- .post(
- () -> {
- if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()
- && dismissedTaskView instanceof DesktopTaskView) {
- // TODO: b/362720497 - Use the api with desktop id instead.
- SystemUiProxy.INSTANCE
+ private void removeTaskInternal(@NonNull TaskView dismissedTaskView) {
+ UI_HELPER_EXECUTOR
+ .getHandler()
+ .post(
+ () -> {
+ if (dismissedTaskView instanceof DesktopTaskView desktopTaskView) {
+ removeDesktopTaskView(desktopTaskView);
+ } else {
+ for (int taskId : dismissedTaskView.getTaskIds()) {
+ ActivityManagerWrapper.getInstance().removeTask(taskId);
+ }
+ }
+ });
+ }
+
+ private void removeDesktopTaskView(DesktopTaskView desktopTaskView) {
+ if (areMultiDesksFlagsEnabled()) {
+ SystemUiProxy.INSTANCE
.get(getContext())
- .removeDesktop(mContainer.getDisplay().getDisplayId());
- } else {
- for (int taskId : dismissedTaskView.getTaskIds()) {
- ActivityManagerWrapper.getInstance().removeTask(taskId);
- }
- }
- });
- }
+ .removeDesk(desktopTaskView.getDeskId());
+ } else if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) {
+ SystemUiProxy.INSTANCE
+ .get(getContext())
+ .removeDefaultDeskInDisplay(
+ mContainer.getDisplay().getDisplayId());
+ }
+ }
protected void onDismissAnimationEnds() {
AccessibilityManagerCompat.sendTestProtocolEventToTest(getContext(),
@@ -4584,6 +4640,12 @@
mPendingAnimation = anim;
mPendingAnimation.addEndListener(isSuccess -> {
if (isSuccess) {
+ // Remove desktops first, since desks can be empty (so they have no recent tasks),
+ // and closing all tasks on a desk doesn't always necessarily mean that the desk
+ // will be removed. So, there are no guarantees that the below call to
+ // `ActivityManagerWrapper::removeAllRecentTasks()` will be enough.
+ SystemUiProxy.INSTANCE.get(getContext()).removeAllDesks();
+
// Remove all the task views now
finishRecentsAnimation(true /* toRecents */, false /* shouldPip */, () -> {
UI_HELPER_EXECUTOR.getHandler().post(
@@ -4624,8 +4686,9 @@
}
// Init task grid nav helper with top/bottom id arrays.
- TaskGridNavHelper taskGridNavHelper = new TaskGridNavHelper(getTopRowIdArray(),
- getBottomRowIdArray(), mUtils.getLargeTaskViewIds(), mAddDesktopButton != null);
+ TaskGridNavHelper taskGridNavHelper = new TaskGridNavHelper(mUtils.getTopRowIdArray(),
+ mUtils.getBottomRowIdArray(), mUtils.getLargeTaskViewIds(),
+ mAddDesktopButton != null);
// Get current page's task view ID.
TaskView currentPageTaskView = getCurrentPageTaskView();
@@ -4684,7 +4747,15 @@
public void dismissTaskView(TaskView taskView, boolean animateTaskView, boolean removeTask) {
PendingAnimation pa = new PendingAnimation(DISMISS_TASK_DURATION);
createTaskDismissAnimation(pa, taskView, animateTaskView, removeTask, DISMISS_TASK_DURATION,
- false /* dismissingForSplitSelection*/);
+ false /* dismissingForSplitSelection*/, false /* isExpressiveDismiss */);
+ runDismissAnimation(pa);
+ }
+
+ protected void expressiveDismissTaskView(TaskView taskView) {
+ PendingAnimation pa = new PendingAnimation(DISMISS_TASK_DURATION);
+ createTaskDismissAnimation(pa, taskView, false /* animateTaskView */, true /* removeTask */,
+ DISMISS_TASK_DURATION, false /* dismissingForSplitSelection*/,
+ true /* isExpressiveDismiss */);
runDismissAnimation(pa);
}
@@ -4704,7 +4775,7 @@
private void createDesk(View view) {
SystemUiProxy.INSTANCE
.get(getContext())
- .createDesktop(mContainer.getDisplay().getDisplayId());
+ .createDesk(mContainer.getDisplay().getDisplayId());
}
@Override
@@ -4712,6 +4783,11 @@
if (isHandlingTouch() || event.getAction() != KeyEvent.ACTION_DOWN) {
return super.dispatchKeyEvent(event);
}
+
+ if (mUtils.shouldInterceptKeyEvent(event)) {
+ return super.dispatchKeyEvent(event);
+ }
+
switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_TAB:
return snapToPageRelative(event.isShiftPressed() ? -1 : 1, true /* cycle */,
@@ -4968,6 +5044,10 @@
}
setPivotX(mTempPointF.x);
setPivotY(mTempPointF.y);
+ if (enableGridOnlyOverview()) {
+ runActionOnRemoteHandles(remoteTargetHandle ->
+ remoteTargetHandle.getTaskViewSimulator().setPivotOverride(mTempPointF));
+ }
}
/**
@@ -5413,7 +5493,7 @@
}
// Splitting from Overview for fullscreen task
createTaskDismissAnimation(builder, mSplitHiddenTaskView, true, false, duration,
- true /* dismissingForSplitSelection*/);
+ true /* dismissingForSplitSelection*/, false /* isExpressiveDismiss */);
} else {
// Splitting from Home
TaskView currentPageTaskView = getTaskViewAt(mCurrentPage);
@@ -5421,7 +5501,7 @@
// display correct animation in split mode
if (currentPageTaskView instanceof DesktopTaskView) {
createTaskDismissAnimation(builder, null, true, false, duration,
- true /* dismissingForSplitSelection*/);
+ true /* dismissingForSplitSelection*/, false /* isExpressiveDismiss */);
} else {
createInitialSplitSelectAnimation(builder);
}
@@ -5742,12 +5822,10 @@
// above RecentsView to avoid wallpaper blur from being applied to it.
if (!taskView.isRunningTask()) {
runActionOnRemoteHandles(
- remoteTargetHandle -> {
- remoteTargetHandle.getTaskViewSimulator().setPivotOverride(
- mTempPointF);
- remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(
- false);
- });
+ remoteTargetHandle ->
+ remoteTargetHandle.getTaskViewSimulator()
+ .setPivotOverride(mTempPointF));
+ mBlurUtils.setDrawLiveTileBelowRecents(false);
}
}
@@ -5796,6 +5874,9 @@
if (taskView instanceof DesktopTaskView) {
anim.play(ObjectAnimator.ofArgb(mContainer.getScrimView(), VIEW_BACKGROUND_COLOR,
Color.TRANSPARENT));
+ if (enableDesktopExplodedView()) {
+ anim.play(ObjectAnimator.ofFloat(this, DESK_EXPLODE_PROGRESS, 1f, 0f));
+ }
}
DepthController depthController = getDepthController();
if (depthController != null) {
@@ -5879,8 +5960,7 @@
mPendingAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
- runActionOnRemoteHandles(remoteTargetHandle ->
- remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(false));
+ mBlurUtils.setDrawLiveTileBelowRecents(false);
}
});
mPendingAnimation.addEndListener(isSuccess -> {
@@ -5918,8 +5998,7 @@
// If launch animation didn't complete i.e. user dragged live tile down and then
// back up and returned to Overview, then we need to ensure we reset the
// view to draw below recents so that it can't be interacted with.
- runActionOnRemoteHandles(remoteTargetHandle ->
- remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true));
+ mBlurUtils.setDrawLiveTileBelowRecents(true);
redrawLiveTile();
}
return Unit.INSTANCE;
@@ -5998,6 +6077,7 @@
});
}
+ @Nullable
public RemoteTargetHandle[] getRemoteTargetHandles() {
return mRemoteTargetHandles;
}
@@ -6032,7 +6112,7 @@
// mSyncTransactionApplier doesn't get transferred over
runActionOnRemoteHandles(remoteTargetHandle -> {
final TransformParams params = remoteTargetHandle.getTransformParams();
- if (Flags.enableFallbackOverviewInWindow() || Flags.enableLauncherOverviewInWindow()) {
+ if (RecentsWindowFlags.Companion.getEnableOverviewInWindow()) {
params.setHomeBuilderProxy((builder, app, transformParams) -> {
mTmpMatrix.setScale(
1f, 1f, app.localBounds.exactCenterX(), app.localBounds.exactCenterY());
@@ -6151,6 +6231,9 @@
mRecentsAnimationController = null;
mSplitSelectStateController.setRecentsAnimationRunning(false);
executeSideTaskLaunchCallback();
+ if (enableOverviewBackgroundWallpaperBlur()) {
+ mBlurUtils.setDrawLiveTileBelowRecents(false);
+ }
}
public void setDisallowScrollToClearAll(boolean disallowScrollToClearAll) {
@@ -6159,6 +6242,17 @@
updateMinAndMaxScrollX();
}
}
+ /**
+ * Update the value of [mDisallowScrollToAddDesk]
+ */
+ public void setDisallowScrollToAddDesk(boolean disallowScrollToAddDesk) {
+ if (mDisallowScrollToAddDesk != disallowScrollToAddDesk) {
+ mDisallowScrollToAddDesk = disallowScrollToAddDesk;
+ updateMinAndMaxScrollX();
+ }
+ }
+
+
/**
* Updates page scroll synchronously after measure and layout child views.
@@ -6215,9 +6309,7 @@
private int getFirstViewIndex() {
final View firstView;
- if (mAddDesktopButton != null) {
- firstView = mAddDesktopButton;
- } else if (mShowAsGridLastOnLayout) {
+ if (mShowAsGridLastOnLayout) {
// For grid Overview, it always start if a large tile (focused task or desktop task) if
// they exist, otherwise it start with the first task.
TaskView firstLargeTaskView = mUtils.getFirstLargeTaskView();
@@ -6287,13 +6379,6 @@
outPageScrolls[clearAllIndex] = clearAllScroll;
}
- int addDesktopButtonIndex = indexOfChild(mAddDesktopButton);
- if (addDesktopButtonIndex != -1 && addDesktopButtonIndex < outPageScrolls.length) {
- outPageScrolls[addDesktopButtonIndex] =
- newPageScrolls[addDesktopButtonIndex] + mAddDesktopButton.getScrollAdjustment(
- showAsGrid);
- }
-
int lastTaskScroll = getLastTaskScroll(clearAllScroll, clearAllWidth);
getTaskViews().forEachWithIndexInParent((index, taskView) -> {
float scrollDiff = taskView.getScrollAdjustment(showAsGrid);
@@ -6308,6 +6393,27 @@
"getPageScrolls - outPageScrolls[" + index + "]: " + outPageScrolls[index]);
}
});
+
+ int addDesktopButtonIndex = indexOfChild(mAddDesktopButton);
+ if (addDesktopButtonIndex >= 0 && addDesktopButtonIndex < outPageScrolls.length) {
+ int firstViewIndex = getFirstViewIndex();
+ if (firstViewIndex >= 0 && firstViewIndex < outPageScrolls.length) {
+ // If we can scroll to [AddDesktopButton], make its page scroll equal to
+ // the first [TaskView]. Otherwise, make its page scroll out of range of
+ // [minScroll, maxScroll].
+ if (!mDisallowScrollToAddDesk) {
+ outPageScrolls[addDesktopButtonIndex] = outPageScrolls[firstViewIndex];
+ } else {
+ outPageScrolls[addDesktopButtonIndex] =
+ outPageScrolls[firstViewIndex] + (mIsRtl ? 1 : -1);
+ }
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "getPageScrolls - addDesktopButtonScroll: "
+ + outPageScrolls[addDesktopButtonIndex]);
+ }
+ }
if (DEBUG) {
Log.d(TAG, "getPageScrolls - clearAllScroll: " + clearAllScroll);
}
@@ -6416,7 +6522,8 @@
* Returns how many pixels the page is offset from its scroll position.
*/
private int getOffsetFromScrollPosition(int pageIndex) {
- return getOffsetFromScrollPosition(pageIndex, getTopRowIdArray(), getBottomRowIdArray());
+ return getOffsetFromScrollPosition(pageIndex, mUtils.getTopRowIdArray(),
+ mUtils.getBottomRowIdArray());
}
private int getOffsetFromScrollPosition(
@@ -6715,7 +6822,7 @@
.displayOverviewTasksAsGrid(mContainer.getDeviceProfile()));
}
- private boolean showAsFullscreen() {
+ protected boolean showAsFullscreen() {
return mOverviewFullscreenEnabled
&& mCurrentGestureEndTarget != GestureState.GestureEndTarget.RECENTS;
}
@@ -6930,6 +7037,58 @@
// TODO: b/389209338 - update the AddDesktopButton's visibility on this.
}
+ @Override
+ public void onDeskAdded(int displayId, int deskId) {
+ // Ignore desk changes that don't belong to this display.
+ if (displayId != mContainer.getDisplay().getDisplayId()) {
+ return;
+ }
+
+ if (mUtils.getDesktopTaskViewForDeskId(deskId) != null) {
+ Log.e(TAG, "A task view for this desk has already been added.");
+ return;
+ }
+
+ // We assume that a newly added desk is always empty and gets added to the left of the
+ // `AddNewDesktopButton`.
+ DesktopTaskView desktopTaskView =
+ (DesktopTaskView) getTaskViewFromPool(TaskViewType.DESKTOP);
+ desktopTaskView.bind(new DesktopTask(deskId, new ArrayList<>()),
+ mOrientationState, mTaskOverlayFactory);
+
+ Objects.requireNonNull(mAddDesktopButton);
+ final int insertionIndex = 1 + indexOfChild(mAddDesktopButton);
+ addView(desktopTaskView, insertionIndex);
+
+ updateTaskSize();
+ updateChildTaskOrientations();
+
+ // TODO: b/401002178 - Recalculate the new current page such that the addition of the new
+ // desk does not result in a change in the current scroll page.
+ }
+
+ @Override
+ public void onDeskRemoved(int displayId, int deskId) {
+ // Ignore desk changes that don't belong to this display.
+ if (displayId != mContainer.getDisplay().getDisplayId()) {
+ return;
+ }
+
+ // We need to distinguish between desk removals that are triggered from outside of overview
+ // vs. the ones that were initiated from overview by dismissing the corresponding desktop
+ // task view.
+ var taskView = mUtils.getDesktopTaskViewForDeskId(deskId);
+ if (taskView != null) {
+ dismissTaskView(taskView, true, true);
+ }
+ }
+
+ @Override
+ public void onActiveDeskChanged(int displayId, int newActiveDesk, int oldActiveDesk) {
+ // TODO: b/400870600 - We may need to add code here to special case when an empty desk gets
+ // activated, since `RemoteDesktopLaunchTransitionRunner` doesn't always get triggered.
+ }
+
/** Get the color used for foreground scrimming the RecentsView for sharing. */
public static int getForegroundScrimDimColor(Context context) {
return context.getColor(R.color.overview_foreground_scrim_color);
@@ -7054,4 +7213,15 @@
public interface TaskLaunchListener {
void onTaskLaunched();
}
+
+ /**
+ * Sets whether the remote animation targets should draw below the recents view.
+ *
+ * @param drawBelowRecents whether the surface should draw below Recents.
+ * @param remoteTargetHandles collection of remoteTargetHandles in Recents.
+ */
+ public void setDrawBelowRecents(boolean drawBelowRecents,
+ RemoteTargetHandle[] remoteTargetHandles) {
+ mBlurUtils.setDrawBelowRecents(drawBelowRecents, remoteTargetHandles);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt b/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt
index 31ae890..24b7fa7 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt
+++ b/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt
@@ -18,11 +18,18 @@
import android.graphics.Rect
import android.util.FloatProperty
+import android.view.KeyEvent
import android.view.View
import androidx.core.view.children
+import com.android.launcher3.AbstractFloatingView
+import com.android.launcher3.Flags
import com.android.launcher3.Flags.enableLargeDesktopWindowingTile
import com.android.launcher3.Flags.enableSeparateExternalDisplayTasks
+import com.android.launcher3.statehandlers.DesktopVisibilityController
+import com.android.launcher3.statehandlers.DesktopVisibilityController.Companion.INACTIVE_DESK_ID
import com.android.launcher3.util.IntArray
+import com.android.quickstep.util.DesksUtils.Companion.areMultiDesksFlagsEnabled
+import com.android.quickstep.util.DesktopTask
import com.android.quickstep.util.GroupTask
import com.android.quickstep.util.isExternalDisplay
import com.android.quickstep.views.RecentsView.RUNNING_TASK_ATTACH_ALPHA
@@ -52,7 +59,12 @@
* @return Sorted list of GroupTasks to be used in the RecentsView.
*/
fun sortDesktopTasksToFront(tasks: List<GroupTask>): List<GroupTask> {
- val (desktopTasks, otherTasks) = tasks.partition { it.taskViewType == TaskViewType.DESKTOP }
+ var (desktopTasks, otherTasks) = tasks.partition { it.taskViewType == TaskViewType.DESKTOP }
+ if (areMultiDesksFlagsEnabled()) {
+ // Desk IDs of newer desks are larger than those of older desks, hence we can use them
+ // to sort desks from old to new.
+ desktopTasks = desktopTasks.sortedBy { (it as DesktopTask).deskId }
+ }
return otherTasks + desktopTasks
}
@@ -83,6 +95,25 @@
/** Returns a list of all large TaskView Ids from [TaskView]s */
fun getLargeTaskViewIds(): List<Int> = taskViews.filter { it.isLargeTile }.map { it.taskViewId }
+ /** Returns a list of all large TaskViews [TaskView]s */
+ fun getLargeTaskViews(): List<TaskView> = taskViews.filter { it.isLargeTile }
+
+ /** Returns all the TaskViews in the top row, without the focused task */
+ fun getTopRowTaskViews(): List<TaskView> =
+ taskViews.filter { recentsView.mTopRowIdSet.contains(it.taskViewId) }
+
+ /** Returns all the task Ids in the top row, without the focused task */
+ fun getTopRowIdArray(): IntArray = getTopRowTaskViews().map { it.taskViewId }.toIntArray()
+
+ /** Returns all the TaskViews in the bottom row, without the focused task */
+ fun getBottomRowTaskViews(): List<TaskView> =
+ taskViews.filter { !recentsView.mTopRowIdSet.contains(it.taskViewId) && !it.isLargeTile }
+
+ /** Returns all the task Ids in the bottom row, without the focused task */
+ fun getBottomRowIdArray(): IntArray = getBottomRowTaskViews().map { it.taskViewId }.toIntArray()
+
+ private fun List<Int>.toIntArray() = IntArray(size).apply { this@toIntArray.forEach(::add) }
+
/** Counts [TaskView]s that are large tiles. */
fun getLargeTileCount(): Int = taskViews.count { it.isLargeTile }
@@ -95,6 +126,22 @@
it.isLargeTile && !(recentsView.isSplitSelectionActive && it is DesktopTaskView)
}
+ /**
+ * Returns the [DesktopTaskView] that matches the given [deskId], or null if it doesn't exist.
+ */
+ fun getDesktopTaskViewForDeskId(deskId: Int): DesktopTaskView? {
+ if (deskId == INACTIVE_DESK_ID) {
+ return null
+ }
+ return taskViews.firstOrNull { it is DesktopTaskView && it.deskId == deskId }
+ as? DesktopTaskView
+ }
+
+ /** Returns the active desk ID of the display that contains the [recentsView] instance. */
+ fun getActiveDeskIdOnThisDisplay(): Int =
+ DesktopVisibilityController.INSTANCE.get(recentsView.context)
+ .getActiveDeskId(recentsView.mContainer.display.displayId)
+
/** Returns the expected focus task. */
fun getFirstNonDesktopTaskView(): TaskView? =
if (enableLargeDesktopWindowingTile()) taskViews.firstOrNull { it !is DesktopTaskView }
@@ -121,6 +168,17 @@
private fun getDeviceProfile() = (recentsView.mContainer as RecentsViewContainer).deviceProfile
fun getRunningTaskExpectedIndex(runningTaskView: TaskView): Int {
+ if (areMultiDesksFlagsEnabled() && runningTaskView is DesktopTaskView) {
+ // Use the [deskId] to keep desks in the order of their creation, as a newer desk
+ // always has a larger [deskId] than the older desks.
+ val desktopTaskView =
+ taskViews.firstOrNull {
+ it is DesktopTaskView &&
+ it.deskId != INACTIVE_DESK_ID &&
+ it.deskId <= runningTaskView.deskId
+ }
+ if (desktopTaskView != null) return recentsView.indexOfChild(desktopTaskView)
+ }
val firstTaskViewIndex = recentsView.indexOfChild(getFirstTaskView())
return if (getDeviceProfile().isTablet) {
var index = firstTaskViewIndex
@@ -266,8 +324,8 @@
return
}
getRowRect(getFirstLargeTaskView(), getLastLargeTaskView(), outTaskViewRowRect)
- getRowRect(recentsView.getTopRowIdArray(), outTopRowRect)
- getRowRect(recentsView.getBottomRowIdArray(), outBottomRowRect)
+ getRowRect(getTopRowIdArray(), outTopRowRect)
+ getRowRect(getBottomRowIdArray(), outBottomRowRect)
// Expand large tile Rect to include space between top/bottom row.
val nonEmptyRowRect =
@@ -299,6 +357,18 @@
}
}
+ fun shouldInterceptKeyEvent(event: KeyEvent): Boolean {
+ if (Flags.enableOverviewIconMenu()) {
+ val floatingView: AbstractFloatingView? = AbstractFloatingView.getTopOpenViewWithType(
+ recentsView.mContainer as RecentsViewContainer,
+ AbstractFloatingView.TYPE_TASK_MENU
+ )
+ val isMenuOpen = floatingView?.isOpen
+ return isMenuOpen == true || event.keyCode == KeyEvent.KEYCODE_TAB
+ }
+ return false
+ }
+
var deskExplodeProgress: Float = 0f
set(value) {
field = value
diff --git a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
index 0d80f6a..4cf1eae 100644
--- a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
+++ b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
@@ -39,10 +39,11 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.statemanager.StateManager;
import com.android.quickstep.util.AnimUtils;
import com.android.quickstep.util.SplitSelectStateController;
+import com.android.wm.shell.shared.TypefaceUtils;
+import com.android.wm.shell.shared.TypefaceUtils.FontFamily;
/**
* A rounded rectangular component containing a single TextView.
@@ -129,6 +130,7 @@
cancelTextView.setVisibility(VISIBLE);
cancelTextView.setOnClickListener((v) -> exitSplitSelection());
instructionTextView.setText(R.string.toast_contextual_split_select_app);
+ TypefaceUtils.setTypeface(instructionTextView, FontFamily.GSF_BODY_MEDIUM);
// After layout, expand touch target of cancel button to meet minimum a11y measurements.
post(() -> {
diff --git a/quickstep/src/com/android/quickstep/views/TaskContainer.kt b/quickstep/src/com/android/quickstep/views/TaskContainer.kt
index 2b9d036..afe7e92 100644
--- a/quickstep/src/com/android/quickstep/views/TaskContainer.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskContainer.kt
@@ -18,16 +18,20 @@
import android.graphics.Bitmap
import android.graphics.Matrix
+import android.util.Log
import android.view.View
import android.view.View.OnClickListener
+import com.android.app.tracing.traceSection
import com.android.launcher3.Flags.enableRefactorTaskThumbnail
import com.android.launcher3.model.data.TaskViewItemInfo
import com.android.launcher3.util.SplitConfigurationOptions
import com.android.launcher3.util.TransformingTouchDelegate
import com.android.quickstep.TaskOverlayFactory
import com.android.quickstep.ViewUtils.addAccessibleChildToList
+import com.android.quickstep.recents.domain.usecase.ThumbnailPosition
import com.android.quickstep.recents.ui.mapper.TaskUiStateMapper
import com.android.quickstep.recents.ui.viewmodel.TaskData
+import com.android.quickstep.task.thumbnail.TaskContentView
import com.android.quickstep.task.thumbnail.TaskThumbnailView
import com.android.systemui.shared.recents.model.Task
import com.android.systemui.shared.recents.model.ThumbnailData
@@ -36,6 +40,7 @@
class TaskContainer(
val taskView: TaskView,
val task: Task,
+ val taskContentView: TaskContentView,
val snapshotView: View,
val iconView: TaskViewIcon,
/**
@@ -52,6 +57,8 @@
taskOverlayFactory: TaskOverlayFactory,
) {
val overlay: TaskOverlayFactory.TaskOverlay<*> = taskOverlayFactory.createOverlay(this)
+ var thumbnailPosition: ThumbnailPosition? = null
+ private var overlayEnabledStatus = false
init {
if (enableRefactorTaskThumbnail()) {
@@ -94,27 +101,28 @@
val itemInfo: TaskViewItemInfo
get() = TaskViewItemInfo(taskView, this)
- fun bind() {
- digitalWellBeingToast?.bind(task, taskView, snapshotView, stagePosition)
- if (!enableRefactorTaskThumbnail()) {
- thumbnailViewDeprecated.bind(task, overlay, taskView)
+ fun bind() =
+ traceSection("TaskContainer.bind") {
+ digitalWellBeingToast?.bind(task, taskView, snapshotView, stagePosition)
+ if (!enableRefactorTaskThumbnail()) {
+ thumbnailViewDeprecated.bind(task, overlay, taskView)
+ }
}
- overlay.init()
- }
- fun destroy() {
- digitalWellBeingToast?.destroy()
- snapshotView.scaleX = 1f
- snapshotView.scaleY = 1f
- overlay.destroy()
- if (enableRefactorTaskThumbnail()) {
- isThumbnailValid = false
- thumbnailData = null
- thumbnailView.onRecycle()
- } else {
- thumbnailViewDeprecated.setShowSplashForSplitSelection(false)
+ fun destroy() =
+ traceSection("TaskContainer.destroy") {
+ digitalWellBeingToast?.destroy()
+ taskContentView.scaleX = 1f
+ taskContentView.scaleY = 1f
+ overlay.reset()
+ if (enableRefactorTaskThumbnail()) {
+ isThumbnailValid = false
+ thumbnailData = null
+ thumbnailView.onRecycle()
+ } else {
+ thumbnailViewDeprecated.setShowSplashForSplitSelection(false)
+ }
}
- }
fun setOverlayEnabled(enabled: Boolean) {
if (!enableRefactorTaskThumbnail()) {
@@ -122,6 +130,35 @@
}
}
+ fun setOverlayEnabled(enabled: Boolean, thumbnailPosition: ThumbnailPosition?) {
+ if (enableRefactorTaskThumbnail()) {
+ if (overlayEnabledStatus != enabled || this.thumbnailPosition != thumbnailPosition) {
+ overlayEnabledStatus = enabled
+
+ refreshOverlay(thumbnailPosition)
+ }
+ }
+ }
+
+ fun refreshOverlay(thumbnailPosition: ThumbnailPosition?) =
+ traceSection("TaskContainer.refreshOverlay") {
+ this.thumbnailPosition = thumbnailPosition
+ when {
+ !overlayEnabledStatus -> overlay.reset()
+ thumbnailPosition == null -> {
+ Log.e(TAG, "Thumbnail position was null during overlay refresh", Exception())
+ overlay.reset()
+ }
+ else ->
+ overlay.initOverlay(
+ task,
+ thumbnailData?.thumbnail,
+ thumbnailPosition.matrix,
+ thumbnailPosition.isRotated,
+ )
+ }
+ }
+
fun addChildForAccessibility(outChildren: ArrayList<View>) {
addAccessibleChildToList(iconView.asView(), outChildren)
addAccessibleChildToList(snapshotView, outChildren)
@@ -135,18 +172,16 @@
liveTile: Boolean,
hasHeader: Boolean,
clickCloseListener: OnClickListener?,
- ) {
- thumbnailView.setState(
- TaskUiStateMapper.toTaskThumbnailUiState(
- state,
- liveTile,
- hasHeader,
- clickCloseListener,
- ),
- state?.taskId,
- )
- thumbnailData = if (state is TaskData.Data) state.thumbnailData else null
- }
+ ) =
+ traceSection("TaskContainer.setState") {
+ taskContentView.setState(
+ TaskUiStateMapper.toTaskHeaderState(state, hasHeader, clickCloseListener),
+ TaskUiStateMapper.toTaskThumbnailUiState(state, liveTile),
+ state?.taskId,
+ )
+ thumbnailData = if (state is TaskData.Data) state.thumbnailData else null
+ overlay.setThumbnailState(thumbnailData)
+ }
fun updateTintAmount(tintAmount: Float) {
thumbnailView.updateTintAmount(tintAmount)
@@ -186,4 +221,8 @@
fun updateThumbnailMatrix(matrix: Matrix) {
thumbnailView.setImageMatrix(matrix)
}
+
+ companion object {
+ const val TAG = "TaskContainer"
+ }
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailViewHeader.kt b/quickstep/src/com/android/quickstep/views/TaskHeaderView.kt
similarity index 63%
rename from quickstep/src/com/android/quickstep/views/TaskThumbnailViewHeader.kt
rename to quickstep/src/com/android/quickstep/views/TaskHeaderView.kt
index 9a8805b..1fda5a3 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailViewHeader.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskHeaderView.kt
@@ -18,23 +18,33 @@
import android.content.Context
import android.util.AttributeSet
-import android.widget.FrameLayout
import android.widget.ImageButton
import android.widget.ImageView
import android.widget.TextView
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.core.view.isGone
import com.android.launcher3.R
-import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.ThumbnailHeader
+import com.android.quickstep.task.thumbnail.TaskHeaderUiState
-class TaskThumbnailViewHeader
-@JvmOverloads
-constructor(context: Context, attrs: AttributeSet? = null) : FrameLayout(context, attrs) {
+class TaskHeaderView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
+ ConstraintLayout(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) }
private val headerCloseButton: ImageButton by lazy { findViewById(R.id.header_close_button) }
- fun setHeader(header: ThumbnailHeader) {
- headerTitleView.setText(header.title)
+ fun setState(taskHeaderState: TaskHeaderUiState) {
+ when (taskHeaderState) {
+ is TaskHeaderUiState.ShowHeader -> {
+ setHeader(taskHeaderState.header)
+ isGone = false
+ }
+ TaskHeaderUiState.HideHeader -> isGone = true
+ }
+ }
+
+ private fun setHeader(header: TaskHeaderUiState.ThumbnailHeader) {
+ headerTitleView.text = header.title
headerIconView.setImageDrawable(header.icon)
headerCloseButton.setOnClickListener(header.clickCloseListener)
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.kt b/quickstep/src/com/android/quickstep/views/TaskMenuView.kt
index 4777f4f..696f934 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuView.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.kt
@@ -22,16 +22,17 @@
import android.content.Context
import android.graphics.Outline
import android.graphics.Rect
-import android.graphics.drawable.GradientDrawable
import android.graphics.drawable.ShapeDrawable
import android.graphics.drawable.shapes.RectShape
import android.util.AttributeSet
import android.view.Gravity
+import android.view.KeyEvent
import android.view.MotionEvent
import android.view.View
import android.view.ViewOutlineProvider
import android.widget.LinearLayout
import android.widget.TextView
+import androidx.core.content.res.ResourcesCompat
import com.android.app.animation.Interpolators
import com.android.launcher3.AbstractFloatingView
import com.android.launcher3.Flags.enableOverviewIconMenu
@@ -67,6 +68,17 @@
private var menuTranslationXBeforeOpen = 0f
private var menuTranslationYBeforeOpen = 0f
+ // Spaced claimed below Overview (taskbar and insets)
+ private val taskbarTop by lazy {
+ recentsViewContainer.deviceProfile.heightPx -
+ recentsViewContainer.deviceProfile.overviewActionsClaimedSpaceBelow
+ }
+ private val minMenuTop by lazy { taskContainer.iconView.height.toFloat() }
+ // TODO(b/401476868): Replace overviewRowSpacing with correct margin to the taskbarTop.
+ private val maxMenuBottom by lazy {
+ (taskbarTop - recentsViewContainer.deviceProfile.overviewRowSpacing).toFloat()
+ }
+
init {
clipToOutline = true
}
@@ -103,13 +115,9 @@
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
var heightMeasure = heightMeasureSpec
- if (!(enableOverviewIconMenu() && taskView.isOnGridBottomRow())) {
- // TODO(b/326952853): Cap menu height for grid bottom row in a way that doesn't break
- // additionalTranslationY.
- val maxMenuHeight = calculateMaxHeight()
- if (MeasureSpec.getSize(heightMeasure) > maxMenuHeight) {
- heightMeasure = MeasureSpec.makeMeasureSpec(maxMenuHeight, MeasureSpec.AT_MOST)
- }
+ val maxMenuHeight = calculateMaxHeight()
+ if (MeasureSpec.getSize(heightMeasure) > maxMenuHeight) {
+ heightMeasure = MeasureSpec.makeMeasureSpec(maxMenuHeight, MeasureSpec.AT_MOST)
}
super.onMeasure(widthMeasureSpec, heightMeasure)
}
@@ -157,7 +165,18 @@
recentsViewContainer.layoutInflater.inflate(R.layout.task_view_menu_option, this, false)
as LinearLayout
if (enableOverviewIconMenu()) {
- (menuOptionView.background as GradientDrawable).cornerRadius = 0f
+ menuOptionView.background =
+ ResourcesCompat.getDrawable(
+ resources,
+ R.drawable.app_chip_menu_item_bg,
+ context.theme,
+ )
+ menuOptionView.foreground =
+ ResourcesCompat.getDrawable(
+ resources,
+ R.drawable.app_chip_menu_item_fg,
+ context.theme,
+ )
}
menuOption.setIconAndLabelFor(
menuOptionView.findViewById(R.id.icon),
@@ -196,7 +215,7 @@
taskContainer.stagePosition,
)
// Gravity set to Left instead of Start as sTempRect.left measures Left distance not Start
- params.gravity = Gravity.START
+ params.gravity = Gravity.LEFT
layoutParams = params
scaleX = taskView.scaleX
scaleY = taskView.scaleY
@@ -208,6 +227,13 @@
optionLayout.showDividers =
if (enableOverviewIconMenu()) SHOW_DIVIDER_NONE else SHOW_DIVIDER_MIDDLE
+ optionLayout.background =
+ if (enableOverviewIconMenu()) {
+ ResourcesCompat.getDrawable(resources, R.drawable.app_chip_menu_bg, context.theme)
+ } else {
+ null
+ }
+
orientationHandler.setTaskOptionsMenuLayoutOrientation(
deviceProfile,
optionLayout,
@@ -345,15 +371,17 @@
* view will scroll. The maximum menu size will sit inside the task with a margin on the top and
* bottom.
*/
- private fun calculateMaxHeight(): Int {
- val taskInsetMargin = resources.getDimension(R.dimen.task_card_margin)
- return taskView.pagedOrientationHandler.getTaskMenuHeight(
- taskInsetMargin,
- recentsViewContainer.deviceProfile,
- translationX,
- translationY,
+ private fun calculateMaxHeight(): Int =
+ taskView.pagedOrientationHandler.getTaskMenuHeight(
+ taskInsetMargin = resources.getDimension(R.dimen.task_card_margin), // taskInsetMargin
+ deviceProfile = recentsViewContainer.deviceProfile,
+ taskMenuX = translationX,
+ taskMenuY =
+ // Bottom menu can translate up to show more options. So we use the min
+ // translation allowed to calculate its max height.
+ if (enableOverviewIconMenu() && taskView.isOnGridBottomRow()) minMenuTop
+ else translationY,
)
- }
private fun setOnClosingStartCallback(onClosingStartCallback: Runnable?) {
this.onClosingStartCallback = onClosingStartCallback
@@ -362,18 +390,23 @@
private fun animateOpenOrCloseAppChip(closing: Boolean, animatorBuilder: AnimatorSet.Builder) {
val iconAppChip = taskContainer.iconView.asView() as IconAppChipView
+ // Animate menu up for enough room to display full menu when task on bottom row.
var additionalTranslationY = 0f
if (taskView.isOnGridBottomRow()) {
- // Animate menu up for enough room to display full menu when task on bottom row.
- val menuBottom = height + menuTranslationYBeforeOpen
- val taskBottom = taskView.height + taskView.persistentTranslationY
- val taskbarTop =
- (recentsViewContainer.deviceProfile.heightPx -
- recentsViewContainer.deviceProfile.overviewActionsClaimedSpaceBelow)
- .toFloat()
- val midpoint = (taskBottom + taskbarTop) / 2f
- additionalTranslationY = (-max((menuBottom - midpoint).toDouble(), 0.0)).toFloat()
+ val currentMenuBottom: Float = menuTranslationYBeforeOpen + height
+ additionalTranslationY =
+ if (currentMenuBottom < maxMenuBottom) 0f
+ // Translate menu up for enough room to display full menu when task on bottom row.
+ else maxMenuBottom - currentMenuBottom
+
+ val currentMenuTop = menuTranslationYBeforeOpen + additionalTranslationY
+ // If it translate above the min accepted, it translates to the top of the screen
+ if (currentMenuTop < minMenuTop) {
+ // It subtracts the menuTranslation to make it 0 (top of the screen) + chip size.
+ additionalTranslationY = -menuTranslationYBeforeOpen + minMenuTop
+ }
}
+
val translationYAnim =
ObjectAnimator.ofFloat(
this,
@@ -433,6 +466,24 @@
animatorBuilder.with(menuTranslationXAnim)
}
+ override fun dispatchKeyEvent(event: KeyEvent): Boolean {
+ if (enableOverviewIconMenu()) {
+ if (event.action != KeyEvent.ACTION_DOWN) return super.dispatchKeyEvent(event)
+
+ val isFirstMenuOptionFocused = optionLayout.indexOfChild(optionLayout.focusedChild) == 0
+ val isLastMenuOptionFocused =
+ optionLayout.indexOfChild(optionLayout.focusedChild) == optionLayout.childCount - 1
+ if (
+ (isLastMenuOptionFocused && event.keyCode == KeyEvent.KEYCODE_DPAD_DOWN)
+ || (isFirstMenuOptionFocused && event.keyCode == KeyEvent.KEYCODE_DPAD_UP)
+ ) {
+ iconView.requestFocus()
+ return true
+ }
+ }
+ return super.dispatchKeyEvent(event)
+ }
+
companion object {
private val REVEAL_OPEN_DURATION = if (enableOverviewIconMenu()) 417L else 150L
private val REVEAL_CLOSE_DURATION = if (enableOverviewIconMenu()) 333L else 100L
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailViewDeprecated.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailViewDeprecated.java
index 5ee5e10..74d76e6 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailViewDeprecated.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailViewDeprecated.java
@@ -412,7 +412,12 @@
thumbnailDataAspect, MAX_PCT_BEFORE_ASPECT_RATIOS_CONSIDERED_DIFFERENT);
}
- private boolean isThumbnailRotationDifferentFromTask() {
+ /**
+ * Returns whether or not the current thumbnail is a different orientation to the task.
+ * <p>
+ * Used to disable modal state when screenshot doesn't match the device orientation.
+ */
+ public boolean isThumbnailRotationDifferentFromTask() {
RecentsView recents = mTaskView.getRecentsView();
if (recents == null || mThumbnailData == null) {
return false;
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.kt b/quickstep/src/com/android/quickstep/views/TaskView.kt
index 276318c..be05122 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskView.kt
@@ -33,7 +33,6 @@
import android.view.Display
import android.view.MotionEvent
import android.view.View
-import android.view.View.OnClickListener
import android.view.ViewGroup
import android.view.ViewStub
import android.view.accessibility.AccessibilityNodeInfo
@@ -43,6 +42,8 @@
import androidx.annotation.VisibleForTesting
import androidx.core.view.updateLayoutParams
import com.android.app.animation.Interpolators
+import com.android.app.tracing.traceSection
+import com.android.launcher3.AbstractFloatingView
import com.android.launcher3.Flags.enableCursorHoverStates
import com.android.launcher3.Flags.enableDesktopExplodedView
import com.android.launcher3.Flags.enableGridOnlyOverview
@@ -76,15 +77,18 @@
import com.android.quickstep.FullscreenDrawParams
import com.android.quickstep.RecentsModel
import com.android.quickstep.RemoteAnimationTargets
+import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle
import com.android.quickstep.TaskOverlayFactory
import com.android.quickstep.TaskViewUtils
import com.android.quickstep.orientation.RecentsPagedOrientationHandler
import com.android.quickstep.recents.di.RecentsDependencies
import com.android.quickstep.recents.di.get
import com.android.quickstep.recents.di.inject
+import com.android.quickstep.recents.domain.usecase.ThumbnailPosition
import com.android.quickstep.recents.ui.viewmodel.TaskData
import com.android.quickstep.recents.ui.viewmodel.TaskTileUiState
import com.android.quickstep.recents.ui.viewmodel.TaskViewModel
+import com.android.quickstep.task.thumbnail.TaskContentView
import com.android.quickstep.util.ActiveGestureErrorDetector
import com.android.quickstep.util.ActiveGestureLog
import com.android.quickstep.util.BorderAnimator
@@ -100,7 +104,6 @@
import com.android.systemui.shared.system.ActivityManagerWrapper
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
-import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
@@ -221,7 +224,7 @@
SPLIT_SELECT_TRANSLATION_Y,
)
- protected val primaryDismissTranslationProperty: FloatProperty<TaskView>
+ val primaryDismissTranslationProperty: FloatProperty<TaskView>
get() =
pagedOrientationHandler.getPrimaryValue(DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y)
@@ -309,6 +312,7 @@
// progress: 0 = show icon and no insets; 1 = don't show icon and show full insets.
protected var fullscreenProgress = 0f
set(value) {
+ if (value == field && enableOverviewIconMenu()) return
field = Utilities.boundToRange(value, 0f, 1f)
onFullscreenProgressChanged(field)
}
@@ -490,6 +494,7 @@
// 1 = The TaskView is settled and no longer transitioning
private var settledProgress = 1f
set(value) {
+ if (value == field && enableOverviewIconMenu()) return
field = value
onSettledProgressUpdated(field)
}
@@ -511,7 +516,7 @@
private var viewModel: TaskViewModel? = null
private val dispatcherProvider: DispatcherProvider by RecentsDependencies.inject()
- private val coroutineScope by lazy { CoroutineScope(SupervisorJob() + dispatcherProvider.main) }
+ private val coroutineScope: CoroutineScope by RecentsDependencies.inject()
private val coroutineJobs = mutableListOf<Job>()
/**
@@ -718,13 +723,10 @@
}
protected open fun inflateViewStubs() {
- findViewById<ViewStub>(R.id.snapshot)
- ?.apply {
- layoutResource =
- if (enableRefactorTaskThumbnail()) R.layout.task_thumbnail
- else R.layout.task_thumbnail_deprecated
- }
+ findViewById<ViewStub>(R.id.task_content_view)
+ ?.apply { layoutResource = R.layout.task_content_view }
?.inflate()
+
findViewById<ViewStub>(R.id.icon)
?.apply {
layoutResource =
@@ -734,57 +736,63 @@
?.inflate()
}
- override fun onAttachedToWindow() {
- super.onAttachedToWindow()
- if (enableRefactorTaskThumbnail()) {
- // The TaskView lifecycle is starts the ViewModel during onBind, and cleans it in
- // onRecycle. So it should be initialized at this point. TaskView Lifecycle:
- // `bind` -> `onBind` -> onAttachedToWindow() -> onDetachFromWindow -> onRecycle
- coroutineJobs +=
- coroutineScope.launch { viewModel!!.state.collectLatest(::updateTaskViewState) }
- }
- }
-
- private fun updateTaskViewState(state: TaskTileUiState) {
- sysUiStatusNavFlags = state.sysUiStatusNavFlags
-
- // Updating containers
- val mapOfTasks = state.tasks.associateBy { it.taskId }
- taskContainers.forEach { container ->
- val taskId = container.task.key.id
- val containerState = mapOfTasks[taskId]
- val shouldHaveHeader = (type == TaskViewType.DESKTOP) && enableDesktopExplodedView()
- container.setState(
- state = containerState,
- liveTile = state.isLiveTile,
- hasHeader = shouldHaveHeader,
- clickCloseListener =
- if (shouldHaveHeader) {
- {
- // Update the layout UI to remove this task from the layout grid, and
- // remove the task from ActivityManager afterwards.
- recentsView?.dismissTask(
- taskId,
- /* animate= */ true,
- /* removeTask= */ true,
- )
- }
- } else {
- null
- },
- )
- updateThumbnailValidity(container)
- updateThumbnailMatrix(
- container = container,
- width = container.thumbnailView.width,
- height = container.thumbnailView.height,
- )
-
- if (enableOverviewIconMenu()) {
- setIconState(container, containerState)
+ override fun onAttachedToWindow() =
+ traceSection("TaskView.onAttachedToWindow") {
+ super.onAttachedToWindow()
+ if (enableRefactorTaskThumbnail()) {
+ // The TaskView lifecycle is starts the ViewModel during onBind, and cleans it in
+ // onRecycle. So it should be initialized at this point. TaskView Lifecycle:
+ // `bind` -> `onBind` -> onAttachedToWindow() -> onDetachFromWindow -> onRecycle
+ coroutineJobs +=
+ coroutineScope.launch(dispatcherProvider.main) {
+ viewModel!!.state.collectLatest(::updateTaskViewState)
+ }
}
}
- }
+
+ private fun updateTaskViewState(state: TaskTileUiState) =
+ traceSection("TaskView.updateTaskViewState") {
+ sysUiStatusNavFlags = state.sysUiStatusNavFlags
+
+ // Updating containers
+ val mapOfTasks = state.tasks.associateBy { it.taskId }
+ taskContainers.forEach { container ->
+ val taskId = container.task.key.id
+ val containerState = mapOfTasks[taskId]
+ val shouldHaveHeader = (type == TaskViewType.DESKTOP) && enableDesktopExplodedView()
+ container.setState(
+ state = containerState,
+ liveTile = state.isLiveTile,
+ hasHeader = shouldHaveHeader,
+ clickCloseListener =
+ if (shouldHaveHeader) {
+ {
+ // Update the layout UI to remove this task from the layout grid,
+ // and remove the task from ActivityManager afterwards.
+ recentsView?.dismissTask(
+ taskId,
+ /* animate= */ true,
+ /* removeTask= */ true,
+ )
+ }
+ } else {
+ null
+ },
+ )
+ updateThumbnailValidity(container)
+ val thumbnailPosition =
+ updateThumbnailMatrix(
+ container = container,
+ width = container.thumbnailView.width,
+ height = container.thumbnailView.height,
+ )
+ container.setOverlayEnabled(state.taskOverlayEnabled, thumbnailPosition)
+
+ if (enableOverviewIconMenu()) {
+ setIconState(container, containerState)
+ }
+ }
+ }
private fun updateThumbnailValidity(container: TaskContainer) {
container.isThumbnailValid =
@@ -807,26 +815,37 @@
* @param width The desired width of the thumbnail's container.
* @param height The desired height of the thumbnail's container.
*/
- private fun updateThumbnailMatrix(container: TaskContainer, width: Int, height: Int) {
- val thumbnailPosition =
- viewModel?.getThumbnailPosition(container.thumbnailData, width, height, isLayoutRtl)
- ?: return
- container.updateThumbnailMatrix(thumbnailPosition.matrix)
- }
+ private fun updateThumbnailMatrix(
+ container: TaskContainer,
+ width: Int,
+ height: Int,
+ ): ThumbnailPosition? =
+ traceSection("TaskView.updateThumbnailMatrix") {
+ val thumbnailPosition =
+ viewModel?.getThumbnailPosition(container.thumbnailData, width, height, isLayoutRtl)
+ ?: return null
+ container.updateThumbnailMatrix(thumbnailPosition.matrix)
+ return thumbnailPosition
+ }
- override fun onDetachedFromWindow() {
- super.onDetachedFromWindow()
- if (enableRefactorTaskThumbnail()) {
- // The jobs are being cancelled in the background thread. So we make a copy of the list
- // to prevent cleaning a new job that might be added to this list during onAttach
- // or another moment in the lifecycle.
- val coroutineJobsToCancel = coroutineJobs.toList()
- coroutineJobs.clear()
- coroutineScope.launch(dispatcherProvider.background) {
- coroutineJobsToCancel.forEach { it.cancel("TaskView detaching from window") }
+ override fun onDetachedFromWindow() =
+ traceSection("TaskView.onDetachedFromWindow") {
+ super.onDetachedFromWindow()
+ if (enableRefactorTaskThumbnail()) {
+ // The jobs are being cancelled in the background thread. So we make a copy of the
+ // list to prevent cleaning a new job that might be added to this list during
+ // onAttach or another moment in the lifecycle.
+ val coroutineJobsToCancel = coroutineJobs.toList()
+ coroutineJobs.clear()
+ coroutineScope.launch(dispatcherProvider.background) {
+ traceSection("TaskView.onDetachedFromWindow.cancellingJobs") {
+ coroutineJobsToCancel.forEach {
+ it.cancel("TaskView detaching from window")
+ }
+ }
+ }
}
}
- }
/** Updates this task view to the given {@param task}. */
open fun bind(
@@ -835,10 +854,12 @@
taskOverlayFactory: TaskOverlayFactory,
) {
cancelPendingLoadTasks()
+ this.orientedState = orientedState // Needed for dependencies
taskContainers =
listOf(
createTaskContainer(
task,
+ R.id.task_content_view,
R.id.snapshot,
R.id.icon,
R.id.show_windows,
@@ -850,33 +871,40 @@
onBind(orientedState)
}
- protected open fun onBind(orientedState: RecentsOrientedState) {
- if (enableRefactorTaskThumbnail()) {
- viewModel =
- TaskViewModel(
- taskViewType = type,
- recentsViewData = RecentsDependencies.get(),
- getTaskUseCase = RecentsDependencies.get(),
- getSysUiStatusNavFlagsUseCase = RecentsDependencies.get(),
- isThumbnailValidUseCase = RecentsDependencies.get(),
- getThumbnailPositionUseCase = RecentsDependencies.get(),
- dispatcherProvider = RecentsDependencies.get(),
- )
- .apply { bind(*taskIds) }
- }
-
- taskContainers.forEach { container ->
- container.bind()
- if (enableRefactorTaskThumbnail()) {
- container.thumbnailView.cornerRadius = thumbnailFullscreenParams.currentCornerRadius
- container.thumbnailView.doOnSizeChange { width, height ->
- updateThumbnailValidity(container)
- updateThumbnailMatrix(container, width, height)
+ protected open fun onBind(orientedState: RecentsOrientedState) =
+ traceSection("TaskView.onBind") {
+ traceSection("TaskView.onBind.createViewModel") {
+ if (enableRefactorTaskThumbnail()) {
+ val scopeId = context
+ Log.d(TAG, "onBind $scopeId ${orientedState.containerInterface}")
+ viewModel =
+ TaskViewModel(
+ taskViewType = type,
+ recentsViewData = RecentsDependencies.get(scopeId),
+ getTaskUseCase = RecentsDependencies.get(scopeId),
+ getSysUiStatusNavFlagsUseCase = RecentsDependencies.get(scopeId),
+ isThumbnailValidUseCase = RecentsDependencies.get(scopeId),
+ getThumbnailPositionUseCase = RecentsDependencies.get(scopeId),
+ dispatcherProvider = RecentsDependencies.get(scopeId),
+ )
+ .apply { bind(*taskIds) }
}
}
+
+ taskContainers.forEach { container ->
+ container.bind()
+ if (enableRefactorTaskThumbnail()) {
+ container.taskContentView.cornerRadius =
+ thumbnailFullscreenParams.currentCornerRadius
+ container.taskContentView.doOnSizeChange { width, height ->
+ updateThumbnailValidity(container)
+ val thumbnailPosition = updateThumbnailMatrix(container, width, height)
+ container.refreshOverlay(thumbnailPosition)
+ }
+ }
+ }
+ setOrientationState(orientedState)
}
- setOrientationState(orientedState)
- }
private fun applyThumbnailSplashAlpha() {
val alpha = getSplashAlphaProgress()
@@ -895,26 +923,30 @@
protected fun createTaskContainer(
task: Task,
+ @IdRes taskContentViewId: Int,
@IdRes thumbnailViewId: Int,
@IdRes iconViewId: Int,
@IdRes showWindowViewId: Int,
@IdRes digitalWellbeingBannerId: Int,
@StagePosition stagePosition: Int,
taskOverlayFactory: TaskOverlayFactory,
- ): TaskContainer {
- val iconView = findViewById<View>(iconViewId) as TaskViewIcon
- return TaskContainer(
- this,
- task,
- findViewById(thumbnailViewId),
- iconView,
- TransformingTouchDelegate(iconView.asView()),
- stagePosition,
- findViewById(digitalWellbeingBannerId)!!,
- findViewById(showWindowViewId)!!,
- taskOverlayFactory,
- )
- }
+ ): TaskContainer =
+ traceSection("TaskView.createTaskContainer") {
+ val iconView = findViewById<View>(iconViewId) as TaskViewIcon
+ val taskContentView = findViewById<TaskContentView>(taskContentViewId)
+ return TaskContainer(
+ this,
+ task,
+ taskContentView,
+ taskContentView.findViewById(thumbnailViewId),
+ iconView,
+ TransformingTouchDelegate(iconView.asView()),
+ stagePosition,
+ findViewById(digitalWellbeingBannerId)!!,
+ findViewById(showWindowViewId)!!,
+ taskOverlayFactory,
+ )
+ }
fun containsMultipleTasks() = taskContainers.size > 1
@@ -927,11 +959,12 @@
/** Check if given `taskId` is tracked in this view */
fun containsTaskId(taskId: Int) = getTaskContainerById(taskId) != null
- open fun setOrientationState(orientationState: RecentsOrientedState) {
- this.orientedState = orientationState
- taskContainers.forEach { it.iconView.setIconOrientation(orientationState, isGridTask) }
- setThumbnailOrientation(orientationState)
- }
+ open fun setOrientationState(orientationState: RecentsOrientedState) =
+ traceSection("TaskView.setOrientationState") {
+ this.orientedState = orientationState
+ taskContainers.forEach { it.iconView.setIconOrientation(orientationState, isGridTask) }
+ setThumbnailOrientation(orientationState)
+ }
protected open fun setThumbnailOrientation(orientationState: RecentsOrientedState) {
taskContainers.forEach {
@@ -994,7 +1027,7 @@
protected open fun updateThumbnailSize() {
// TODO(b/271468547), we should default to setting translations only on the snapshot instead
// of a hybrid of both margins and translations
- firstTaskContainer?.snapshotView?.updateLayoutParams<LayoutParams> {
+ firstTaskContainer?.taskContentView?.updateLayoutParams<LayoutParams> {
topMargin = container.deviceProfile.overviewTaskThumbnailTopMarginPx
}
taskContainers.forEach { it.digitalWellBeingToast?.setupLayout() }
@@ -1008,11 +1041,11 @@
val thumbnailBounds = Rect()
if (relativeToDragLayer) {
container.dragLayer.getDescendantRectRelativeToSelf(
- it.snapshotView,
+ it.taskContentView,
thumbnailBounds,
)
} else {
- thumbnailBounds.set(it.snapshotView)
+ thumbnailBounds.set(it.taskContentView)
}
bounds.union(thumbnailBounds)
}
@@ -1078,25 +1111,27 @@
protected open fun needsUpdate(@TaskDataChanges dataChange: Int, @TaskDataChanges flag: Int) =
(dataChange and flag) == flag
- protected open fun cancelPendingLoadTasks() {
- pendingThumbnailLoadRequests.forEach { it.cancel() }
- pendingThumbnailLoadRequests.clear()
- pendingIconLoadRequests.forEach { it.cancel() }
- pendingIconLoadRequests.clear()
- }
+ protected open fun cancelPendingLoadTasks() =
+ traceSection("TaskView.cancelPendingLoadTasks") {
+ pendingThumbnailLoadRequests.forEach { it.cancel() }
+ pendingThumbnailLoadRequests.clear()
+ pendingIconLoadRequests.forEach { it.cancel() }
+ pendingIconLoadRequests.clear()
+ }
- protected open fun setIconState(container: TaskContainer, state: TaskData?) {
- if (enableOverviewIconMenu()) {
- if (state is TaskData.Data) {
- setIcon(container.iconView, state.icon)
- container.iconView.setText(state.title)
- container.digitalWellBeingToast?.initialize()
- } else {
- setIcon(container.iconView, null)
- container.iconView.setText(null)
+ protected open fun setIconState(container: TaskContainer, state: TaskData?) =
+ traceSection("TaskView.setIconState") {
+ if (enableOverviewIconMenu()) {
+ if (state is TaskData.Data) {
+ setIcon(container.iconView, state.icon)
+ container.iconView.setText(state.title)
+ container.digitalWellBeingToast?.initialize()
+ } else {
+ setIcon(container.iconView, null)
+ container.iconView.setText(null)
+ }
}
}
- }
protected open fun onIconLoaded(taskContainer: TaskContainer) {
setIcon(taskContainer.iconView, taskContainer.task.icon)
@@ -1176,15 +1211,14 @@
/** Launch of the current task (both live and inactive tasks) with an animation. */
fun launchWithAnimation(): RunnableList? {
return if (isRunningTask && recentsView?.remoteTargetHandles != null) {
- launchAsLiveTile()
+ launchAsLiveTile(recentsView?.remoteTargetHandles!!)
} else {
launchAsStaticTile()
}
}
- private fun launchAsLiveTile(): RunnableList? {
+ private fun launchAsLiveTile(remoteTargetHandles: Array<RemoteTargetHandle>): RunnableList? {
val recentsView = recentsView ?: return null
- val remoteTargetHandles = recentsView.remoteTargetHandles
if (!isClickableAsLiveTile) {
Log.e(
TAG,
@@ -1194,21 +1228,27 @@
}
isClickableAsLiveTile = false
val targets =
- if (remoteTargetHandles.size == 1) {
- remoteTargetHandles[0].transformParams.targetSet
+ if (remoteTargetHandles.isNotEmpty()) {
+ if (remoteTargetHandles.size == 1) {
+ remoteTargetHandles[0].transformParams.targetSet
+ } else {
+ val apps =
+ remoteTargetHandles.flatMap {
+ it.transformParams.targetSet.apps.asIterable()
+ }
+ val wallpapers =
+ remoteTargetHandles.flatMap {
+ it.transformParams.targetSet.wallpapers.asIterable()
+ }
+ RemoteAnimationTargets(
+ apps.toTypedArray(),
+ wallpapers.toTypedArray(),
+ remoteTargetHandles[0].transformParams.targetSet.nonApps,
+ remoteTargetHandles[0].transformParams.targetSet.targetMode,
+ )
+ }
} else {
- val apps =
- remoteTargetHandles.flatMap { it.transformParams.targetSet.apps.asIterable() }
- val wallpapers =
- remoteTargetHandles.flatMap {
- it.transformParams.targetSet.wallpapers.asIterable()
- }
- RemoteAnimationTargets(
- apps.toTypedArray(),
- wallpapers.toTypedArray(),
- remoteTargetHandles[0].transformParams.targetSet.nonApps,
- remoteTargetHandles[0].transformParams.targetSet.targetMode,
- )
+ null
}
if (targets == null) {
// If the recents animation is cancelled somehow between the parent if block and
@@ -1449,6 +1489,20 @@
return showTaskMenuWithContainer(menuContainer)
}
+ private fun closeTaskMenu(): Boolean {
+ val floatingView: AbstractFloatingView? =
+ AbstractFloatingView.getTopOpenViewWithType(
+ container,
+ AbstractFloatingView.TYPE_TASK_MENU,
+ )
+ if (floatingView?.isOpen == true) {
+ floatingView.close(true)
+ return true
+ } else {
+ return false
+ }
+ }
+
private fun showTaskMenuWithContainer(menuContainer: TaskContainer): Boolean {
val recentsView = recentsView ?: return false
if (enableHoverOfChildElementsInTaskview()) {
@@ -1456,12 +1510,16 @@
recentsView.setTaskBorderEnabled(false)
}
return if (enableOverviewIconMenu() && menuContainer.iconView is IconAppChipView) {
- menuContainer.iconView.revealAnim(/* isRevealing= */ true)
- TaskMenuView.showForTask(menuContainer) {
- val isAnimated = !recentsView.isSplitSelectionActive
- menuContainer.iconView.revealAnim(/* isRevealing= */ false, isAnimated)
- if (enableHoverOfChildElementsInTaskview()) {
- recentsView.setTaskBorderEnabled(true)
+ if (menuContainer.iconView.isExpanded) {
+ closeTaskMenu()
+ } else {
+ menuContainer.iconView.revealAnim(/* isRevealing= */ true)
+ TaskMenuView.showForTask(menuContainer) {
+ val isAnimated = !recentsView.isSplitSelectionActive
+ menuContainer.iconView.revealAnim(/* isRevealing= */ false, isAnimated)
+ if (enableHoverOfChildElementsInTaskview()) {
+ recentsView.setTaskBorderEnabled(true)
+ }
}
}
} else if (container.deviceProfile.isTablet) {
@@ -1649,7 +1707,7 @@
protected fun getScrollAdjustment(gridEnabled: Boolean) =
if (gridEnabled) gridTranslationX else nonGridTranslationX
- protected fun getOffsetAdjustment(gridEnabled: Boolean) = getScrollAdjustment(gridEnabled)
+ fun getOffsetAdjustment(gridEnabled: Boolean) = getScrollAdjustment(gridEnabled)
fun getSizeAdjustment(fullscreenEnabled: Boolean) = if (fullscreenEnabled) nonGridScale else 1f
@@ -1687,7 +1745,9 @@
protected open fun onFullscreenProgressChanged(fullscreenProgress: Float) {
taskContainers.forEach {
- it.iconView.setVisibility(if (fullscreenProgress < 1) VISIBLE else INVISIBLE)
+ if (!enableOverviewIconMenu()) {
+ it.iconView.setVisibility(if (fullscreenProgress < 1) VISIBLE else INVISIBLE)
+ }
it.overlay.setFullscreenProgress(fullscreenProgress)
}
settledProgressFullscreen =
@@ -1699,7 +1759,7 @@
updateFullscreenParams(thumbnailFullscreenParams)
taskContainers.forEach {
if (enableRefactorTaskThumbnail()) {
- it.thumbnailView.cornerRadius = thumbnailFullscreenParams.currentCornerRadius
+ it.taskContentView.cornerRadius = thumbnailFullscreenParams.currentCornerRadius
} else {
it.thumbnailViewDeprecated.setFullscreenParams(thumbnailFullscreenParams)
}
diff --git a/quickstep/src_protolog/com/android/launcher3/util/StateManagerProtoLogProxy.java b/quickstep/src_protolog/com/android/launcher3/util/StateManagerProtoLogProxy.java
index c319cb1..cb7254f 100644
--- a/quickstep/src_protolog/com/android/launcher3/util/StateManagerProtoLogProxy.java
+++ b/quickstep/src_protolog/com/android/launcher3/util/StateManagerProtoLogProxy.java
@@ -16,22 +16,25 @@
package com.android.launcher3.util;
-import static com.android.launcher3.Flags.enableStateManagerProtoLog;
import static com.android.quickstep.util.QuickstepProtoLogGroup.LAUNCHER_STATE_MANAGER;
import static com.android.quickstep.util.QuickstepProtoLogGroup.isProtoLogInitialized;
+import android.window.DesktopModeFlags.DesktopModeFlag;
+
import androidx.annotation.NonNull;
import com.android.internal.protolog.ProtoLog;
+import com.android.launcher3.Flags;
/**
* Proxy class used for StateManager ProtoLog support.
*/
public class StateManagerProtoLogProxy {
-
+ private static final DesktopModeFlag ENABLE_STATE_MANAGER_PROTO_LOG =
+ new DesktopModeFlag(Flags::enableStateManagerProtoLog, true);
public static void logGoToState(
@NonNull Object fromState, @NonNull Object toState, @NonNull String trace) {
- if (!enableStateManagerProtoLog() || !isProtoLogInitialized()) return;
+ if (!ENABLE_STATE_MANAGER_PROTO_LOG.isTrue() || !isProtoLogInitialized()) return;
ProtoLog.d(LAUNCHER_STATE_MANAGER,
"StateManager.goToState: fromState: %s, toState: %s, partial trace:\n%s",
fromState,
@@ -41,7 +44,7 @@
public static void logCreateAtomicAnimation(
@NonNull Object fromState, @NonNull Object toState, @NonNull String trace) {
- if (!enableStateManagerProtoLog() || !isProtoLogInitialized()) return;
+ if (!ENABLE_STATE_MANAGER_PROTO_LOG.isTrue() || !isProtoLogInitialized()) return;
ProtoLog.d(LAUNCHER_STATE_MANAGER, "StateManager.createAtomicAnimation: "
+ "fromState: %s, toState: %s, partial trace:\n%s",
fromState,
@@ -50,17 +53,17 @@
}
public static void logOnStateTransitionStart(@NonNull Object state) {
- if (!enableStateManagerProtoLog() || !isProtoLogInitialized()) return;
+ if (!ENABLE_STATE_MANAGER_PROTO_LOG.isTrue() || !isProtoLogInitialized()) return;
ProtoLog.d(LAUNCHER_STATE_MANAGER, "StateManager.onStateTransitionStart: state: %s", state);
}
public static void logOnStateTransitionEnd(@NonNull Object state) {
- if (!enableStateManagerProtoLog() || !isProtoLogInitialized()) return;
+ if (!ENABLE_STATE_MANAGER_PROTO_LOG.isTrue() || !isProtoLogInitialized()) return;
ProtoLog.d(LAUNCHER_STATE_MANAGER, "StateManager.onStateTransitionEnd: state: %s", state);
}
public static void logCancelAnimation(boolean animationOngoing, @NonNull String trace) {
- if (!enableStateManagerProtoLog() || !isProtoLogInitialized()) return;
+ if (!ENABLE_STATE_MANAGER_PROTO_LOG.isTrue() || !isProtoLogInitialized()) return;
ProtoLog.d(LAUNCHER_STATE_MANAGER,
"StateManager.cancelAnimation: animation ongoing: %b, partial trace:\n%s",
animationOngoing,
diff --git a/quickstep/src_protolog/com/android/quickstep/util/ActiveGestureProtoLogProxy.java b/quickstep/src_protolog/com/android/quickstep/util/ActiveGestureProtoLogProxy.java
index 37c64cf..18a5338 100644
--- a/quickstep/src_protolog/com/android/quickstep/util/ActiveGestureProtoLogProxy.java
+++ b/quickstep/src_protolog/com/android/quickstep/util/ActiveGestureProtoLogProxy.java
@@ -159,31 +159,37 @@
ProtoLog.d(ACTIVE_GESTURE_LOG, "cleanUpRecentsAnimation");
}
- public static void logOnInputEventUserLocked() {
- ActiveGestureLog.INSTANCE.addLog(
- "TIS.onInputEvent: Cannot process input event: user is locked");
+ public static void logOnInputEventUserLocked(int displayId) {
+ ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
+ "TIS.onInputEvent(displayId=%d): Cannot process input event: user is locked",
+ displayId));
if (!enableActiveGestureProtoLog() || !isProtoLogInitialized()) return;
ProtoLog.d(ACTIVE_GESTURE_LOG,
- "TIS.onInputEvent: Cannot process input event: user is locked");
+ "TIS.onInputEvent(displayId=%d): Cannot process input event: user is locked",
+ displayId);
}
- public static void logOnInputIgnoringFollowingEvents() {
- ActiveGestureLog.INSTANCE.addLog("TIS.onMotionEvent: A new gesture has been started, "
+ public static void logOnInputIgnoringFollowingEvents(int displayId) {
+ ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
+ "TIS.onMotionEvent(displayId=%d): A new gesture has been started, "
+ "but a previously-requested recents animation hasn't started. "
- + "Ignoring all following motion events.",
+ + "Ignoring all following motion events.", displayId),
RECENTS_ANIMATION_START_PENDING);
if (!enableActiveGestureProtoLog() || !isProtoLogInitialized()) return;
- ProtoLog.d(ACTIVE_GESTURE_LOG, "TIS.onMotionEvent: A new gesture has been started, "
- + "but a previously-requested recents animation hasn't started. "
- + "Ignoring all following motion events.");
+ ProtoLog.d(ACTIVE_GESTURE_LOG,
+ "TIS.onMotionEvent(displayId=%d): A new gesture has been started, "
+ + "but a previously-requested recents animation hasn't started. "
+ + "Ignoring all following motion events.", displayId);
}
- public static void logOnInputEventThreeButtonNav() {
- ActiveGestureLog.INSTANCE.addLog("TIS.onInputEvent: Cannot process input event: "
- + "using 3-button nav and event is not a trackpad event");
+ public static void logOnInputEventThreeButtonNav(int displayId) {
+ ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
+ "TIS.onInputEvent(displayId=%d): Cannot process input event: "
+ + "using 3-button nav and event is not a trackpad event", displayId));
if (!enableActiveGestureProtoLog() || !isProtoLogInitialized()) return;
- ProtoLog.d(ACTIVE_GESTURE_LOG, "TIS.onInputEvent: Cannot process input event: "
- + "using 3-button nav and event is not a trackpad event");
+ ProtoLog.d(ACTIVE_GESTURE_LOG,
+ "TIS.onInputEvent(displayId=%d): Cannot process input event: "
+ + "using 3-button nav and event is not a trackpad event", displayId);
}
public static void logPreloadRecentsAnimation() {
@@ -322,61 +328,84 @@
}
public static void logOnInputEventActionUp(
- int x, int y, int action, @NonNull String classification) {
+ int x, int y, int action, @NonNull String classification, int displayId) {
String actionString = MotionEvent.actionToString(action);
ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
- "onMotionEvent(%d, %d): %s, %s", x, y, actionString, classification),
+ "onMotionEvent(%d, %d): %s, %s, displayId=%d",
+ x,
+ y,
+ actionString,
+ classification,
+ displayId),
/* gestureEvent= */ action == ACTION_DOWN
? MOTION_DOWN
: MOTION_UP);
if (!enableActiveGestureProtoLog() || !isProtoLogInitialized()) return;
ProtoLog.d(ACTIVE_GESTURE_LOG,
- "onMotionEvent(%d, %d): %s, %s", x, y, actionString, classification);
+ "onMotionEvent(%d, %d): %s, %s, displayId=%d",
+ x,
+ y,
+ actionString,
+ classification,
+ displayId);
}
public static void logOnInputEventActionMove(
- @NonNull String action, @NonNull String classification, int pointerCount) {
+ @NonNull String action,
+ @NonNull String classification,
+ int pointerCount,
+ int displayId) {
ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
- "onMotionEvent: %s, %s, pointerCount: %d",
+ "onMotionEvent: %s, %s, pointerCount: %d, displayId=%d",
action,
classification,
- pointerCount),
+ pointerCount,
+ displayId),
MOTION_MOVE);
if (!enableActiveGestureProtoLog() || !isProtoLogInitialized()) return;
ProtoLog.d(ACTIVE_GESTURE_LOG,
- "onMotionEvent: %s, %s, pointerCount: %d", action, classification, pointerCount);
+ "onMotionEvent: %s, %s, pointerCount: %d, displayId=%d",
+ action,
+ classification,
+ pointerCount,
+ displayId);
}
public static void logOnInputEventGenericAction(
- @NonNull String action, @NonNull String classification) {
+ @NonNull String action, @NonNull String classification, int displayId) {
ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
- "onMotionEvent: %s, %s", action, classification));
+ "onMotionEvent: %s, %s, displayId=%d", action, classification, displayId));
if (!enableActiveGestureProtoLog() || !isProtoLogInitialized()) return;
- ProtoLog.d(ACTIVE_GESTURE_LOG, "onMotionEvent: %s, %s", action, classification);
+ ProtoLog.d(ACTIVE_GESTURE_LOG,
+ "onMotionEvent: %s, %s, displayId=%d", action, classification, displayId);
}
public static void logOnInputEventNavModeSwitched(
- @NonNull String startNavMode, @NonNull String currentNavMode) {
+ int displayId, @NonNull String startNavMode, @NonNull String currentNavMode) {
ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
- "TIS.onInputEvent: Navigation mode switched mid-gesture (%s -> %s); "
+ "TIS.onInputEvent(displayId=%d): Navigation mode switched mid-gesture (%s -> %s); "
+ "cancelling gesture.",
+ displayId,
startNavMode,
currentNavMode),
NAVIGATION_MODE_SWITCHED);
if (!enableActiveGestureProtoLog() || !isProtoLogInitialized()) return;
ProtoLog.d(ACTIVE_GESTURE_LOG,
- "TIS.onInputEvent: Navigation mode switched mid-gesture (%s -> %s); "
+ "TIS.onInputEvent(displayId=%d): Navigation mode switched mid-gesture (%s -> %s); "
+ "cancelling gesture.",
+ displayId,
startNavMode,
currentNavMode);
}
- public static void logUnknownInputEvent(@NonNull String event) {
+ public static void logUnknownInputEvent(int displayId, @NonNull String event) {
ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
- "TIS.onInputEvent: Cannot process input event: received unknown event %s", event));
+ "TIS.onInputEvent(displayId=%d): Cannot process input event: "
+ + "received unknown event %s", displayId, event));
if (!enableActiveGestureProtoLog() || !isProtoLogInitialized()) return;
ProtoLog.d(ACTIVE_GESTURE_LOG,
- "TIS.onInputEvent: Cannot process input event: received unknown event %s", event);
+ "TIS.onInputEvent(displayId=%d): Cannot process input event: "
+ + "received unknown event %s", displayId, event);
}
public static void logFinishRunningRecentsAnimation(boolean toHome) {
@@ -433,11 +462,13 @@
ProtoLog.d(ACTIVE_GESTURE_LOG, "Launching side task id=%d", taskId);
}
- public static void logOnInputEventActionDown(@NonNull ActiveGestureLog.CompoundString reason) {
+ public static void logOnInputEventActionDown(
+ int displayId, @NonNull ActiveGestureLog.CompoundString reason) {
ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
- "TIS.onMotionEvent: ").append(reason));
+ "TIS.onMotionEvent(displayId=%d): ", displayId).append(reason));
if (!enableActiveGestureProtoLog() || !isProtoLogInitialized()) return;
- ProtoLog.d(ACTIVE_GESTURE_LOG, "TIS.onMotionEvent: %s", reason.toString());
+ ProtoLog.d(ACTIVE_GESTURE_LOG,
+ "TIS.onMotionEvent(displayId=%d): %s", displayId, reason.toString());
}
public static void logStartNewTask(@NonNull ActiveGestureLog.CompoundString tasks) {
diff --git a/quickstep/src_protolog/com/android/quickstep/util/RecentsWindowProtoLogProxy.java b/quickstep/src_protolog/com/android/quickstep/util/RecentsWindowProtoLogProxy.java
index 2c9ae33..99888fb 100644
--- a/quickstep/src_protolog/com/android/quickstep/util/RecentsWindowProtoLogProxy.java
+++ b/quickstep/src_protolog/com/android/quickstep/util/RecentsWindowProtoLogProxy.java
@@ -16,14 +16,16 @@
package com.android.quickstep.util;
-import static com.android.launcher3.Flags.enableRecentsWindowProtoLog;
import static com.android.quickstep.util.QuickstepProtoLogGroup.RECENTS_WINDOW;
import static com.android.quickstep.util.QuickstepProtoLogGroup.isProtoLogInitialized;
+import android.window.DesktopModeFlags;
+
import androidx.annotation.NonNull;
import com.android.internal.protolog.ProtoLog;
import com.android.internal.protolog.common.IProtoLogGroup;
+import com.android.launcher3.Flags;
/**
* Proxy class used for Recents Window ProtoLog support.
@@ -35,19 +37,20 @@
* method. Or, if an existing entry needs to be modified, simply update it here.
*/
public class RecentsWindowProtoLogProxy {
-
+ private static final DesktopModeFlags.DesktopModeFlag ENABLE_RECENTS_WINDOW_PROTO_LOG =
+ new DesktopModeFlags.DesktopModeFlag(Flags::enableRecentsWindowProtoLog, true);
public static void logOnStateSetStart(@NonNull String stateName) {
- if (!enableRecentsWindowProtoLog() || !isProtoLogInitialized()) return;
+ if (!ENABLE_RECENTS_WINDOW_PROTO_LOG.isTrue() || !isProtoLogInitialized()) return;
ProtoLog.d(RECENTS_WINDOW, "onStateSetStart: %s", stateName);
}
public static void logOnStateSetEnd(@NonNull String stateName) {
- if (!enableRecentsWindowProtoLog() || !isProtoLogInitialized()) return;
+ if (!ENABLE_RECENTS_WINDOW_PROTO_LOG.isTrue() || !isProtoLogInitialized()) return;
ProtoLog.d(RECENTS_WINDOW, "onStateSetEnd: %s", stateName);
}
public static void logStartRecentsWindow(boolean isShown, boolean windowViewIsNull) {
- if (!enableRecentsWindowProtoLog() || !isProtoLogInitialized()) return;
+ if (!ENABLE_RECENTS_WINDOW_PROTO_LOG.isTrue() || !isProtoLogInitialized()) return;
ProtoLog.d(RECENTS_WINDOW,
"Starting recents window: isShow= %b, windowViewIsNull=%b",
isShown,
@@ -55,7 +58,7 @@
}
public static void logCleanup(boolean isShown) {
- if (!enableRecentsWindowProtoLog() || !isProtoLogInitialized()) return;
+ if (!ENABLE_RECENTS_WINDOW_PROTO_LOG.isTrue() || !isProtoLogInitialized()) return;
ProtoLog.d(RECENTS_WINDOW, "Cleaning up recents window: isShow= %b", isShown);
}
}
diff --git a/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/SplashHelper.kt b/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/SplashHelper.kt
new file mode 100644
index 0000000..8cc09d4
--- /dev/null
+++ b/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/SplashHelper.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.task.thumbnail
+
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Paint
+
+object SplashHelper {
+ private val BITMAP_RECT_COLORS = listOf(Color.GREEN, Color.RED, Color.BLUE, Color.CYAN)
+
+ fun createSplash(): Bitmap = createBitmap(width = 20, height = 20, rectColorRotation = 1)
+
+ fun createBitmap(width: Int, height: Int, rectColorRotation: Int = 0): Bitmap =
+ Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).apply {
+ Canvas(this).apply {
+ val paint = Paint()
+ paint.color = BITMAP_RECT_COLORS[rectColorRotation % 4]
+ drawRect(0f, 0f, width / 2f, height / 2f, paint)
+ paint.color = BITMAP_RECT_COLORS[(1 + rectColorRotation) % 4]
+ drawRect(width / 2f, 0f, width.toFloat(), height / 2f, paint)
+ paint.color = BITMAP_RECT_COLORS[(2 + rectColorRotation) % 4]
+ drawRect(0f, height / 2f, width / 2f, height.toFloat(), paint)
+ paint.color = BITMAP_RECT_COLORS[(3 + rectColorRotation) % 4]
+ drawRect(width / 2f, height / 2f, width.toFloat(), height.toFloat(), paint)
+ }
+ }
+}
diff --git a/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskContentViewScreenshotTest.kt b/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskContentViewScreenshotTest.kt
new file mode 100644
index 0000000..7b1e445
--- /dev/null
+++ b/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskContentViewScreenshotTest.kt
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.task.thumbnail
+
+import android.content.Context
+import android.graphics.Color
+import android.graphics.drawable.BitmapDrawable
+import android.platform.test.flag.junit.SetFlagsRule
+import android.view.LayoutInflater
+import com.android.launcher3.Flags
+import com.android.launcher3.R
+import com.android.launcher3.util.rule.setFlags
+import com.android.quickstep.task.thumbnail.SplashHelper.createSplash
+import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.BackgroundOnly
+import com.google.android.apps.nexuslauncher.imagecomparison.goldenpathmanager.ViewScreenshotGoldenPathManager
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+import platform.test.screenshot.DeviceEmulationSpec
+import platform.test.screenshot.Displays
+import platform.test.screenshot.ViewScreenshotTestRule
+import platform.test.screenshot.getEmulatedDevicePathConfig
+
+/** Screenshot tests for [TaskContentView]. */
+@RunWith(ParameterizedAndroidJunit4::class)
+class TaskContentViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
+
+ @get:Rule(order = 0) val setFlagsRule = SetFlagsRule()
+
+ @get:Rule(order = 1)
+ val screenshotRule =
+ ViewScreenshotTestRule(
+ emulationSpec,
+ ViewScreenshotGoldenPathManager(getEmulatedDevicePathConfig(emulationSpec)),
+ )
+
+ @Before
+ fun setUp() {
+ setFlagsRule.setFlags(true, Flags.FLAG_ENABLE_REFACTOR_TASK_THUMBNAIL)
+ }
+
+ @Test
+ fun taskContentView_recyclesToUninitialized() {
+ screenshotRule.screenshotTest("taskContentView_uninitialized") { activity ->
+ activity.actionBar?.hide()
+ val taskContentView = createTaskContentView(activity)
+ taskContentView.setState(
+ TaskHeaderUiState.HideHeader,
+ BackgroundOnly(Color.YELLOW),
+ null,
+ )
+ taskContentView.onRecycle()
+ taskContentView
+ }
+ }
+
+ @Test
+ fun taskContentView_shows_thumbnail_and_header() {
+ screenshotRule.screenshotTest("taskContentView_shows_thumbnail_and_header") { activity ->
+ activity.actionBar?.hide()
+ createTaskContentView(activity).apply {
+ setState(
+ TaskHeaderUiState.ShowHeader(
+ TaskHeaderUiState.ThumbnailHeader(
+ BitmapDrawable(activity.resources, createSplash()),
+ "test",
+ ) {}
+ ),
+ BackgroundOnly(Color.YELLOW),
+ null,
+ )
+ }
+ }
+ }
+
+ @Test
+ fun taskContentView_scaled_roundRoundedCorners() {
+ screenshotRule.screenshotTest("taskContentView_scaledRoundedCorners") { activity ->
+ activity.actionBar?.hide()
+ createTaskContentView(activity).apply {
+ scaleX = 0.75f
+ scaleY = 0.3f
+ setState(TaskHeaderUiState.HideHeader, BackgroundOnly(Color.YELLOW), null)
+ }
+ }
+ }
+
+ private fun createTaskContentView(context: Context): TaskContentView {
+ val taskContentView =
+ LayoutInflater.from(context).inflate(R.layout.task_content_view, null, false)
+ as TaskContentView
+ taskContentView.cornerRadius = CORNER_RADIUS
+ return taskContentView
+ }
+
+ companion object {
+ @Parameters(name = "{0}")
+ @JvmStatic
+ fun getTestSpecs() =
+ DeviceEmulationSpec.forDisplays(
+ Displays.Phone,
+ isDarkTheme = false,
+ isLandscape = false,
+ )
+
+ const val CORNER_RADIUS = 56f
+ }
+}
diff --git a/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskHeaderViewScreenshotTest.kt b/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskHeaderViewScreenshotTest.kt
new file mode 100644
index 0000000..e30554e
--- /dev/null
+++ b/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskHeaderViewScreenshotTest.kt
@@ -0,0 +1,80 @@
+/*
+ * 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.task.thumbnail
+
+import android.content.Context
+import android.graphics.drawable.BitmapDrawable
+import android.view.LayoutInflater
+import com.android.launcher3.R
+import com.android.quickstep.task.thumbnail.SplashHelper.createSplash
+import com.android.quickstep.views.TaskHeaderView
+import com.google.android.apps.nexuslauncher.imagecomparison.goldenpathmanager.ViewScreenshotGoldenPathManager
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+import platform.test.screenshot.DeviceEmulationSpec
+import platform.test.screenshot.Displays
+import platform.test.screenshot.ViewScreenshotTestRule
+import platform.test.screenshot.getEmulatedDevicePathConfig
+
+/** Screenshot tests for [TaskHeaderView]. */
+@RunWith(ParameterizedAndroidJunit4::class)
+class TaskHeaderViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
+ @get:Rule
+ val screenshotRule =
+ ViewScreenshotTestRule(
+ emulationSpec,
+ ViewScreenshotGoldenPathManager(getEmulatedDevicePathConfig(emulationSpec)),
+ )
+
+ @Test
+ fun taskHeaderView_showHeader() {
+ screenshotRule.screenshotTest("taskHeaderView_showHeader") { activity ->
+ activity.actionBar?.hide()
+ createTaskHeaderView(activity).apply {
+ setState(
+ TaskHeaderUiState.ShowHeader(
+ TaskHeaderUiState.ThumbnailHeader(
+ BitmapDrawable(activity.resources, createSplash()),
+ "Example",
+ ) {}
+ )
+ )
+ }
+ }
+ }
+
+ private fun createTaskHeaderView(context: Context): TaskHeaderView {
+ val taskHeaderView =
+ LayoutInflater.from(context).inflate(R.layout.task_header_view, null, false)
+ as TaskHeaderView
+ return taskHeaderView
+ }
+
+ companion object {
+ @Parameters(name = "{0}")
+ @JvmStatic
+ fun getTestSpecs() =
+ DeviceEmulationSpec.forDisplays(
+ Displays.Tablet,
+ isDarkTheme = false,
+ isLandscape = true,
+ )
+ }
+}
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 80b2c16..45df735 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
@@ -16,16 +16,14 @@
package com.android.quickstep.task.thumbnail
import android.content.Context
-import android.graphics.Bitmap
-import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Matrix
-import android.graphics.Paint
import android.graphics.drawable.BitmapDrawable
import android.view.LayoutInflater
import android.view.Surface.ROTATION_0
-import androidx.core.graphics.set
import com.android.launcher3.R
+import com.android.quickstep.task.thumbnail.SplashHelper.createBitmap
+import com.android.quickstep.task.thumbnail.SplashHelper.createSplash
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.BackgroundOnly
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Snapshot
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.SnapshotSplash
@@ -90,23 +88,25 @@
}
@Test
- fun taskThumbnailView_liveTile_withoutHeader() {
+ fun taskThumbnailView_liveTile() {
screenshotRule.screenshotTest("taskThumbnailView_liveTile") { activity ->
activity.actionBar?.hide()
- createTaskThumbnailView(activity).apply {
- setState(TaskThumbnailUiState.LiveTile.WithoutHeader)
- }
+ createTaskThumbnailView(activity).apply { setState(TaskThumbnailUiState.LiveTile) }
}
}
@Test
- fun taskThumbnailView_image_withoutHeader() {
+ fun taskThumbnailView_image() {
screenshotRule.screenshotTest("taskThumbnailView_image") { activity ->
activity.actionBar?.hide()
createTaskThumbnailView(activity).apply {
setState(
SnapshotSplash(
- Snapshot.WithoutHeader(createBitmap(), ROTATION_0, Color.DKGRAY),
+ Snapshot(
+ createBitmap(VIEW_ENV_WIDTH, VIEW_ENV_HEIGHT),
+ ROTATION_0,
+ Color.DKGRAY,
+ ),
null,
)
)
@@ -115,14 +115,14 @@
}
@Test
- fun taskThumbnailView_image_withoutHeader_withImageMatrix() {
+ fun taskThumbnailView_image_withImageMatrix() {
screenshotRule.screenshotTest("taskThumbnailView_image_withMatrix") { activity ->
activity.actionBar?.hide()
createTaskThumbnailView(activity).apply {
val lessThanHeightMatchingAspectRatio = (VIEW_ENV_HEIGHT / 2) - 200
setState(
SnapshotSplash(
- Snapshot.WithoutHeader(
+ Snapshot(
createBitmap(
width = VIEW_ENV_WIDTH / 2,
height = lessThanHeightMatchingAspectRatio,
@@ -139,13 +139,17 @@
}
@Test
- fun taskThumbnailView_splash_withoutHeader() {
+ fun taskThumbnailView_splash() {
screenshotRule.screenshotTest("taskThumbnailView_partial_splash") { activity ->
activity.actionBar?.hide()
createTaskThumbnailView(activity).apply {
setState(
SnapshotSplash(
- Snapshot.WithoutHeader(createBitmap(), ROTATION_0, Color.DKGRAY),
+ Snapshot(
+ createBitmap(VIEW_ENV_WIDTH, VIEW_ENV_HEIGHT),
+ ROTATION_0,
+ Color.DKGRAY,
+ ),
BitmapDrawable(activity.resources, createSplash()),
)
)
@@ -155,14 +159,14 @@
}
@Test
- fun taskThumbnailView_splash_withoutHeader_withImageMatrix() {
+ fun taskThumbnailView_splash_withImageMatrix() {
screenshotRule.screenshotTest("taskThumbnailView_partial_splash_withMatrix") { activity ->
activity.actionBar?.hide()
createTaskThumbnailView(activity).apply {
val lessThanHeightMatchingAspectRatio = (VIEW_ENV_HEIGHT / 2) - 200
setState(
SnapshotSplash(
- Snapshot.WithoutHeader(
+ Snapshot(
createBitmap(
width = VIEW_ENV_WIDTH / 2,
height = lessThanHeightMatchingAspectRatio,
@@ -229,31 +233,9 @@
val taskThumbnailView =
LayoutInflater.from(context).inflate(R.layout.task_thumbnail, null, false)
as TaskThumbnailView
- taskThumbnailView.cornerRadius = CORNER_RADIUS
return taskThumbnailView
}
- private fun createSplash() = createBitmap(width = 20, height = 20, rectColorRotation = 1)
-
- private fun createBitmap(
- width: Int = VIEW_ENV_WIDTH,
- height: Int = VIEW_ENV_HEIGHT,
- rectColorRotation: Int = 0,
- ) =
- Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).apply {
- Canvas(this).apply {
- val paint = Paint()
- paint.color = BITMAP_RECT_COLORS[rectColorRotation % 4]
- drawRect(0f, 0f, width / 2f, height / 2f, paint)
- paint.color = BITMAP_RECT_COLORS[(1 + rectColorRotation) % 4]
- drawRect(width / 2f, 0f, width.toFloat(), height / 2f, paint)
- paint.color = BITMAP_RECT_COLORS[(2 + rectColorRotation) % 4]
- drawRect(0f, height / 2f, width / 2f, height.toFloat(), paint)
- paint.color = BITMAP_RECT_COLORS[(3 + rectColorRotation) % 4]
- drawRect(width / 2f, height / 2f, width.toFloat(), height.toFloat(), paint)
- }
- }
-
companion object {
@Parameters(name = "{0}")
@JvmStatic
@@ -264,8 +246,6 @@
isLandscape = false,
)
- const val CORNER_RADIUS = 56f
- val BITMAP_RECT_COLORS = listOf(Color.GREEN, Color.RED, Color.BLUE, Color.CYAN)
const val VIEW_ENV_WIDTH = 1440
const val VIEW_ENV_HEIGHT = 3120
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/model/QuickstepModelDelegateTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/model/QuickstepModelDelegateTest.kt
index 09c62aa..1c7af14 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/model/QuickstepModelDelegateTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/model/QuickstepModelDelegateTest.kt
@@ -64,7 +64,7 @@
underTest.mHotseatState.predictor = hotseatPredictor
underTest.mWidgetsRecommendationState.predictor = widgetRecommendationPredictor
underTest.mModel = modelHelper.model
- underTest.mDataModel = BgDataModel()
+ underTest.mDataModel = BgDataModel(WidgetsModel(modelHelper.sandboxContext))
}
@After
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/model/WidgetsPredictionsRequesterTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/model/WidgetsPredictionsRequesterTest.kt
index 4ea74df..d445189 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/model/WidgetsPredictionsRequesterTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/model/WidgetsPredictionsRequesterTest.kt
@@ -16,6 +16,8 @@
package com.android.launcher3.model
+import android.app.prediction.AppPredictionManager
+import android.app.prediction.AppPredictor
import android.app.prediction.AppTarget
import android.app.prediction.AppTargetEvent
import android.app.prediction.AppTargetId
@@ -36,9 +38,15 @@
import com.android.launcher3.model.WidgetPredictionsRequester.notOnUiSurfaceFilter
import com.android.launcher3.util.ActivityContextWrapper
import com.android.launcher3.util.ComponentKey
+import com.android.launcher3.util.Executors
+import com.android.launcher3.util.Executors.MODEL_EXECUTOR
+import com.android.launcher3.util.TestUtil
import com.android.launcher3.util.WidgetUtils.createAppWidgetProviderInfo
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo
+import com.android.launcher3.widget.PendingAddWidgetInfo
import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
import java.util.function.Predicate
import junit.framework.Assert.assertNotNull
import org.junit.Before
@@ -46,6 +54,9 @@
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
class WidgetsPredictionsRequesterTest {
@@ -67,11 +78,26 @@
@Mock private lateinit var iconCache: IconCache
+ @Mock private lateinit var apmMock: AppPredictionManager
+
+ @Mock private lateinit var predictorMock: AppPredictor
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
mUserHandle = myUserHandle()
- context = ActivityContextWrapper(ApplicationProvider.getApplicationContext())
+
+ whenever(apmMock.createAppPredictionSession(any())).thenReturn(predictorMock)
+
+ context =
+ object : ActivityContextWrapper(ApplicationProvider.getApplicationContext()) {
+ override fun getSystemService(name: String): Any? {
+ if (name == "app_prediction") {
+ return apmMock
+ }
+ return super.getSystemService(name)
+ }
+ }
testInvariantProfile = LauncherAppState.getIDP(context)
deviceProfile = testInvariantProfile.getDeviceProfile(context).copy(context)
@@ -114,22 +140,68 @@
buildExpectedAppTargetEvent(
/*pkg=*/ APP_1_PACKAGE_NAME,
/*providerClassName=*/ APP_1_PROVIDER_A_CLASS_NAME,
- /*user=*/ mUserHandle
+ /*user=*/ mUserHandle,
),
buildExpectedAppTargetEvent(
/*pkg=*/ APP_1_PACKAGE_NAME,
/*providerClassName=*/ APP_1_PROVIDER_B_CLASS_NAME,
- /*user=*/ mUserHandle
+ /*user=*/ mUserHandle,
),
buildExpectedAppTargetEvent(
/*pkg=*/ APP_2_PACKAGE_NAME,
/*providerClassName=*/ APP_2_PROVIDER_1_CLASS_NAME,
- /*user=*/ mUserHandle
- )
+ /*user=*/ mUserHandle,
+ ),
)
}
@Test
+ fun request_invokesCallbackWithPredictedItems() {
+ TestUtil.runOnExecutorSync(MODEL_EXECUTOR) {
+ val underTest = WidgetPredictionsRequester(context, TEST_UI_SURFACE, allWidgets)
+ val existingWidgets = arrayListOf(widget1aInfo, widget1bInfo)
+ val predictions =
+ listOf(
+ // (existing) already on surface
+ AppTarget(
+ AppTargetId(APP_1_PACKAGE_NAME),
+ APP_1_PACKAGE_NAME,
+ APP_1_PROVIDER_B_CLASS_NAME,
+ mUserHandle,
+ ),
+ // eligible
+ AppTarget(
+ AppTargetId(APP_2_PACKAGE_NAME),
+ APP_2_PACKAGE_NAME,
+ APP_2_PROVIDER_1_CLASS_NAME,
+ mUserHandle,
+ ),
+ )
+ doAnswer {
+ underTest.onTargetsAvailable(predictions)
+ null
+ }
+ .whenever(predictorMock)
+ .requestPredictionUpdate()
+ val testCountDownLatch = CountDownLatch(1)
+ val listener =
+ WidgetPredictionsRequester.WidgetPredictionsListener { itemInfos ->
+ if (itemInfos.size == 1 && itemInfos[0] is PendingAddWidgetInfo) {
+ // only one item was eligible.
+ testCountDownLatch.countDown()
+ } else {
+ println("Unexpected prediction items found: ${itemInfos.size}")
+ }
+ }
+
+ underTest.request(existingWidgets, listener)
+ TestUtil.runOnExecutorSync(Executors.MAIN_EXECUTOR) {}
+
+ assertThat(testCountDownLatch.await(TEST_TIMEOUT, TimeUnit.SECONDS)).isTrue()
+ }
+ }
+
+ @Test
fun filterPredictions_notOnUiSurfaceFilter_returnsOnlyEligiblePredictions() {
val widgetsAlreadyOnSurface = arrayListOf(widget1bInfo)
val filter: Predicate<WidgetItem> = notOnUiSurfaceFilter(widgetsAlreadyOnSurface)
@@ -141,15 +213,15 @@
AppTargetId(APP_1_PACKAGE_NAME),
APP_1_PACKAGE_NAME,
APP_1_PROVIDER_B_CLASS_NAME,
- mUserHandle
+ mUserHandle,
),
// eligible
AppTarget(
AppTargetId(APP_2_PACKAGE_NAME),
APP_2_PACKAGE_NAME,
APP_2_PROVIDER_1_CLASS_NAME,
- mUserHandle
- )
+ mUserHandle,
+ ),
)
// only 2 was eligible
@@ -167,27 +239,27 @@
AppTargetId(APP_1_PACKAGE_NAME),
APP_1_PACKAGE_NAME,
"$APP_1_PACKAGE_NAME.SomeActivity",
- mUserHandle
+ mUserHandle,
),
AppTarget(
AppTargetId(APP_2_PACKAGE_NAME),
APP_2_PACKAGE_NAME,
"$APP_2_PACKAGE_NAME.SomeActivity2",
- mUserHandle
+ mUserHandle,
),
)
assertThat(filterPredictions(predictions, allWidgets, filter)).isEmpty()
}
- private fun createWidgetItem(
- providerInfo: AppWidgetProviderInfo,
- ): WidgetItem {
+ private fun createWidgetItem(providerInfo: AppWidgetProviderInfo): WidgetItem {
val widgetInfo = LauncherAppWidgetProviderInfo.fromProviderInfo(context, providerInfo)
return WidgetItem(widgetInfo, testInvariantProfile, iconCache, context)
}
companion object {
+ const val TEST_TIMEOUT = 3L
+
const val TEST_UI_SURFACE = "widgets_test"
const val BUNDLE_KEY_ADDED_APP_WIDGETS = "added_app_widgets"
@@ -203,13 +275,13 @@
private fun buildExpectedAppTargetEvent(
pkg: String,
providerClassName: String,
- userHandle: UserHandle
+ userHandle: UserHandle,
): AppTargetEvent {
val appTarget =
AppTarget.Builder(
/*id=*/ AppTargetId("widget:$pkg"),
/*packageName=*/ pkg,
- /*user=*/ userHandle
+ /*user=*/ userHandle,
)
.setClassName(providerClassName)
.build()
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/model/data/TaskViewItemInfoTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/model/data/TaskViewItemInfoTest.kt
index 8d20ba8..d2abed8 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/model/data/TaskViewItemInfoTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/model/data/TaskViewItemInfoTest.kt
@@ -35,6 +35,7 @@
import com.android.quickstep.TaskOverlayFactory
import com.android.quickstep.TaskOverlayFactory.TaskOverlay
import com.android.quickstep.recents.di.RecentsDependencies
+import com.android.quickstep.task.thumbnail.TaskContentView
import com.android.quickstep.task.thumbnail.TaskThumbnailView
import com.android.quickstep.views.RecentsView
import com.android.quickstep.views.TaskContainer
@@ -76,12 +77,12 @@
context.initDaggerComponent(
DaggerTaskViewItemInfoTest_TestComponent.builder().bindUserCache(userCache)
)
- RecentsDependencies.initialize(context)
+ RecentsDependencies.maybeInitialize(context)
}
@After
fun tearDown() {
- RecentsDependencies.destroy()
+ RecentsDependencies.destroy(context)
}
@Test
@@ -198,6 +199,7 @@
return TaskContainer(
taskView,
task,
+ mock<TaskContentView>(),
if (enableRefactorTaskThumbnail()) mock<TaskThumbnailView>()
else mock<TaskThumbnailViewDeprecated>(),
mock<TaskViewIcon>(),
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt
index 26f1197..52d288a 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt
@@ -17,6 +17,7 @@
import com.android.launcher3.taskbar.allapps.TaskbarAllAppsController
import com.android.launcher3.taskbar.bubbles.BubbleControllers
+import com.android.launcher3.taskbar.growth.NudgeController
import com.android.launcher3.taskbar.overlay.TaskbarOverlayController
import com.android.systemui.shared.rotation.RotationButtonController
import java.util.Optional
@@ -58,6 +59,7 @@
@Mock lateinit var taskbarPinningController: TaskbarPinningController
@Mock lateinit var optionalBubbleControllers: Optional<BubbleControllers>
@Mock lateinit var taskbarDesktopModeController: TaskbarDesktopModeController
+ @Mock lateinit var nudgeController: NudgeController
lateinit var taskbarControllers: TaskbarControllers
@@ -100,6 +102,7 @@
taskbarPinningController,
optionalBubbleControllers,
taskbarDesktopModeController,
+ nudgeController,
)
}
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarPopupControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarPopupControllerTest.kt
new file mode 100644
index 0000000..6bb3205
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarPopupControllerTest.kt
@@ -0,0 +1,101 @@
+/*
+ * 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.taskbar
+
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.flag.junit.SetFlagsRule
+import com.android.launcher3.AbstractFloatingView
+import com.android.launcher3.BubbleTextView
+import com.android.launcher3.Flags.FLAG_ENABLE_MULTI_INSTANCE_MENU_TASKBAR
+import com.android.launcher3.Flags.FLAG_ENABLE_PINNING_APP_WITH_CONTEXT_MENU
+import com.android.launcher3.R
+import com.android.launcher3.model.data.WorkspaceItemInfo
+import com.android.launcher3.taskbar.TaskbarControllerTestUtil.runOnMainSync
+import com.android.launcher3.taskbar.TaskbarViewTestUtil.createHotseatWorkspaceItem
+import com.android.launcher3.taskbar.TaskbarViewTestUtil.createRecents
+import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule
+import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.InjectController
+import com.android.launcher3.taskbar.rules.TaskbarWindowSandboxContext
+import com.android.launcher3.util.LauncherMultivalentJUnit
+import com.android.launcher3.util.LauncherMultivalentJUnit.EmulatedDevices
+import com.android.quickstep.util.GroupTask
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(LauncherMultivalentJUnit::class)
+@EmulatedDevices(["pixelFoldable2023", "pixelTablet2023"])
+@DisableFlags(FLAG_ENABLE_MULTI_INSTANCE_MENU_TASKBAR, FLAG_ENABLE_PINNING_APP_WITH_CONTEXT_MENU)
+class TaskbarPopupControllerTest {
+ @get:Rule(order = 0) val setFlagsRule = SetFlagsRule()
+
+ @get:Rule(order = 1) val context = TaskbarWindowSandboxContext.create()
+
+ @get:Rule(order = 2) val taskbarUnitTestRule = TaskbarUnitTestRule(this, context)
+
+ @InjectController lateinit var popupController: TaskbarPopupController
+
+ private val taskbarContext: TaskbarActivityContext
+ get() = taskbarUnitTestRule.activityContext
+
+ private lateinit var taskbarView: TaskbarView
+ private lateinit var hotseatIcon: BubbleTextView
+ private lateinit var recentTaskIcon: BubbleTextView
+
+ @Before
+ fun setup() {
+ taskbarContext.controllers.uiController.init(taskbarContext.controllers)
+ runOnMainSync { taskbarView = taskbarContext.dragLayer.findViewById(R.id.taskbar_view) }
+
+ val hotseatItems = arrayOf(createHotseatWorkspaceItem())
+ val recentItems = createRecents(2)
+ runOnMainSync {
+ taskbarView.updateItems(hotseatItems, recentItems)
+ hotseatIcon =
+ taskbarView.iconViews.filterIsInstance<BubbleTextView>().first {
+ it.tag is WorkspaceItemInfo
+ }
+ recentTaskIcon =
+ taskbarView.iconViews.filterIsInstance<BubbleTextView>().first {
+ it.tag is GroupTask
+ }
+ }
+ }
+
+ @Test
+ fun showForIcon_hotseatItem() {
+ assertThat(hasPopupMenu()).isFalse()
+ runOnMainSync { popupController.showForIcon(hotseatIcon) }
+ assertThat(hasPopupMenu()).isTrue()
+ }
+
+ @Test
+ fun showForIcon_recentTask() {
+ assertThat(hasPopupMenu()).isFalse()
+ runOnMainSync { popupController.showForIcon(recentTaskIcon) }
+ assertThat(hasPopupMenu()).isTrue()
+ }
+
+ private fun hasPopupMenu(): Boolean {
+ return AbstractFloatingView.hasOpenView(
+ taskbarContext,
+ AbstractFloatingView.TYPE_ACTION_POPUP,
+ )
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarViewTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarViewTest.kt
index 44d31c4..24ed81f 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarViewTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarViewTest.kt
@@ -19,7 +19,6 @@
import android.platform.test.flag.junit.FlagsParameterization
import android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf
import android.platform.test.flag.junit.SetFlagsRule
-import com.android.launcher3.Flags.FLAG_TASKBAR_RECENTS_LAYOUT_TRANSITION
import com.android.launcher3.R
import com.android.launcher3.taskbar.TaskbarControllerTestUtil.runOnMainSync
import com.android.launcher3.taskbar.TaskbarIconType.ALL_APPS
@@ -34,6 +33,7 @@
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.ForceRtl
import com.android.launcher3.taskbar.rules.TaskbarWindowSandboxContext
import com.android.launcher3.util.LauncherMultivalentJUnit.Companion.isRunningInRobolectric
+import com.android.window.flags.Flags.FLAG_ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -54,7 +54,7 @@
} else {
listOf("onDevice") // Unused.
}
- val flags = allCombinationsOf(FLAG_TASKBAR_RECENTS_LAYOUT_TRANSITION)
+ val flags = allCombinationsOf(FLAG_ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION)
return devices.flatMap { d -> flags.map { f -> arrayOf(d, f) } } // Cartesian product.
}
}
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 df70b10..e52aacf 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarViewTestUtil.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarViewTestUtil.kt
@@ -46,12 +46,14 @@
/** Creates an array of fake hotseat items. */
fun createHotseatItems(size: Int): Array<ItemInfo> {
- return Array(size) {
- WorkspaceItemInfo(
- AppInfo(TEST_COMPONENT, "Test App $it", Process.myUserHandle(), Intent())
- )
- .apply { id = it }
- }
+ return Array(size) { createHotseatWorkspaceItem(it) }
+ }
+
+ fun createHotseatWorkspaceItem(id: Int = 0): WorkspaceItemInfo {
+ return WorkspaceItemInfo(
+ AppInfo(TEST_COMPONENT, "Test App $id", Process.myUserHandle(), Intent())
+ )
+ .apply { this.id = id }
}
/** Creates a list of fake recent tasks. */
@@ -75,13 +77,13 @@
}
/** A `Truth` [Subject] with extensions for verifying [TaskbarView]. */
-class TaskbarViewSubject(failureMetadata: FailureMetadata, private val view: TaskbarView) :
+class TaskbarViewSubject(failureMetadata: FailureMetadata, private val view: TaskbarView?) :
Subject(failureMetadata, view) {
/** Verifies that the types of icons match [expectedTypes] in order. */
fun hasIconTypes(vararg expectedTypes: TaskbarIconType) {
val actualTypes =
- view.iconViews.map {
+ view?.iconViews?.map {
when (it) {
view.allAppsButtonContainer -> ALL_APPS
view.taskbarDividerViewContainer -> DIVIDER
@@ -100,7 +102,7 @@
/** 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).flatMap {
+ view?.iconViews?.slice(startIndex..<startIndex + expectedIds.size)?.flatMap {
assertThat(it.tag).isInstanceOf(GroupTask::class.java)
(it.tag as GroupTask).tasks.map { task -> task.key.id }
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarViewWithLayoutTransitionTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarViewWithLayoutTransitionTest.kt
index 4b6d5e5..2df4fab 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarViewWithLayoutTransitionTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarViewWithLayoutTransitionTest.kt
@@ -19,12 +19,14 @@
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
import android.view.View
-import com.android.launcher3.Flags.FLAG_TASKBAR_RECENTS_LAYOUT_TRANSITION
+import com.android.launcher3.Flags.FLAG_TASKBAR_OVERFLOW
import com.android.launcher3.R
+import com.android.launcher3.statehandlers.DesktopVisibilityController
import com.android.launcher3.taskbar.TaskbarControllerTestUtil.runOnMainSync
import com.android.launcher3.taskbar.TaskbarIconType.ALL_APPS
import com.android.launcher3.taskbar.TaskbarIconType.DIVIDER
import com.android.launcher3.taskbar.TaskbarIconType.HOTSEAT
+import com.android.launcher3.taskbar.TaskbarIconType.OVERFLOW
import com.android.launcher3.taskbar.TaskbarIconType.RECENT
import com.android.launcher3.taskbar.TaskbarViewTestUtil.assertThat
import com.android.launcher3.taskbar.TaskbarViewTestUtil.createHotseatItems
@@ -34,15 +36,17 @@
import com.android.launcher3.taskbar.rules.TaskbarWindowSandboxContext
import com.android.launcher3.util.LauncherMultivalentJUnit
import com.android.launcher3.util.LauncherMultivalentJUnit.EmulatedDevices
+import com.android.window.flags.Flags.FLAG_ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.whenever
@RunWith(LauncherMultivalentJUnit::class)
@EmulatedDevices(["pixelFoldable2023", "pixelTablet2023"])
-@EnableFlags(FLAG_TASKBAR_RECENTS_LAYOUT_TRANSITION)
+@EnableFlags(FLAG_ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION, FLAG_TASKBAR_OVERFLOW)
class TaskbarViewWithLayoutTransitionTest {
@get:Rule(order = 0) val setFlagsRule = SetFlagsRule()
@@ -54,6 +58,12 @@
private val iconViews: Array<View>
get() = taskbarView.iconViews
+ private val desktopVisibilityController: DesktopVisibilityController
+ get() = DesktopVisibilityController.INSTANCE[context]
+
+ private val maxShownRecents: Int
+ get() = taskbarView.maxNumIconViews - 2 // Account for All Apps and Divider.
+
@Before
fun obtainView() {
taskbarView = taskbarUnitTestRule.activityContext.dragLayer.findViewById(R.id.taskbar_view)
@@ -225,4 +235,118 @@
assertThat(expectedViewToRemove in iconViews).isFalse()
}
}
+
+ @Test
+ fun testUpdateItems_desktopMode_hotseatItem_noDivider() {
+ whenever(desktopVisibilityController.isInDesktopMode(context.displayId)).thenReturn(true)
+ runOnMainSync { taskbarView.updateItems(createHotseatItems(1), emptyList()) }
+ assertThat(taskbarView).hasIconTypes(ALL_APPS, HOTSEAT)
+ }
+
+ @Test
+ @ForceRtl
+ fun testUpdateItems_rtlAndDesktopMode_hotseatItem_noDivider() {
+ whenever(desktopVisibilityController.isInDesktopMode(context.displayId)).thenReturn(true)
+ runOnMainSync { taskbarView.updateItems(createHotseatItems(1), emptyList()) }
+ assertThat(taskbarView).hasIconTypes(HOTSEAT, ALL_APPS)
+ }
+
+ @Test
+ fun testUpdateItems_desktopMode_recentItem_hasDivider() {
+ whenever(desktopVisibilityController.isInDesktopMode(context.displayId)).thenReturn(true)
+ runOnMainSync { taskbarView.updateItems(emptyArray(), createRecents(1)) }
+ assertThat(taskbarView).hasIconTypes(ALL_APPS, DIVIDER, RECENT)
+ }
+
+ @Test
+ @ForceRtl
+ fun testUpdateItems_rtlAndDesktopMode_recentItem_hasDivider() {
+ whenever(desktopVisibilityController.isInDesktopMode(context.displayId)).thenReturn(true)
+ runOnMainSync { taskbarView.updateItems(emptyArray(), createRecents(1)) }
+ assertThat(taskbarView).hasIconTypes(RECENT, DIVIDER, ALL_APPS)
+ }
+
+ @Test
+ fun testUpdateItems_maxRecents_noOverflow() {
+ runOnMainSync { taskbarView.updateItems(emptyArray(), createRecents(maxShownRecents)) }
+ assertThat(taskbarView).hasIconTypes(ALL_APPS, DIVIDER, *RECENT * maxShownRecents)
+ }
+
+ @Test
+ fun testUpdateItems_moreThanMaxRecents_overflowShownBeforeRecents() {
+ val recentsSize = maxShownRecents + 2
+ runOnMainSync { taskbarView.updateItems(emptyArray(), createRecents(recentsSize)) }
+
+ val expectedNumRecents = RECENT * getExpectedNumRecentsWithOverflow()
+ assertThat(taskbarView).hasIconTypes(ALL_APPS, DIVIDER, OVERFLOW, *expectedNumRecents)
+ }
+
+ @Test
+ @ForceRtl
+ fun testUpdateItems_rtl_moreThanMaxRecents_overflowShownAfterRecents() {
+ val recentsSize = maxShownRecents + 2
+ runOnMainSync { taskbarView.updateItems(emptyArray(), createRecents(recentsSize)) }
+
+ val expectedRecents = RECENT * getExpectedNumRecentsWithOverflow()
+ assertThat(taskbarView).hasIconTypes(*expectedRecents, OVERFLOW, DIVIDER, ALL_APPS)
+ }
+
+ @Test
+ fun testUpdateItems_moreThanMaxRecentsWithHotseat_fewerRecentsShown() {
+ val hotseatSize = 4
+ val recentsSize = maxShownRecents + 2
+ runOnMainSync {
+ taskbarView.updateItems(createHotseatItems(hotseatSize), createRecents(recentsSize))
+ }
+
+ val expectedRecents = RECENT * getExpectedNumRecentsWithOverflow(hotseatSize)
+ assertThat(taskbarView)
+ .hasIconTypes(ALL_APPS, *HOTSEAT * hotseatSize, DIVIDER, OVERFLOW, *expectedRecents)
+ }
+
+ @Test
+ @ForceRtl
+ fun testUpdateItems_rtl_moreThanMaxRecentsWithHotseat_fewerRecentsShown() {
+ val hotseatSize = 4
+ val recentsSize = maxShownRecents + 2
+ runOnMainSync {
+ taskbarView.updateItems(createHotseatItems(hotseatSize), createRecents(recentsSize))
+ }
+
+ val expectedRecents = RECENT * getExpectedNumRecentsWithOverflow(hotseatSize)
+ assertThat(taskbarView)
+ .hasIconTypes(*expectedRecents, OVERFLOW, DIVIDER, *HOTSEAT * hotseatSize, ALL_APPS)
+ }
+
+ @Test
+ fun testUpdateItems_moreThanMaxRecents_verifyShownRecentsOrder() {
+ val recentsSize = maxShownRecents + 2
+ runOnMainSync { taskbarView.updateItems(emptyArray(), createRecents(recentsSize)) }
+
+ val expectedNumRecents = getExpectedNumRecentsWithOverflow()
+ assertThat(taskbarView)
+ .hasRecentsOrder(
+ startIndex = iconViews.size - expectedNumRecents,
+ expectedIds = ((recentsSize - expectedNumRecents)..<recentsSize).toList(),
+ )
+ }
+
+ @Test
+ @ForceRtl
+ fun testUpdateItems_rtl_moreThanMaxRecents_verifyShownRecentsReversed() {
+ val recentsSize = maxShownRecents + 2
+ runOnMainSync { taskbarView.updateItems(emptyArray(), createRecents(recentsSize)) }
+
+ val expectedNumRecents = getExpectedNumRecentsWithOverflow()
+ assertThat(taskbarView)
+ .hasRecentsOrder(
+ startIndex = 0,
+ expectedIds = ((recentsSize - expectedNumRecents)..<recentsSize).toList().reversed(),
+ )
+ }
+
+ /** Returns the number of expected recents outside of the overflow based on [hotseatSize]. */
+ private fun getExpectedNumRecentsWithOverflow(hotseatSize: Int = 0): Int {
+ return maxShownRecents - hotseatSize - 1 // Account for overflow.
+ }
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
index a456fb9..61a6975 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
@@ -20,8 +20,10 @@
import android.graphics.Color
import android.graphics.Path
import android.graphics.PointF
+import android.graphics.Rect
import android.graphics.drawable.ColorDrawable
import android.view.LayoutInflater
+import android.view.MotionEvent
import android.view.View
import android.view.View.INVISIBLE
import android.view.View.VISIBLE
@@ -35,10 +37,13 @@
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.launcher3.R
+import com.android.launcher3.taskbar.TaskbarInsetsController
import com.android.launcher3.taskbar.bubbles.BubbleBarBubble
import com.android.launcher3.taskbar.bubbles.BubbleBarOverflow
import com.android.launcher3.taskbar.bubbles.BubbleBarParentViewHeightUpdateNotifier
import com.android.launcher3.taskbar.bubbles.BubbleBarView
+import com.android.launcher3.taskbar.bubbles.BubbleBarViewController
+import com.android.launcher3.taskbar.bubbles.BubbleStashedHandleViewController
import com.android.launcher3.taskbar.bubbles.BubbleView
import com.android.launcher3.taskbar.bubbles.flyout.BubbleBarFlyoutController
import com.android.launcher3.taskbar.bubbles.flyout.BubbleBarFlyoutMessage
@@ -46,8 +51,10 @@
import com.android.launcher3.taskbar.bubbles.flyout.FlyoutCallbacks
import com.android.launcher3.taskbar.bubbles.flyout.FlyoutScheduler
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController
+import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.BubbleLauncherState
import com.android.wm.shell.shared.animation.PhysicsAnimator
import com.android.wm.shell.shared.animation.PhysicsAnimatorTestUtils
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation
import com.android.wm.shell.shared.bubbles.BubbleInfo
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.Semaphore
@@ -56,13 +63,6 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.kotlin.any
-import org.mockito.kotlin.atLeastOnce
-import org.mockito.kotlin.eq
-import org.mockito.kotlin.mock
-import org.mockito.kotlin.never
-import org.mockito.kotlin.verify
-import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -78,7 +78,7 @@
private lateinit var bubble: BubbleBarBubble
private lateinit var bubbleBarView: BubbleBarView
private lateinit var flyoutContainer: FrameLayout
- private lateinit var bubbleStashController: BubbleStashController
+ private lateinit var bubbleStashController: FakeBubbleStashController
private lateinit var flyoutController: BubbleBarFlyoutController
private val emptyRunnable = Runnable {}
@@ -89,6 +89,7 @@
fun setUp() {
animatorScheduler = TestBubbleBarViewAnimatorScheduler()
bubbleBarParentViewController = TestBubbleBarParentViewHeightUpdateNotifier()
+ bubbleStashController = FakeBubbleStashController()
PhysicsAnimatorTestUtils.prepareForTest()
setupFlyoutController()
}
@@ -96,11 +97,10 @@
@Test
fun animateBubbleInForStashed() {
setUpBubbleBar()
- setUpBubbleStashController()
val handle = View(context)
val handleAnimator = PhysicsAnimator.getInstance(handle)
- whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
+ bubbleStashController.handleAnimator = handleAnimator
val animator =
BubbleBarViewAnimator(
@@ -147,17 +147,16 @@
assertThat(handle.translationY).isEqualTo(0)
assertThat(bubbleBarView.alpha).isEqualTo(0)
assertThat(animator.isAnimating).isFalse()
- verify(bubbleStashController).stashBubbleBarImmediate()
+ assertThat(bubbleStashController.isStashed).isTrue()
}
@Test
fun animateBubbleInForStashed_tapAnimatingBubble() {
setUpBubbleBar()
- setUpBubbleStashController()
val handle = View(context)
val handleAnimator = PhysicsAnimator.getInstance(handle)
- whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
+ bubbleStashController.handleAnimator = handleAnimator
val animator =
BubbleBarViewAnimator(
@@ -187,7 +186,7 @@
assertThat(bubbleBarView.translationY).isEqualTo(BAR_TRANSLATION_Y_FOR_TASKBAR)
assertThat(animator.isAnimating).isTrue()
- verify(bubbleStashController, atLeastOnce()).updateTaskbarTouchRegion()
+ assertThat(bubbleStashController.taskbarTouchRegionUpdated).isTrue()
assertThat(bubbleBarParentViewController.timesInvoked).isEqualTo(1)
waitForFlyoutToShow()
@@ -211,11 +210,10 @@
@Test
fun animateBubbleInForStashed_touchTaskbarArea_whileShowing() {
setUpBubbleBar()
- setUpBubbleStashController()
val handle = View(context)
val handleAnimator = PhysicsAnimator.getInstance(handle)
- whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
+ bubbleStashController.handleAnimator = handleAnimator
val animator =
BubbleBarViewAnimator(
@@ -241,7 +239,6 @@
// verify the hide bubble animation is pending
assertThat(animatorScheduler.delayedBlock).isNotNull()
- whenever(bubbleStashController.isStashed).thenReturn(true)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
animator.onStashStateChangingWhileAnimating()
}
@@ -255,9 +252,10 @@
// verify that the hide animation was canceled
assertThat(animatorScheduler.delayedBlock).isNull()
assertThat(animator.isAnimating).isFalse()
+ assertThat(bubbleStashController.animationInterrupted).isTrue()
assertThat(bubbleBarView.scaleX).isEqualTo(1)
assertThat(bubbleBarView.scaleY).isEqualTo(1)
- verify(bubbleStashController).onNewBubbleAnimationInterrupted(eq(true), any())
+ assertThat(bubbleStashController.isStashed).isTrue()
// PhysicsAnimatorTestUtils posts the cancellation to the main thread so we need to wait
// again
@@ -268,11 +266,10 @@
@Test
fun animateBubbleInForStashed_touchTaskbarArea_whileHiding() {
setUpBubbleBar()
- setUpBubbleStashController()
val handle = View(context)
val handleAnimator = PhysicsAnimator.getInstance(handle)
- whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
+ bubbleStashController.handleAnimator = handleAnimator
val animator =
BubbleBarViewAnimator(
@@ -310,7 +307,7 @@
}
assertThat(bubbleBarParentViewController.timesInvoked).isEqualTo(2)
assertThat(animator.isAnimating).isFalse()
- verify(bubbleStashController).onNewBubbleAnimationInterrupted(any(), any())
+ assertThat(bubbleStashController.animationInterrupted).isTrue()
// PhysicsAnimatorTestUtils posts the cancellation to the main thread so we need to wait
// again
@@ -321,11 +318,10 @@
@Test
fun animateBubbleInForStashed_showAnimationCanceled() {
setUpBubbleBar()
- setUpBubbleStashController()
val handle = View(context)
val handleAnimator = PhysicsAnimator.getInstance(handle)
- whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
+ bubbleStashController.handleAnimator = handleAnimator
val animator =
BubbleBarViewAnimator(
@@ -359,11 +355,10 @@
@Test
fun animateBubbleInForStashed_autoExpanding() {
setUpBubbleBar()
- setUpBubbleStashController()
val handle = View(context)
val handleAnimator = PhysicsAnimator.getInstance(handle)
- whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
+ bubbleStashController.handleAnimator = handleAnimator
var notifiedExpanded = false
val onExpanded = Runnable { notifiedExpanded = true }
@@ -399,18 +394,17 @@
// verify there is no hide animation
assertThat(animatorScheduler.delayedBlock).isNull()
- verify(bubbleStashController).showBubbleBarImmediate()
+ assertThat(bubbleStashController.isStashed).isFalse()
assertThat(notifiedExpanded).isTrue()
}
@Test
fun animateBubbleInForStashed_expandedWhileAnimatingIn() {
setUpBubbleBar()
- setUpBubbleStashController()
val handle = View(context)
val handleAnimator = PhysicsAnimator.getInstance(handle)
- whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
+ bubbleStashController.handleAnimator = handleAnimator
var notifiedExpanded = false
val onExpanded = Runnable { notifiedExpanded = true }
@@ -459,11 +453,10 @@
@Test
fun animateBubbleInForStashed_expandedWhileFullyIn() {
setUpBubbleBar()
- setUpBubbleStashController()
val handle = View(context)
val handleAnimator = PhysicsAnimator.getInstance(handle)
- whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
+ bubbleStashController.handleAnimator = handleAnimator
var notifiedExpanded = false
val onExpanded = Runnable { notifiedExpanded = true }
@@ -515,13 +508,11 @@
@Test
fun animateToInitialState_inApp() {
setUpBubbleBar()
- setUpBubbleStashController()
- whenever(bubbleStashController.bubbleBarTranslationY)
- .thenReturn(BAR_TRANSLATION_Y_FOR_TASKBAR)
+ bubbleStashController.launcherState = BubbleLauncherState.IN_APP
val handle = View(context)
val handleAnimator = PhysicsAnimator.getInstance(handle)
- whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
+ bubbleStashController.handleAnimator = handleAnimator
val barAnimator = PhysicsAnimator.getInstance(bubbleBarView)
@@ -570,19 +561,17 @@
assertThat(bubbleBarView.visibility).isEqualTo(VISIBLE)
assertThat(notifiedBubbleBarVisible).isTrue()
- verify(bubbleStashController).stashBubbleBarImmediate()
+ assertThat(bubbleStashController.isStashed).isTrue()
}
@Test
fun animateToInitialState_whileDragging_inApp() {
setUpBubbleBar()
- setUpBubbleStashController()
- whenever(bubbleStashController.bubbleBarTranslationY)
- .thenReturn(BAR_TRANSLATION_Y_FOR_TASKBAR)
+ bubbleStashController.launcherState = BubbleLauncherState.IN_APP
val handle = View(context)
val handleAnimator = PhysicsAnimator.getInstance(handle)
- whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
+ bubbleStashController.handleAnimator = handleAnimator
val barAnimator = PhysicsAnimator.getInstance(bubbleBarView)
@@ -634,20 +623,16 @@
assertThat(handle.translationY).isEqualTo(0)
assertThat(bubbleBarView.visibility).isEqualTo(VISIBLE)
assertThat(notifiedBubbleBarVisible).isTrue()
-
- verify(bubbleStashController, never()).stashBubbleBarImmediate()
}
@Test
fun animateToInitialState_inApp_autoExpanding() {
setUpBubbleBar()
- setUpBubbleStashController()
- whenever(bubbleStashController.bubbleBarTranslationY)
- .thenReturn(BAR_TRANSLATION_Y_FOR_TASKBAR)
+ bubbleStashController.launcherState = BubbleLauncherState.IN_APP
val handle = View(context)
val handleAnimator = PhysicsAnimator.getInstance(handle)
- whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
+ bubbleStashController.handleAnimator = handleAnimator
val barAnimator = PhysicsAnimator.getInstance(bubbleBarView)
@@ -677,16 +662,14 @@
assertThat(bubbleBarView.translationY).isEqualTo(BAR_TRANSLATION_Y_FOR_TASKBAR)
assertThat(animatorScheduler.delayedBlock).isNull()
- verify(bubbleStashController).showBubbleBarImmediate()
+ assertThat(bubbleStashController.isStashed).isFalse()
assertThat(notifiedExpanded).isTrue()
}
@Test
fun animateToInitialState_inHome() {
setUpBubbleBar()
- setUpBubbleStashController()
- whenever(bubbleStashController.bubbleBarTranslationY)
- .thenReturn(BAR_TRANSLATION_Y_FOR_HOTSEAT)
+ bubbleStashController.launcherState = BubbleLauncherState.HOME
val barAnimator = PhysicsAnimator.getInstance(bubbleBarView)
@@ -725,15 +708,13 @@
assertThat(bubbleBarView.alpha).isEqualTo(1)
assertThat(bubbleBarView.translationY).isEqualTo(BAR_TRANSLATION_Y_FOR_HOTSEAT)
- verify(bubbleStashController).showBubbleBarImmediate()
+ assertThat(bubbleStashController.isStashed).isFalse()
}
@Test
fun animateToInitialState_expandedWhileAnimatingIn() {
setUpBubbleBar()
- setUpBubbleStashController()
- whenever(bubbleStashController.bubbleBarTranslationY)
- .thenReturn(BAR_TRANSLATION_Y_FOR_HOTSEAT)
+ bubbleStashController.launcherState = BubbleLauncherState.HOME
var notifiedExpanded = false
val onExpanded = Runnable { notifiedExpanded = true }
@@ -775,16 +756,14 @@
verifyBubbleBarIsExpandedWithTranslation(BAR_TRANSLATION_Y_FOR_HOTSEAT)
assertThat(animator.isAnimating).isFalse()
- verify(bubbleStashController).showBubbleBarImmediate()
+ assertThat(bubbleStashController.isStashed).isFalse()
assertThat(notifiedExpanded).isTrue()
}
@Test
fun animateToInitialState_expandedWhileFullyIn() {
setUpBubbleBar()
- setUpBubbleStashController()
- whenever(bubbleStashController.bubbleBarTranslationY)
- .thenReturn(BAR_TRANSLATION_Y_FOR_HOTSEAT)
+ bubbleStashController.launcherState = BubbleLauncherState.HOME
var notifiedExpanded = false
val onExpanded = Runnable { notifiedExpanded = true }
@@ -831,9 +810,7 @@
@Test
fun animateBubbleBarForCollapsed() {
setUpBubbleBar()
- setUpBubbleStashController()
- whenever(bubbleStashController.bubbleBarTranslationY)
- .thenReturn(BAR_TRANSLATION_Y_FOR_HOTSEAT)
+ bubbleStashController.launcherState = BubbleLauncherState.HOME
val barAnimator = PhysicsAnimator.getInstance(bubbleBarView)
@@ -877,16 +854,13 @@
assertThat(bubbleBarParentViewController.timesInvoked).isEqualTo(2)
// the bubble bar translation y should be back to its initial value
assertThat(bubbleBarView.translationY).isEqualTo(BAR_TRANSLATION_Y_FOR_HOTSEAT)
-
- verify(bubbleStashController).showBubbleBarImmediate()
+ assertThat(bubbleStashController.isStashed).isFalse()
}
@Test
fun animateBubbleBarForCollapsed_autoExpanding() {
setUpBubbleBar()
- setUpBubbleStashController()
- whenever(bubbleStashController.bubbleBarTranslationY)
- .thenReturn(BAR_TRANSLATION_Y_FOR_HOTSEAT)
+ bubbleStashController.launcherState = BubbleLauncherState.HOME
val semaphore = Semaphore(0)
var notifiedExpanded = false
@@ -933,16 +907,14 @@
assertThat(animator.isAnimating).isFalse()
assertThat(bubbleBarView.translationY).isEqualTo(BAR_TRANSLATION_Y_FOR_HOTSEAT)
- verify(bubbleStashController).showBubbleBarImmediate()
+ assertThat(bubbleStashController.isStashed).isFalse()
assertThat(notifiedExpanded).isTrue()
}
@Test
fun animateBubbleBarForCollapsed_expandingWhileAnimatingIn() {
setUpBubbleBar()
- setUpBubbleStashController()
- whenever(bubbleStashController.bubbleBarTranslationY)
- .thenReturn(BAR_TRANSLATION_Y_FOR_HOTSEAT)
+ bubbleStashController.launcherState = BubbleLauncherState.HOME
val semaphore = Semaphore(0)
var notifiedExpanded = false
@@ -1001,16 +973,14 @@
assertThat(animator.isAnimating).isFalse()
assertThat(bubbleBarView.translationY).isEqualTo(BAR_TRANSLATION_Y_FOR_HOTSEAT)
assertThat(bubbleBarView.isExpanded).isTrue()
- verify(bubbleStashController).showBubbleBarImmediate()
+ assertThat(bubbleStashController.isStashed).isFalse()
assertThat(notifiedExpanded).isTrue()
}
@Test
fun animateBubbleBarForCollapsed_expandingWhileFullyIn() {
setUpBubbleBar()
- setUpBubbleStashController()
- whenever(bubbleStashController.bubbleBarTranslationY)
- .thenReturn(BAR_TRANSLATION_Y_FOR_HOTSEAT)
+ bubbleStashController.launcherState = BubbleLauncherState.HOME
val barAnimator = PhysicsAnimator.getInstance(bubbleBarView)
@@ -1064,18 +1034,17 @@
assertThat(animator.isAnimating).isFalse()
assertThat(bubbleBarView.translationY).isEqualTo(BAR_TRANSLATION_Y_FOR_HOTSEAT)
assertThat(bubbleBarView.isExpanded).isTrue()
- verify(bubbleStashController).showBubbleBarImmediate()
+ assertThat(bubbleStashController.isStashed).isFalse()
assertThat(notifiedExpanded).isTrue()
}
@Test
fun interruptAnimation_whileAnimatingIn() {
setUpBubbleBar()
- setUpBubbleStashController()
val handle = View(context)
val handleAnimator = PhysicsAnimator.getInstance(handle)
- whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
+ bubbleStashController.handleAnimator = handleAnimator
val animator =
BubbleBarViewAnimator(
@@ -1136,17 +1105,16 @@
assertThat(handle.translationY).isEqualTo(0)
assertThat(bubbleBarView.alpha).isEqualTo(0)
assertThat(animator.isAnimating).isFalse()
- verify(bubbleStashController).stashBubbleBarImmediate()
+ assertThat(bubbleStashController.isStashed).isTrue()
}
@Test
fun interruptAnimation_whileIn() {
setUpBubbleBar()
- setUpBubbleStashController()
val handle = View(context)
val handleAnimator = PhysicsAnimator.getInstance(handle)
- whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
+ bubbleStashController.handleAnimator = handleAnimator
val animator =
BubbleBarViewAnimator(
@@ -1213,17 +1181,16 @@
assertThat(handle.translationY).isEqualTo(0)
assertThat(bubbleBarView.alpha).isEqualTo(0)
assertThat(animator.isAnimating).isFalse()
- verify(bubbleStashController).stashBubbleBarImmediate()
+ assertThat(bubbleStashController.isStashed).isTrue()
}
@Test
fun interruptAnimation_whileAnimatingOut_whileCollapsingFlyout() {
setUpBubbleBar()
- setUpBubbleStashController()
val handle = View(context)
val handleAnimator = PhysicsAnimator.getInstance(handle)
- whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
+ bubbleStashController.handleAnimator = handleAnimator
val animator =
BubbleBarViewAnimator(
@@ -1301,17 +1268,16 @@
assertThat(handle.translationY).isEqualTo(0)
assertThat(bubbleBarView.alpha).isEqualTo(0)
assertThat(animator.isAnimating).isFalse()
- verify(bubbleStashController).stashBubbleBarImmediate()
+ assertThat(bubbleStashController.isStashed).isTrue()
}
@Test
fun interruptAnimation_whileAnimatingOut_barToHandle() {
setUpBubbleBar()
- setUpBubbleStashController()
val handle = View(context)
val handleAnimator = PhysicsAnimator.getInstance(handle)
- whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
+ bubbleStashController.handleAnimator = handleAnimator
val animator =
BubbleBarViewAnimator(
@@ -1416,17 +1382,16 @@
assertThat(handle.translationY).isEqualTo(0)
assertThat(bubbleBarView.alpha).isEqualTo(0)
assertThat(animator.isAnimating).isFalse()
- verify(bubbleStashController).stashBubbleBarImmediate()
+ assertThat(bubbleStashController.isStashed).isTrue()
}
@Test
fun interruptForIme() {
setUpBubbleBar()
- setUpBubbleStashController()
val handle = View(context)
val handleAnimator = PhysicsAnimator.getInstance(handle)
- whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
+ bubbleStashController.handleAnimator = handleAnimator
val animator =
BubbleBarViewAnimator(
@@ -1457,7 +1422,8 @@
// verify that the hide animation was canceled
assertThat(animatorScheduler.delayedBlock).isNull()
assertThat(animator.isAnimating).isFalse()
- verify(bubbleStashController).onNewBubbleAnimationInterrupted(eq(true), any())
+ assertThat(bubbleStashController.animationInterrupted).isTrue()
+ assertThat(bubbleStashController.isStashed).isTrue()
// PhysicsAnimatorTestUtils posts the cancellation to the main thread so we need to wait
// again
@@ -1510,17 +1476,6 @@
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
}
- private fun setUpBubbleStashController() {
- bubbleStashController = mock<BubbleStashController>()
- whenever(bubbleStashController.isStashed).thenReturn(true)
- whenever(bubbleStashController.getDiffBetweenHandleAndBarCenters())
- .thenReturn(DIFF_BETWEEN_HANDLE_AND_BAR_CENTERS)
- whenever(bubbleStashController.getStashedHandleTranslationForNewBubbleAnimation())
- .thenReturn(HANDLE_TRANSLATION)
- whenever(bubbleStashController.bubbleBarTranslationYForTaskbar)
- .thenReturn(BAR_TRANSLATION_Y_FOR_TASKBAR)
- }
-
private fun setupFlyoutController() {
flyoutContainer = FrameLayout(context)
val flyoutPositioner =
@@ -1627,6 +1582,91 @@
timesInvoked++
}
}
+
+ private class FakeBubbleStashController : BubbleStashController {
+
+ var handleAnimator: PhysicsAnimator<View>? = null
+ var taskbarTouchRegionUpdated = false
+ private set
+
+ var animationInterrupted = false
+ private set
+
+ private var _isStashed = true
+
+ override var launcherState = BubbleLauncherState.HOME
+ override val isStashed: Boolean
+ get() = _isStashed
+
+ override var bubbleBarVerticalCenterForHome = 0
+ override var isSysuiLocked = false
+ override val isTransientTaskBar = true
+ override val hasHandleView = true
+ override val bubbleBarTranslationYForTaskbar = BAR_TRANSLATION_Y_FOR_TASKBAR
+ override val bubbleBarTranslationYForHotseat = BAR_TRANSLATION_Y_FOR_HOTSEAT
+ override var inAppDisplayOverrideProgress = 0f
+
+ override fun init(
+ taskbarInsetsController: TaskbarInsetsController,
+ bubbleBarViewController: BubbleBarViewController,
+ bubbleStashedHandleViewController: BubbleStashedHandleViewController?,
+ controllersAfterInitAction: BubbleStashController.ControllersAfterInitAction,
+ ) {}
+
+ override fun showBubbleBarImmediate() {
+ _isStashed = false
+ }
+
+ override fun showBubbleBarImmediate(bubbleBarTranslationY: Float) {
+ _isStashed = false
+ }
+
+ override fun stashBubbleBarImmediate() {
+ _isStashed = true
+ }
+
+ override fun getTouchableHeight() = 100
+
+ override fun isBubbleBarVisible() = true
+
+ override fun onNewBubbleAnimationInterrupted(
+ isStashed: Boolean,
+ bubbleBarTranslationY: Float,
+ ) {
+ _isStashed = isStashed
+ animationInterrupted = true
+ }
+
+ override fun isEventOverBubbleBarViews(ev: MotionEvent) = false
+
+ override fun setBubbleBarLocation(bubbleBarLocation: BubbleBarLocation) {}
+
+ override fun stashBubbleBar() {
+ _isStashed = true
+ }
+
+ override fun showBubbleBar(expandBubbles: Boolean, bubbleBarGesture: Boolean) {
+ _isStashed = false
+ }
+
+ override fun getDiffBetweenHandleAndBarCenters() = DIFF_BETWEEN_HANDLE_AND_BAR_CENTERS
+
+ override fun getStashedHandleTranslationForNewBubbleAnimation() = HANDLE_TRANSLATION
+
+ override fun getStashedHandlePhysicsAnimator(): PhysicsAnimator<View>? {
+ return handleAnimator
+ }
+
+ override fun updateTaskbarTouchRegion() {
+ taskbarTouchRegionUpdated = true
+ }
+
+ override fun setHandleTranslationY(translationY: Float) {}
+
+ override fun getHandleTranslationY() = 0f
+
+ override fun getHandleBounds(bounds: Rect) {}
+ }
}
private const val DIFF_BETWEEN_HANDLE_AND_BAR_CENTERS = -20f
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 6d53e8e..d96e06e 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
@@ -26,6 +26,7 @@
import com.android.launcher3.dagger.ApplicationContext
import com.android.launcher3.dagger.LauncherAppComponent
import com.android.launcher3.dagger.LauncherAppSingleton
+import com.android.launcher3.statehandlers.DesktopVisibilityController
import com.android.launcher3.util.AllModulesMinusWMProxy
import com.android.launcher3.util.DaggerSingletonTracker
import com.android.launcher3.util.DisplayController
@@ -39,12 +40,14 @@
import dagger.BindsInstance
import dagger.Component
import dagger.Module
+import dagger.Provides
import javax.inject.Inject
import org.junit.rules.ExternalResource
import org.junit.rules.RuleChain
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
+import org.mockito.kotlin.spy
/**
* [SandboxApplication] for running Taskbar tests.
@@ -135,6 +138,20 @@
@Binds abstract fun bindDisplayController(controller: DisplayControllerSpy): DisplayController
}
+@Module
+object DesktopVisibilityControllerModule {
+ @JvmStatic
+ @Provides
+ @LauncherAppSingleton
+ fun provideDesktopVisibilityController(
+ @ApplicationContext context: Context,
+ systemUiProxy: SystemUiProxy,
+ lifecycleTracker: DaggerSingletonTracker,
+ ): DesktopVisibilityController {
+ return spy(DesktopVisibilityController(context, systemUiProxy, lifecycleTracker))
+ }
+}
+
@LauncherAppSingleton
@Component(
modules =
@@ -143,6 +160,7 @@
FakePrefsModule::class,
DisplayControllerModule::class,
TaskbarSandboxModule::class,
+ DesktopVisibilityControllerModule::class,
]
)
interface TaskbarSandboxComponent : LauncherAppComponent {
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java
index 0c74610..6fbbd59 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java
@@ -364,7 +364,7 @@
float xVelocityPxPerMs = isQuickSwitch ? 100 : 0;
float yVelocityPxPerMs = isQuickSwitch ? 0 : -100;
swipeHandler.onGestureEnded(
- yVelocityPxPerMs, new PointF(xVelocityPxPerMs, yVelocityPxPerMs));
+ yVelocityPxPerMs, new PointF(xVelocityPxPerMs, yVelocityPxPerMs), isQuickSwitch);
swipeHandler.onCalculateEndTarget();
runOnMainSync(swipeHandler::onSettledOnEndTarget);
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/DisplayModelTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/DisplayModelTest.kt
index a939e84..fa7907f 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/DisplayModelTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/DisplayModelTest.kt
@@ -21,6 +21,7 @@
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import java.io.PrintWriter
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Test
@@ -37,25 +38,29 @@
override fun cleanup() {
isCleanupCalled = true
}
+
+ override fun dump(prefix: String, writer: PrintWriter) {
+ // No-Op
+ }
}
private val testableDisplayModel =
object : DisplayModel<TestableResource>(context) {
- override fun createDisplayResource(displayId: Int) {
- displayResourceArray.put(displayId, TestableResource())
+ override fun createDisplayResource(display: Display): TestableResource {
+ return TestableResource()
}
}
@Test
fun testCreate() {
- testableDisplayModel.createDisplayResource(Display.DEFAULT_DISPLAY)
+ testableDisplayModel.storeDisplayResource(Display.DEFAULT_DISPLAY)
val resource = testableDisplayModel.getDisplayResource(Display.DEFAULT_DISPLAY)
assertNotNull(resource)
}
@Test
fun testCleanAndDelete() {
- testableDisplayModel.createDisplayResource(Display.DEFAULT_DISPLAY)
+ testableDisplayModel.storeDisplayResource(Display.DEFAULT_DISPLAY)
val resource = testableDisplayModel.getDisplayResource(Display.DEFAULT_DISPLAY)!!
assertNotNull(resource)
testableDisplayModel.deleteDisplayResource(Display.DEFAULT_DISPLAY)
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2Test.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2Test.kt
index af741f6..5661dcf 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2Test.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2Test.kt
@@ -91,7 +91,14 @@
.bindRotationHelper(mock(RotationTouchHelper::class.java))
.bindRecentsState(mock(RecentsAnimationDeviceState::class.java))
)
- gestureState = spy(GestureState(OverviewComponentObserver.INSTANCE.get(sandboxContext), 0))
+ gestureState =
+ spy(
+ GestureState(
+ OverviewComponentObserver.INSTANCE.get(sandboxContext),
+ DEFAULT_DISPLAY,
+ 0,
+ )
+ )
underTest =
LauncherSwipeHandlerV2(
@@ -109,14 +116,14 @@
@Test
fun goHomeFromAppByTrackpad_updateEduStats() {
gestureState.setTrackpadGestureType(GestureState.TrackpadGestureType.THREE_FINGER)
- underTest.onGestureEnded(flingSpeed, PointF())
+ underTest.onGestureEnded(flingSpeed, PointF(), /* horizontalTouchSlopPassed= */ false)
verify(systemUiProxy)
.updateContextualEduStats(/* isTrackpadGesture= */ eq(true), eq(GestureType.HOME))
}
@Test
fun goHomeFromAppByTouch_updateEduStats() {
- underTest.onGestureEnded(flingSpeed, PointF())
+ underTest.onGestureEnded(flingSpeed, PointF(), /* horizontalTouchSlopPassed= */ false)
verify(systemUiProxy)
.updateContextualEduStats(/* isTrackpadGesture= */ eq(false), eq(GestureType.HOME))
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentTasksListTest.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentTasksListTest.java
index ad9bbb9..9722e9d 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentTasksListTest.java
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentTasksListTest.java
@@ -19,6 +19,7 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.launcher3.Flags.FLAG_ENABLE_SEPARATE_EXTERNAL_DISPLAY_TASKS;
+import static com.android.window.flags.Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND;
import static com.google.common.truth.Truth.assertThat;
@@ -48,6 +49,8 @@
import androidx.test.filters.SmallTest;
import com.android.internal.R;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
+import com.android.launcher3.util.DaggerSingletonTracker;
import com.android.launcher3.util.LooperExecutor;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.views.TaskViewType;
@@ -101,7 +104,9 @@
.thenReturn(true);
mRecentTasksList = new RecentTasksList(mContext, mockMainThreadExecutor,
- mockKeyguardManager, mSystemUiProxy, mTopTaskTracker);
+ mockKeyguardManager, mSystemUiProxy, mTopTaskTracker,
+ mock(DesktopVisibilityController.class),
+ mock(DaggerSingletonTracker.class));
}
@Test
@@ -201,6 +206,7 @@
@Test
@EnableFlags(FLAG_ENABLE_SEPARATE_EXTERNAL_DISPLAY_TASKS)
+ @DisableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
public void loadTasksInBackground_freeformTask_createsDesktopTaskPerDisplay() throws Exception {
List<TaskInfo> tasks = Arrays.asList(
createRecentTaskInfo(1 /* taskId */, DEFAULT_DISPLAY),
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 b3056f5..cfeade8 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumerTest.java
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumerTest.java
@@ -311,6 +311,18 @@
}
@Test
+ public void testTouchCancelWithoutTouchDown() {
+ mUnderTest.onMotionEvent(generateCenteredMotionEvent(ACTION_CANCEL));
+
+ assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_INACTIVE);
+ assertFalse(mLongPressTriggered.get());
+ verify(mNavHandleLongPressHandler, never()).onTouchStarted(any());
+ verify(mNavHandleLongPressHandler, times(1)).onTouchFinished(any(), any());
+ verifyNoMoreInteractions(mStatsLogger);
+ verifyNoMoreInteractions(mStatsLatencyLogger);
+ }
+
+ @Test
public void testLongPressAbortedByTouchSlopPassedVertically() {
mUnderTest.onMotionEvent(generateCenteredMotionEvent(ACTION_DOWN));
sleep(MIN_TIME_TO_LOG_ABANDON_MS);
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapperTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapperTest.kt
index 7ca194a..6c0d0ed 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapperTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapperTest.kt
@@ -19,55 +19,44 @@
import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.drawable.ShapeDrawable
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
import android.view.Surface
import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.launcher3.Flags
import com.android.quickstep.recents.ui.viewmodel.TaskData
+import com.android.quickstep.task.thumbnail.TaskHeaderUiState
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.LiveTile
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Snapshot
-import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.ThumbnailHeader
import com.android.systemui.shared.recents.model.ThumbnailData
import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class TaskUiStateMapperTest {
+ @get:Rule val mSetFlagsRule = SetFlagsRule()
+
+ /** TaskHeaderUiState */
@Test
- fun taskData_isNull_returns_Uninitialized() {
+ fun taskData_isNull_returns_HideHeader() {
val result =
- TaskUiStateMapper.toTaskThumbnailUiState(
+ TaskUiStateMapper.toTaskHeaderState(
taskData = null,
- isLiveTile = false,
hasHeader = false,
clickCloseListener = null,
)
- assertThat(result).isEqualTo(TaskThumbnailUiState.Uninitialized)
+ assertThat(result).isEqualTo(TaskHeaderUiState.HideHeader)
}
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_EXPLODED_VIEW)
@Test
- fun taskData_isLiveTile_returns_LiveTile() {
- val inputs =
- listOf(TASK_DATA, TASK_DATA.copy(thumbnailData = null), TASK_DATA.copy(isLocked = true))
- inputs.forEach { input ->
- val result =
- TaskUiStateMapper.toTaskThumbnailUiState(
- taskData = input,
- isLiveTile = true,
- hasHeader = false,
- clickCloseListener = null,
- )
- assertThat(result).isEqualTo(LiveTile.WithoutHeader)
- }
- }
-
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_EXPLODED_VIEW)
- @Test
- fun taskData_isLiveTileWithHeader_returns_LiveTileWithHeader() {
+ fun explodedFlagDisabled_returnsHideHeader() {
val inputs =
listOf(
TASK_DATA,
@@ -76,15 +65,11 @@
TASK_DATA.copy(title = null),
)
val closeCallback = View.OnClickListener {}
- val expected =
- LiveTile.WithHeader(
- header = ThumbnailHeader(TASK_ICON, TASK_TITLE_DESCRIPTION, closeCallback)
- )
+ val expected = TaskHeaderUiState.HideHeader
inputs.forEach { taskData ->
val result =
- TaskUiStateMapper.toTaskThumbnailUiState(
+ TaskUiStateMapper.toTaskHeaderState(
taskData = taskData,
- isLiveTile = true,
hasHeader = true,
clickCloseListener = closeCallback,
)
@@ -94,7 +79,38 @@
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_EXPLODED_VIEW)
@Test
- fun taskData_isLiveTileWithHeader_missingHeaderData_returns_LiveTileWithoutHeader() {
+ fun taskData_hasHeader_and_taskData_returnsShowHeader() {
+ val inputs =
+ listOf(
+ TASK_DATA,
+ TASK_DATA.copy(thumbnailData = null),
+ TASK_DATA.copy(isLocked = true),
+ TASK_DATA.copy(title = null),
+ )
+ val closeCallback = View.OnClickListener {}
+ val expected =
+ TaskHeaderUiState.ShowHeader(
+ header =
+ TaskHeaderUiState.ThumbnailHeader(
+ TASK_ICON,
+ TASK_TITLE_DESCRIPTION,
+ closeCallback,
+ )
+ )
+ inputs.forEach { taskData ->
+ val result =
+ TaskUiStateMapper.toTaskHeaderState(
+ taskData = taskData,
+ hasHeader = true,
+ clickCloseListener = closeCallback,
+ )
+ assertThat(result).isEqualTo(expected)
+ }
+ }
+
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_EXPLODED_VIEW)
+ @Test
+ fun taskData_hasHeader_emptyTaskData_returns_HideHeader() {
val inputs =
listOf(
TASK_DATA.copy(icon = null),
@@ -104,30 +120,42 @@
inputs.forEach { taskData ->
val result =
- TaskUiStateMapper.toTaskThumbnailUiState(
+ TaskUiStateMapper.toTaskHeaderState(
taskData = taskData,
- isLiveTile = true,
hasHeader = true,
clickCloseListener = {},
)
- assertThat(result).isEqualTo(LiveTile.WithoutHeader)
+ assertThat(result).isEqualTo(TaskHeaderUiState.HideHeader)
+ }
+ }
+
+ /** TaskThumbnailUiState */
+ @Test
+ fun taskData_isNull_returns_Uninitialized() {
+ val result = TaskUiStateMapper.toTaskThumbnailUiState(taskData = null, isLiveTile = false)
+ assertThat(result).isEqualTo(TaskThumbnailUiState.Uninitialized)
+ }
+
+ @Test
+ fun taskData_isLiveTile_returns_LiveTile() {
+ val inputs =
+ listOf(TASK_DATA, TASK_DATA.copy(thumbnailData = null), TASK_DATA.copy(isLocked = true))
+ inputs.forEach { input ->
+ val result =
+ TaskUiStateMapper.toTaskThumbnailUiState(taskData = input, isLiveTile = true)
+ assertThat(result).isEqualTo(LiveTile)
}
}
@Test
fun taskData_isStaticTile_returns_SnapshotSplash() {
val result =
- TaskUiStateMapper.toTaskThumbnailUiState(
- taskData = TASK_DATA,
- isLiveTile = false,
- hasHeader = false,
- clickCloseListener = null,
- )
+ TaskUiStateMapper.toTaskThumbnailUiState(taskData = TASK_DATA, isLiveTile = false)
val expected =
TaskThumbnailUiState.SnapshotSplash(
snapshot =
- Snapshot.WithoutHeader(
+ Snapshot(
backgroundColor = TASK_BACKGROUND_COLOR,
bitmap = TASK_THUMBNAIL,
thumbnailRotation = Surface.ROTATION_0,
@@ -138,72 +166,12 @@
assertThat(result).isEqualTo(expected)
}
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_EXPLODED_VIEW)
- @Test
- fun taskData_isStaticTile_withHeader_returns_SnapshotSplashWithHeader() {
- val inputs = listOf(TASK_DATA, TASK_DATA.copy(title = null))
- val closeCallback = View.OnClickListener {}
- val expected =
- TaskThumbnailUiState.SnapshotSplash(
- snapshot =
- Snapshot.WithHeader(
- backgroundColor = TASK_BACKGROUND_COLOR,
- bitmap = TASK_THUMBNAIL,
- thumbnailRotation = Surface.ROTATION_0,
- header = ThumbnailHeader(TASK_ICON, TASK_TITLE_DESCRIPTION, closeCallback),
- ),
- splash = TASK_ICON,
- )
- inputs.forEach { taskData ->
- val result =
- TaskUiStateMapper.toTaskThumbnailUiState(
- taskData = taskData,
- isLiveTile = false,
- hasHeader = true,
- clickCloseListener = closeCallback,
- )
- assertThat(result).isEqualTo(expected)
- }
- }
-
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_EXPLODED_VIEW)
- @Test
- fun taskData_isStaticTile_missingHeaderData_returns_SnapshotSplashWithoutHeader() {
- val inputs =
- listOf(
- TASK_DATA.copy(titleDescription = null, icon = null),
- TASK_DATA.copy(titleDescription = null),
- TASK_DATA.copy(icon = null),
- )
- val expected =
- Snapshot.WithoutHeader(
- backgroundColor = TASK_BACKGROUND_COLOR,
- thumbnailRotation = Surface.ROTATION_0,
- bitmap = TASK_THUMBNAIL,
- )
- inputs.forEach { taskData ->
- val result =
- TaskUiStateMapper.toTaskThumbnailUiState(
- taskData = taskData,
- isLiveTile = false,
- hasHeader = true,
- clickCloseListener = {},
- )
-
- assertThat(result).isInstanceOf(TaskThumbnailUiState.SnapshotSplash::class.java)
- result as TaskThumbnailUiState.SnapshotSplash
- assertThat(result.snapshot).isEqualTo(expected)
- }
- }
-
@Test
fun taskData_thumbnailIsNull_returns_BackgroundOnly() {
val result =
TaskUiStateMapper.toTaskThumbnailUiState(
taskData = TASK_DATA.copy(thumbnailData = null),
isLiveTile = false,
- hasHeader = false,
- clickCloseListener = null,
)
val expected = TaskThumbnailUiState.BackgroundOnly(TASK_BACKGROUND_COLOR)
@@ -216,8 +184,6 @@
TaskUiStateMapper.toTaskThumbnailUiState(
taskData = TASK_DATA.copy(isLocked = true),
isLiveTile = false,
- hasHeader = false,
- clickCloseListener = null,
)
val expected = TaskThumbnailUiState.BackgroundOnly(TASK_BACKGROUND_COLOR)
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
index a97ef0c..18b9fe9 100644
--- 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
@@ -66,16 +66,7 @@
@Before
fun setUp() {
- sut =
- TaskViewModel(
- taskViewType = TaskViewType.SINGLE,
- recentsViewData = recentsViewData,
- getTaskUseCase = getTaskUseCase,
- getSysUiStatusNavFlagsUseCase = GetSysUiStatusNavFlagsUseCase(),
- isThumbnailValidUseCase = isThumbnailValidUseCase,
- getThumbnailPositionUseCase = getThumbnailPositionUseCase,
- dispatcherProvider = TestDispatcherProvider(unconfinedTestDispatcher),
- )
+ sut = createTaskViewModel(TaskViewType.SINGLE)
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) })
@@ -93,6 +84,7 @@
isLiveTile = false,
hasHeader = false,
sysUiStatusNavFlags = FLAGS_APPEARANCE_LIGHT_THEME,
+ taskOverlayEnabled = false,
)
assertThat(sut.state.first()).isEqualTo(expectedResult)
}
@@ -139,6 +131,7 @@
isLiveTile = false,
hasHeader = false,
sysUiStatusNavFlags = FLAGS_APPEARANCE_LIGHT_THEME,
+ taskOverlayEnabled = false,
)
assertThat(sut.state.first()).isEqualTo(expectedResult)
}
@@ -161,6 +154,7 @@
isLiveTile = true,
hasHeader = false,
sysUiStatusNavFlags = FLAGS_APPEARANCE_LIGHT_THEME,
+ taskOverlayEnabled = false,
)
assertThat(sut.state.first()).isEqualTo(expectedResult)
}
@@ -183,6 +177,7 @@
isLiveTile = false,
hasHeader = false,
sysUiStatusNavFlags = FLAGS_APPEARANCE_LIGHT_THEME,
+ taskOverlayEnabled = false,
)
assertThat(sut.state.first()).isEqualTo(expectedResult)
}
@@ -204,6 +199,7 @@
isLiveTile = false,
hasHeader = false,
sysUiStatusNavFlags = FLAGS_APPEARANCE_LIGHT_THEME,
+ taskOverlayEnabled = false,
)
assertThat(sut.state.first()).isEqualTo(expectedResult)
}
@@ -221,6 +217,7 @@
isLiveTile = false,
hasHeader = false,
sysUiStatusNavFlags = FLAGS_APPEARANCE_LIGHT_THEME,
+ taskOverlayEnabled = false,
)
assertThat(sut.state.first()).isEqualTo(expectedResult)
}
@@ -235,11 +232,64 @@
isLiveTile = false,
hasHeader = false,
sysUiStatusNavFlags = FLAGS_APPEARANCE_DEFAULT,
+ taskOverlayEnabled = false,
)
assertThat(sut.state.first()).isEqualTo(expectedResult)
}
@Test
+ fun taskOverlayEnabled_when_OverlayIsEnabledForVisibleSingleTask() =
+ testScope.runTest {
+ sut.bind(TASK_MODEL_1.id)
+ recentsViewData.overlayEnabled.value = true
+ recentsViewData.settledFullyVisibleTaskIds.value = setOf(1)
+
+ assertThat(sut.state.first().taskOverlayEnabled).isTrue()
+ }
+
+ @Test
+ fun taskOverlayDisabled_when_usingGroupedTask() =
+ testScope.runTest {
+ sut = createTaskViewModel(TaskViewType.GROUPED)
+ sut.bind(TASK_MODEL_1.id)
+ recentsViewData.overlayEnabled.value = true
+ recentsViewData.settledFullyVisibleTaskIds.value = setOf(1)
+
+ assertThat(sut.state.first().taskOverlayEnabled).isFalse()
+ }
+
+ @Test
+ fun taskOverlayDisabled_when_usingDesktopTask() =
+ testScope.runTest {
+ sut = createTaskViewModel(TaskViewType.DESKTOP)
+ sut.bind(TASK_MODEL_1.id)
+ recentsViewData.overlayEnabled.value = true
+ recentsViewData.settledFullyVisibleTaskIds.value = setOf(1)
+
+ assertThat(sut.state.first().taskOverlayEnabled).isFalse()
+ }
+
+ @Test
+ fun taskOverlayDisabled_when_OverlayIsEnabledForInvisibleTask() =
+ testScope.runTest {
+ sut.bind(TASK_MODEL_1.id)
+ recentsViewData.overlayEnabled.value = true
+ recentsViewData.settledFullyVisibleTaskIds.value = setOf(2)
+
+ assertThat(sut.state.first().taskOverlayEnabled).isFalse()
+ }
+
+ @Test
+ fun taskOverlayDisabled_when_OverlayIsDisabledForVisibleTask() =
+ testScope.runTest {
+ sut.bind(TASK_MODEL_1.id)
+ recentsViewData.overlayEnabled.value = false
+ recentsViewData.settledFullyVisibleTaskIds.value = setOf(1)
+
+ assertThat(sut.state.first().taskOverlayEnabled).isFalse()
+ }
+
+ @Test
fun shouldShowSplash_calls_useCase() {
sut.isThumbnailValid(null, 0, 0)
verify(isThumbnailValidUseCase).invoke(anyOrNull(), anyInt(), anyInt())
@@ -256,6 +306,17 @@
isLocked = isLocked,
)
+ private fun createTaskViewModel(taskViewType: TaskViewType) =
+ TaskViewModel(
+ taskViewType = taskViewType,
+ recentsViewData = recentsViewData,
+ getTaskUseCase = getTaskUseCase,
+ getSysUiStatusNavFlagsUseCase = GetSysUiStatusNavFlagsUseCase(),
+ isThumbnailValidUseCase = isThumbnailValidUseCase,
+ getThumbnailPositionUseCase = getThumbnailPositionUseCase,
+ dispatcherProvider = TestDispatcherProvider(unconfinedTestDispatcher),
+ )
+
private companion object {
const val INVALID_TASK_ID = -1
const val FLAGS_APPEARANCE_LIGHT_THEME = FLAG_LIGHT_STATUS or FLAG_LIGHT_NAV
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 44ea73e..0119679 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
@@ -88,16 +88,16 @@
@Test
fun testCreateSeparateInstances() {
- val display = Display.DEFAULT_DISPLAY + 1
- runOnMainSync { recentsDisplayModel.createDisplayResource(display) }
+ val displayId = Display.DEFAULT_DISPLAY + 1
+ runOnMainSync { recentsDisplayModel.storeDisplayResource(displayId) }
val defaultManager = recentsDisplayModel.getRecentsWindowManager(Display.DEFAULT_DISPLAY)
- val secondaryManager = recentsDisplayModel.getRecentsWindowManager(display)
+ val secondaryManager = recentsDisplayModel.getRecentsWindowManager(displayId)
Assert.assertNotSame(defaultManager, secondaryManager)
val defaultInterface =
recentsDisplayModel.getFallbackWindowInterface(Display.DEFAULT_DISPLAY)
- val secondInterface = recentsDisplayModel.getFallbackWindowInterface(display)
+ val secondInterface = recentsDisplayModel.getFallbackWindowInterface(displayId)
Assert.assertNotSame(defaultInterface, secondInterface)
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/RecentOrientedStateHandlerTests.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/RecentOrientedStateHandlerTests.kt
new file mode 100644
index 0000000..3cdf608
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/RecentOrientedStateHandlerTests.kt
@@ -0,0 +1,166 @@
+/*
+ * 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.view.Surface
+import android.view.Surface.ROTATION_0
+import android.view.Surface.ROTATION_180
+import android.view.Surface.ROTATION_90
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.quickstep.FallbackActivityInterface
+import com.android.quickstep.orientation.RecentsPagedOrientationHandler
+import com.android.quickstep.orientation.RecentsPagedOrientationHandler.Companion.PORTRAIT
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.whenever
+
+/**
+ * Test all possible inputs to RecentsOrientedState.updateHandler. It tests all possible
+ * combinations of rotations and relevant methods (two methods that return boolean values) but it
+ * only provides the expected result when the final rotation is different from ROTATION_0 for
+ * simplicity. So any case not shown in resultMap you can assume results in ROTATION_0.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class RecentOrientedStateHandlerTests {
+
+ data class TestCase(
+ val recentsRotation: Int,
+ val displayRotation: Int,
+ val touchRotation: Int,
+ val isRotationAllowed: Boolean,
+ val isFixedLandscape: Boolean,
+ ) {
+ override fun toString(): String {
+ return "TestCase(recentsRotation=${Surface.rotationToString(recentsRotation)}, " +
+ "displayRotation=${Surface.rotationToString(displayRotation)}, " +
+ "touchRotation=${Surface.rotationToString(touchRotation)}, " +
+ "isRotationAllowed=$isRotationAllowed, " +
+ "isFixedLandscape=$isFixedLandscape)"
+ }
+ }
+
+ private fun runTestCase(testCase: TestCase, expectedHandler: RecentsPagedOrientationHandler) {
+ val recentOrientedState =
+ spy(
+ RecentsOrientedState(
+ ApplicationProvider.getApplicationContext(),
+ FallbackActivityInterface.INSTANCE,
+ ) {}
+ )
+ whenever(recentOrientedState.isRecentsActivityRotationAllowed).thenAnswer {
+ testCase.isRotationAllowed
+ }
+ whenever(recentOrientedState.isLauncherFixedLandscape).thenAnswer {
+ testCase.isFixedLandscape
+ }
+
+ recentOrientedState.update(testCase.displayRotation, testCase.touchRotation)
+ val rotation = recentOrientedState.orientationHandler.rotation
+ assertWithMessage("$testCase to ${Surface.rotationToString(rotation)},")
+ .that(rotation)
+ .isEqualTo(expectedHandler.rotation)
+ }
+
+ @Test
+ fun `test fixed landscape when device is portrait`() {
+ runTestCase(
+ TestCase(
+ recentsRotation = ROTATION_0,
+ displayRotation = -1,
+ touchRotation = ROTATION_0,
+ isRotationAllowed = false,
+ isFixedLandscape = true,
+ ),
+ PORTRAIT,
+ )
+ }
+
+ @Test
+ fun `test fixed landscape when device is landscape`() {
+ runTestCase(
+ TestCase(
+ recentsRotation = ROTATION_90,
+ displayRotation = -1,
+ touchRotation = ROTATION_0,
+ isRotationAllowed = false,
+ isFixedLandscape = true,
+ ),
+ PORTRAIT,
+ )
+ }
+
+ @Test
+ fun `test fixed landscape when device is seascape`() {
+ runTestCase(
+ TestCase(
+ recentsRotation = ROTATION_180,
+ displayRotation = -1,
+ touchRotation = ROTATION_0,
+ isRotationAllowed = false,
+ isFixedLandscape = true,
+ ),
+ PORTRAIT,
+ )
+ }
+
+ @Test
+ fun `test fixed landscape when device is portrait and display rotation is portrait`() {
+ runTestCase(
+ TestCase(
+ recentsRotation = ROTATION_0,
+ displayRotation = ROTATION_0,
+ touchRotation = ROTATION_0,
+ isRotationAllowed = false,
+ isFixedLandscape = true,
+ ),
+ PORTRAIT,
+ )
+ }
+
+ @Test
+ fun `test fixed landscape when device is landscape and display rotation is landscape `() {
+ runTestCase(
+ TestCase(
+ recentsRotation = ROTATION_90,
+ displayRotation = ROTATION_90,
+ touchRotation = ROTATION_0,
+ isRotationAllowed = false,
+ isFixedLandscape = true,
+ ),
+ PORTRAIT,
+ )
+ }
+
+ @Test
+ fun `test fixed landscape when device is seascape and display rotation is seascape`() {
+ runTestCase(
+ TestCase(
+ recentsRotation = ROTATION_180,
+ displayRotation = ROTATION_180,
+ touchRotation = ROTATION_0,
+ isRotationAllowed = false,
+ isFixedLandscape = true,
+ ),
+ PORTRAIT,
+ )
+ }
+}
diff --git a/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java b/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
index d52d054..59ce637 100644
--- a/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
+++ b/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
@@ -34,7 +34,6 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.spy;
import android.app.prediction.AppTarget;
import android.app.prediction.AppTargetId;
@@ -46,7 +45,6 @@
import android.os.Process;
import android.os.UserHandle;
import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.text.TextUtils;
@@ -71,8 +69,6 @@
import java.util.Arrays;
import java.util.List;
-import java.util.Set;
-import java.util.function.Predicate;
import java.util.stream.Collectors;
@SmallTest
@@ -234,50 +230,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_ENABLE_TIERED_WIDGETS_BY_DEFAULT_IN_PICKER)
- public void widgetsRecommendationRan_keepsWidgetsNotOnWorkspace_addsWidgetsFromEligibleApps() {
- runOnExecutorSync(MODEL_EXECUTOR, () -> {
- WidgetsFilterDataProvider spiedFilterProvider = spy(
- mModelHelper.getModel().getWidgetsFilterDataProvider());
- doAnswer(i -> new Predicate<WidgetItem>() {
- @Override
- public boolean test(WidgetItem widgetItem) {
- // app5's widget is already on workspace, but, app2 is not.
- // And app4's second widget is also not on workspace.
- return Set.of("app5", "app2", "app4").contains(
- widgetItem.componentName.getPackageName());
- }
- }).when(spiedFilterProvider).getPredictedWidgetsFilter();
- mModelHelper.getBgDataModel().widgetsModel.updateWidgetFilters(spiedFilterProvider);
- // App5's widget that's already on workspace.
- AppTarget widget1 = new AppTarget(new AppTargetId("app5"), "app5", "provider1",
- mUserHandle);
- // App4's widget eligible and not on workspace.
- AppTarget widget2 = new AppTarget(new AppTargetId("app4"), "app4", "provider2",
- mUserHandle);
-
- mCallback.mRecommendedWidgets = null;
- mModelHelper.getModel().enqueueModelUpdateTask(
- newWidgetsPredicationTask(List.of(widget1, widget2)));
- runOnExecutorSync(MAIN_EXECUTOR, () -> {
- });
-
- List<PendingAddWidgetInfo> recommendedWidgets = mCallback.mRecommendedWidgets.items
- .stream()
- .map(itemInfo -> (PendingAddWidgetInfo) itemInfo)
- .collect(Collectors.toList());
- assertThat(recommendedWidgets).hasSize(2);
- List<ComponentName> componentNames = recommendedWidgets.stream().map(
- w -> w.componentName).toList();
- assertThat(componentNames).containsExactly(
- // Locally added, not on workspace, eligible app per filter
- mApp2Provider1.provider,
- // From prediction service, not on workspace, eligible app per filter
- mApp4Provider2.provider);
- });
- }
-
- @Test
public void widgetsRecommendations_excludesWidgetsHiddenForPicker() {
runOnExecutorSync(MODEL_EXECUTOR, () -> {
diff --git a/quickstep/tests/src/com/android/quickstep/AbstractTaplTestsTaskbar.java b/quickstep/tests/src/com/android/quickstep/AbstractTaplTestsTaskbar.java
index fc757b4..0ccc76b 100644
--- a/quickstep/tests/src/com/android/quickstep/AbstractTaplTestsTaskbar.java
+++ b/quickstep/tests/src/com/android/quickstep/AbstractTaplTestsTaskbar.java
@@ -55,7 +55,9 @@
"com.android.launcher3.testcomponent.BaseTestingActivity");
mLauncherLayout = TestUtil.setLauncherDefaultLayout(mTargetContext, layoutBuilder);
AbstractLauncherUiTest.initialize(this);
- startAppFast(CALCULATOR_APP_PACKAGE);
+ if (startCalendarAppDuringSetup()) {
+ startAppFast(CALCULATOR_APP_PACKAGE);
+ }
mLauncher.enableBlockTimeout(true);
mLauncher.showTaskbarIfHidden();
}
@@ -72,8 +74,20 @@
return DisplayController.isTransientTaskbar(context);
}
+ protected boolean startCalendarAppDuringSetup() {
+ return true;
+ }
+
+ protected boolean expectTaskbarIconsMatchHotseat() {
+ return true;
+ }
+
protected Taskbar getTaskbar() {
Taskbar taskbar = mLauncher.getLaunchedAppState().getTaskbar();
+ if (!expectTaskbarIconsMatchHotseat()) {
+ return taskbar;
+ }
+
List<String> taskbarIconNames = taskbar.getIconNames();
List<String> hotseatIconNames = mLauncher.getHotseatIconNames();
diff --git a/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt b/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt
index a523e02..7b73be7 100644
--- a/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt
@@ -36,6 +36,7 @@
import com.android.launcher3.util.SplitConfigurationOptions
import com.android.launcher3.util.TransformingTouchDelegate
import com.android.quickstep.TaskOverlayFactory.TaskOverlay
+import com.android.quickstep.task.thumbnail.TaskContentView
import com.android.quickstep.task.thumbnail.TaskThumbnailView
import com.android.quickstep.views.LauncherRecentsView
import com.android.quickstep.views.RecentsViewContainer
@@ -248,6 +249,7 @@
TaskContainer(
taskView,
task,
+ mock<TaskContentView>(),
if (enableRefactorTaskThumbnail()) mock<TaskThumbnailView>()
else mock<TaskThumbnailViewDeprecated>(),
mock<TaskViewIcon>(),
diff --git a/quickstep/tests/src/com/android/quickstep/ExternalDisplaySystemShortcutTest.kt b/quickstep/tests/src/com/android/quickstep/ExternalDisplaySystemShortcutTest.kt
index 2db94f6..5aaed7d 100644
--- a/quickstep/tests/src/com/android/quickstep/ExternalDisplaySystemShortcutTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/ExternalDisplaySystemShortcutTest.kt
@@ -37,6 +37,7 @@
import com.android.launcher3.util.SplitConfigurationOptions
import com.android.launcher3.util.TransformingTouchDelegate
import com.android.quickstep.TaskOverlayFactory.TaskOverlay
+import com.android.quickstep.task.thumbnail.TaskContentView
import com.android.quickstep.task.thumbnail.TaskThumbnailView
import com.android.quickstep.views.LauncherRecentsView
import com.android.quickstep.views.RecentsViewContainer
@@ -246,6 +247,7 @@
TaskContainer(
taskView,
task,
+ mock<TaskContentView>(),
if (enableRefactorTaskThumbnail()) mock<TaskThumbnailView>()
else mock<TaskThumbnailViewDeprecated>(),
mock<TaskViewIcon>(),
diff --git a/quickstep/tests/src/com/android/quickstep/InputConsumerUtilsTest.java b/quickstep/tests/src/com/android/quickstep/InputConsumerUtilsTest.java
index e2ca91a..3db4398 100644
--- a/quickstep/tests/src/com/android/quickstep/InputConsumerUtilsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/InputConsumerUtilsTest.java
@@ -27,9 +27,9 @@
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.os.Looper;
import android.view.Choreographer;
+import android.view.Display;
import android.view.MotionEvent;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -99,11 +99,13 @@
@Rule public final SandboxApplication mContext = new SandboxApplication();
- @NonNull private final InputMonitorCompat mInputMonitorCompat = new InputMonitorCompat("", 0);
+ private final int mDisplayId = Display.DEFAULT_DISPLAY;
+ @NonNull private final InputMonitorCompat mInputMonitorCompat =
+ new InputMonitorCompat("", mDisplayId);
private TaskAnimationManager mTaskAnimationManager;
private InputChannelCompat.InputEventReceiver mInputEventReceiver;
- @Nullable private ResetGestureInputConsumer mResetGestureInputConsumer;
+ private boolean mUserUnlocked = true;
@NonNull private Function<GestureState, AnimatedFloat> mSwipeUpProxyProvider = (state) -> null;
@NonNull @Mock private TaskbarActivityContext mTaskbarActivityContext;
@@ -162,12 +164,6 @@
}
@Before
- public void setUpResetGestureInputConsumer() {
- mResetGestureInputConsumer = new ResetGestureInputConsumer(
- mTaskAnimationManager, mTaskbarManager::getCurrentActivityContext);
- }
-
- @Before
public void setupLockedUserState() {
when(mLockedUserState.isUserUnlocked()).thenReturn(true);
}
@@ -196,7 +192,6 @@
@Before
public void setupDeviceState() {
- when(mDeviceState.getDisplayId()).thenReturn(0);
when(mDeviceState.canStartTrackpadGesture()).thenReturn(true);
when(mDeviceState.canStartSystemGesture()).thenReturn(true);
when(mDeviceState.isFullyGesturalNavMode()).thenReturn(true);
@@ -313,8 +308,6 @@
@Test
public void testNewBaseConsumer_containsOtherActivityInputConsumer() {
- // OtherActivityInputConsumer needs to be initialized on the main thread because of
- // MotionPauseDetector.mForcePauseTimeout
assertCorrectInputConsumer(
this::createBaseInputConsumer,
OtherActivityInputConsumer.class,
@@ -485,7 +478,7 @@
MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
InputConsumer inputConsumer = newConsumer(
mContext,
- mResetGestureInputConsumer,
+ mUserUnlocked,
mOverviewComponentObserver,
mDeviceState,
mPreviousGestureState,
@@ -509,7 +502,8 @@
MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
InputConsumer inputConsumer = newBaseConsumer(
mContext,
- mResetGestureInputConsumer,
+ mUserUnlocked,
+ mTaskbarManager,
mOverviewComponentObserver,
mDeviceState,
mPreviousGestureState,
@@ -534,12 +528,15 @@
ResetGestureInputConsumer.class,
InputConsumer.TYPE_RESET_GESTURE);
- mResetGestureInputConsumer = null;
+ mUserUnlocked = false;
- runOnMainSync(() -> assertThat(inputConsumerProvider.get()).isEqualTo(InputConsumer.NO_OP));
+ assertCorrectInputConsumer(
+ inputConsumerProvider,
+ InputConsumer.class,
+ InputConsumer.TYPE_NO_OP);
}
- private static void assertCorrectInputConsumer(
+ private void assertCorrectInputConsumer(
@NonNull Provider<InputConsumer> inputConsumerProvider,
@NonNull Class<? extends InputConsumer> expectedOutputConsumer,
int expectedType) {
@@ -550,11 +547,13 @@
expectedType);
}
- private static void assertCorrectInputConsumer(
+ private void assertCorrectInputConsumer(
@NonNull Provider<InputConsumer> inputConsumerProvider,
@NonNull Class<? extends InputConsumer> expectedOutputConsumer,
@NonNull Class<? extends InputConsumer> expectedActiveConsumer,
int expectedType) {
+ when(mCurrentGestureState.getDisplayId()).thenReturn(mDisplayId);
+
runOnMainSync(() -> {
InputConsumer inputConsumer = inputConsumerProvider.get();
@@ -562,7 +561,14 @@
assertThat(inputConsumer.getActiveConsumerInHierarchy())
.isInstanceOf(expectedActiveConsumer);
assertThat(inputConsumer.getType()).isEqualTo(expectedType);
+ assertThat(inputConsumer.getDisplayId()).isEqualTo(mDisplayId);
});
+ int expectedDisplayId = mDisplayId + 1;
+
+ when(mCurrentGestureState.getDisplayId()).thenReturn(expectedDisplayId);
+
+ runOnMainSync(() -> assertThat(inputConsumerProvider.get().getDisplayId())
+ .isEqualTo(expectedDisplayId));
}
private static void runOnMainSync(@NonNull Runnable runnable) {
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsLockedTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsLockedTaskbar.java
index 8fedf5c..b2617dd 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsLockedTaskbar.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsLockedTaskbar.java
@@ -15,14 +15,18 @@
*/
package com.android.quickstep;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;
import static androidx.test.InstrumentationRegistry.getTargetContext;
+import static com.android.launcher3.util.TestConstants.AppNames.TEST_APP_NAME;
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 static com.google.common.truth.Truth.assertThat;
+
import android.os.RemoteException;
import android.util.Log;
import android.view.WindowManagerGlobal;
@@ -30,6 +34,7 @@
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.launcher3.tapl.HomeAllApps;
import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
import com.android.launcher3.util.rule.SetPropRule;
import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
@@ -40,6 +45,7 @@
import org.junit.Assume;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.ExternalResource;
import org.junit.runner.RunWith;
@LargeTest
@@ -51,25 +57,40 @@
public SetPropRule mSetPropRule =
new SetPropRule(ENTER_DESKTOP_BY_DEFAULT_ON_FREEFORM_DISPLAY_SYS_PROP, "true");
+ // Default-to-desktop feature requires the display to be freeform mode.
+ @Rule
+ public ExternalResource mFreeformDisplayRule = new ExternalResource() {
+ private int mOriginalWindowingMode = WINDOWING_MODE_UNDEFINED;
+
+ @Override
+ protected void before() {
+ mOriginalWindowingMode = setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
+ }
+
+ @Override
+ protected void after() {
+ if (mOriginalWindowingMode != WINDOWING_MODE_UNDEFINED) {
+ setDisplayWindowingMode(mOriginalWindowingMode);
+ }
+ }
+ };
+
@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);
+ protected boolean startCalendarAppDuringSetup() {
+ return false;
+ }
- mLauncher.recreateTaskbar();
-
- super.tearDown();
+ @Override
+ protected boolean expectTaskbarIconsMatchHotseat() {
+ return false;
}
@Test
@@ -87,10 +108,31 @@
mLauncher.getLaunchedAppState().assertTaskbarVisible();
}
- private void setDisplayWindowingMode(int windowingMode) {
+ @Test
+ @PortraitLandscape
+ @NavigationModeSwitch
+ @TaskbarModeSwitch(mode = PERSISTENT)
+ public void testDragFromAllAppsToWorspace() {
+ mDevice.pressHome();
+ waitForResumed("Launcher internal state is still Background");
+
+ final HomeAllApps allApps = getTaskbar().openAllAppsOnHome();
+ allApps.freeze();
try {
+ allApps.getAppIcon(TEST_APP_NAME).dragToWorkspace(false, false);
+ assertThat(mLauncher.getWorkspace().getWorkspaceAppIcon(TEST_APP_NAME)).isNotNull();
+ } finally {
+ allApps.unfreeze();
+ }
+ }
+
+ private int setDisplayWindowingMode(int windowingMode) {
+ try {
+ int originalWindowingMode =
+ WindowManagerGlobal.getWindowManagerService().getWindowingMode(DEFAULT_DISPLAY);
WindowManagerGlobal.getWindowManagerService().setWindowingMode(
DEFAULT_DISPLAY, windowingMode);
+ return originalWindowingMode;
} catch (RemoteException e) {
Log.e(TAG, "error setting windowing mode", e);
throw new RuntimeException(e);
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 1c87bce..b207d4a 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -100,11 +100,9 @@
@After
public void tearDown() {
- runOnRecentsView(recentsView -> {
- if (recentsView != null) {
- recentsView.getPagedViewOrientedState().forceAllowRotationForTesting(false);
- }
- });
+ runOnRecentsView(recentsView ->
+ recentsView.getPagedViewOrientedState().forceAllowRotationForTesting(false),
+ /* forTearDown= */ true);
}
public static void startTestApps() throws Exception {
@@ -433,18 +431,17 @@
(Math.abs(recentsView.getTopRowTaskCountForTablet()
- recentsView.getBottomRowTaskCountForTablet()) <= 1)));
- // TODO(b/308841019): Re-enable after fixing Overview jank when dismiss
-// // Test dismissing more tasks.
-// assertIsInState(
-// "Launcher internal state didn't remain in Overview", ExpectedState.OVERVIEW);
-// overview.getCurrentTask().dismiss();
-// assertIsInState(
-// "Launcher internal state didn't remain in Overview", ExpectedState.OVERVIEW);
-// overview.getCurrentTask().dismiss();
-// runOnRecentsView(recentsView -> assertTrue(
-// "Grid did not rebalance after multiple dismissals",
-// (Math.abs(recentsView.getTopRowTaskCountForTablet()
-// - recentsView.getBottomRowTaskCountForTablet()) <= 1)));
+ // Test dismissing more tasks.
+ assertIsInState(
+ "Launcher internal state didn't remain in Overview", ExpectedState.OVERVIEW);
+ overview.getCurrentTask().dismiss();
+ assertIsInState(
+ "Launcher internal state didn't remain in Overview", ExpectedState.OVERVIEW);
+ overview.getCurrentTask().dismiss();
+ runOnRecentsView(recentsView -> assertTrue(
+ "Grid did not rebalance after multiple dismissals",
+ (Math.abs(recentsView.getTopRowTaskCountForTablet()
+ - recentsView.getBottomRowTaskCountForTablet()) <= 1)));
// Test dismissing all tasks.
mLauncher.goHome().switchToOverview().dismissAllTasks();
@@ -662,18 +659,31 @@
}
private <T> T getFromRecentsView(Function<RecentsView, T> f) {
+ return getFromRecentsView(f, false);
+ }
+
+ private <T> T getFromRecentsView(Function<RecentsView, T> f, boolean forTearDown) {
if (enableLauncherOverviewInWindow()) {
- return getFromRecentsWindow(
- recentsWindowManager -> f.apply(recentsWindowManager.getOverviewPanel()));
+ return getFromRecentsWindow(recentsWindowManager ->
+ (forTearDown && recentsWindowManager == null)
+ ? null : f.apply(recentsWindowManager.getOverviewPanel()));
} else {
- return getFromLauncher(launcher -> f.apply(launcher.getOverviewPanel()));
+ return getFromLauncher(launcher -> (forTearDown && launcher == null)
+ ? null : f.apply(launcher.getOverviewPanel()));
}
}
private void runOnRecentsView(Consumer<RecentsView> f) {
+ runOnRecentsView(f, false);
+ }
+
+ private void runOnRecentsView(Consumer<RecentsView> f, boolean forTearDown) {
getFromRecentsView(recentsView -> {
+ if (forTearDown && recentsView == null) {
+ return null;
+ }
f.accept(recentsView);
return null;
- });
+ }, forTearDown);
}
}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
index 08ce5e7..ec245ee 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
@@ -24,7 +24,6 @@
import androidx.test.filters.LargeTest;
import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
-import com.android.launcher3.util.rule.ScreenRecordRule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -124,7 +123,6 @@
}
@Test
- @ScreenRecordRule.ScreenRecord // b/373417111
public void testLaunchShortcut_fromTaskbarAllApps() {
getTaskbar().openAllApps()
.getAppIcon(TEST_APP_NAME)
diff --git a/quickstep/tests/src/com/android/quickstep/desktop/DesktopAppLaunchAnimatorHelperTest.kt b/quickstep/tests/src/com/android/quickstep/desktop/DesktopAppLaunchAnimatorHelperTest.kt
index b4d9f5b..daa77d2 100644
--- a/quickstep/tests/src/com/android/quickstep/desktop/DesktopAppLaunchAnimatorHelperTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/desktop/DesktopAppLaunchAnimatorHelperTest.kt
@@ -32,7 +32,9 @@
import android.view.SurfaceControl
import android.view.WindowManager
import android.window.TransitionInfo
+import android.window.TransitionInfo.Change
import androidx.core.util.Supplier
+import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread
import com.android.app.animation.Interpolators
import com.android.internal.jank.Cuj
import com.android.launcher3.desktop.DesktopAppLaunchAnimatorHelper
@@ -45,6 +47,7 @@
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
class DesktopAppLaunchAnimatorHelperTest {
@@ -70,6 +73,10 @@
whenever(transactionSupplier.get()).thenReturn(transaction)
whenever(transaction.setCrop(any(), any())).thenReturn(transaction)
whenever(transaction.setCornerRadius(any(), any())).thenReturn(transaction)
+ whenever(transaction.setScale(any(), any(), any())).thenReturn(transaction)
+ whenever(transaction.setPosition(any(), any(), any())).thenReturn(transaction)
+ whenever(transaction.setAlpha(any(), any())).thenReturn(transaction)
+ whenever(transaction.setFrameTimeline(any())).thenReturn(transaction)
whenever(context.resources).thenReturn(resources)
whenever(resources.displayMetrics).thenReturn(DisplayMetrics())
@@ -77,14 +84,8 @@
}
@Test
- fun launchTransition_returnsLaunchAnimator() {
- val openChange =
- TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_OPEN
- taskInfo = TASK_INFO_FREEFORM
- }
- val transitionInfo = TransitionInfo(WindowManager.TRANSIT_NONE, 0)
- transitionInfo.addChange(openChange)
+ fun launchTransition_returnsLaunchAnimator() = runOnUiThread {
+ val transitionInfo = createTransitionInfo(listOf(OPEN_CHANGE))
val actual = helper.createAnimators(transitionInfo, finishCallback = {})
@@ -93,20 +94,36 @@
}
@Test
- fun minimizeTransition_returnsLaunchAndMinimizeAnimator() {
- val openChange =
+ fun launchTransition_callsAnimationEndListener() = runOnUiThread {
+ val finishCallback = mock<Function1<Animator, Unit>>()
+ val transitionInfo = createTransitionInfo(listOf(OPEN_CHANGE))
+
+ val animators = helper.createAnimators(transitionInfo, finishCallback = finishCallback)
+
+ animators.forEach { animator ->
+ animator.start()
+ animator.end()
+ verify(finishCallback).invoke(animator)
+ }
+ }
+
+ @Test
+ fun noLaunchTransition_returnsEmptyAnimatorsList() = runOnUiThread {
+ val pipChange =
TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_OPEN
+ mode = WindowManager.TRANSIT_PIP
taskInfo = TASK_INFO_FREEFORM
}
- val minimizeChange =
- TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_TO_BACK
- taskInfo = TASK_INFO_FREEFORM
- }
- val transitionInfo = TransitionInfo(WindowManager.TRANSIT_NONE, 0)
- transitionInfo.addChange(openChange)
- transitionInfo.addChange(minimizeChange)
+ val transitionInfo = createTransitionInfo(listOf(pipChange))
+
+ val actual = helper.createAnimators(transitionInfo, finishCallback = {})
+
+ assertThat(actual).hasSize(0)
+ }
+
+ @Test
+ fun minimizeTransition_returnsLaunchAndMinimizeAnimator() = runOnUiThread {
+ val transitionInfo = createTransitionInfo(listOf(OPEN_CHANGE, MINIMIZE_CHANGE))
val actual = helper.createAnimators(transitionInfo, finishCallback = {})
@@ -116,21 +133,23 @@
}
@Test
+ fun minimizeTransition_callsAnimationEndListener() = runOnUiThread {
+ val finishCallback = mock<Function1<Animator, Unit>>()
+ val transitionInfo = createTransitionInfo(listOf(OPEN_CHANGE, MINIMIZE_CHANGE))
+
+ val animators = helper.createAnimators(transitionInfo, finishCallback = finishCallback)
+
+ animators.forEach { animator ->
+ animator.start()
+ animator.end()
+ verify(finishCallback).invoke(animator)
+ }
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_TRAMPOLINE_CLOSE_ANIMATION_BUGFIX)
- fun trampolineTransition_flagEnabled_returnsLaunchAndCloseAnimator() {
- val openChange =
- TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_OPEN
- taskInfo = TASK_INFO_FREEFORM
- }
- val closeChange =
- TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_CLOSE
- taskInfo = TASK_INFO_FREEFORM
- }
- val transitionInfo = TransitionInfo(WindowManager.TRANSIT_NONE, 0)
- transitionInfo.addChange(openChange)
- transitionInfo.addChange(closeChange)
+ fun trampolineTransition_flagEnabled_returnsLaunchAndCloseAnimator() = runOnUiThread {
+ val transitionInfo = createTransitionInfo(listOf(OPEN_CHANGE, CLOSE_CHANGE))
val actual = helper.createAnimators(transitionInfo, finishCallback = {})
@@ -140,21 +159,24 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_TRAMPOLINE_CLOSE_ANIMATION_BUGFIX)
+ fun trampolineTransition_flagEnabled_callsAnimationEndListener() = runOnUiThread {
+ val finishCallback = mock<Function1<Animator, Unit>>()
+ val transitionInfo = createTransitionInfo(listOf(OPEN_CHANGE, CLOSE_CHANGE))
+
+ val animators = helper.createAnimators(transitionInfo, finishCallback = finishCallback)
+
+ animators.forEach { animator ->
+ animator.start()
+ animator.end()
+ verify(finishCallback).invoke(animator)
+ }
+ }
+
+ @Test
@DisableFlags(Flags.FLAG_ENABLE_DESKTOP_TRAMPOLINE_CLOSE_ANIMATION_BUGFIX)
- fun trampolineTransition_flagDisabled_returnsLaunchAnimator() {
- val openChange =
- TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_OPEN
- taskInfo = TASK_INFO_FREEFORM
- }
- val closeChange =
- TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_CLOSE
- taskInfo = TASK_INFO_FREEFORM
- }
- val transitionInfo = TransitionInfo(WindowManager.TRANSIT_NONE, 0)
- transitionInfo.addChange(openChange)
- transitionInfo.addChange(closeChange)
+ fun trampolineTransition_flagDisabled_returnsLaunchAnimator() = runOnUiThread {
+ val transitionInfo = createTransitionInfo(listOf(OPEN_CHANGE, CLOSE_CHANGE))
val actual = helper.createAnimators(transitionInfo, finishCallback = {})
@@ -164,26 +186,9 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_TRAMPOLINE_CLOSE_ANIMATION_BUGFIX)
- fun trampolineTransition_flagEnabled_hitDesktopWindowLimit_returnsLaunchMinimizeCloseAnimator() {
- val openChange =
- TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_OPEN
- taskInfo = TASK_INFO_FREEFORM
- }
- val minimizeChange =
- TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_TO_BACK
- taskInfo = TASK_INFO_FREEFORM
- }
- val closeChange =
- TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_CLOSE
- taskInfo = TASK_INFO_FREEFORM
- }
- val transitionInfo = TransitionInfo(WindowManager.TRANSIT_NONE, 0)
- transitionInfo.addChange(openChange)
- transitionInfo.addChange(minimizeChange)
- transitionInfo.addChange(closeChange)
+ fun trampolineTransition_flagEnabled_hitDesktopWindowLimit_returnsLaunchMinimizeCloseAnimator() = runOnUiThread {
+ val transitionInfo = createTransitionInfo(
+ listOf(OPEN_CHANGE, MINIMIZE_CHANGE, CLOSE_CHANGE))
val actual = helper.createAnimators(transitionInfo, finishCallback = {})
@@ -195,26 +200,9 @@
@Test
@DisableFlags(Flags.FLAG_ENABLE_DESKTOP_TRAMPOLINE_CLOSE_ANIMATION_BUGFIX)
- fun trampolineTransition_flagDisabled_hitDesktopWindowLimit_returnsLaunchMinimizeAnimator() {
- val openChange =
- TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_OPEN
- taskInfo = TASK_INFO_FREEFORM
- }
- val minimizeChange =
- TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_TO_BACK
- taskInfo = TASK_INFO_FREEFORM
- }
- val closeChange =
- TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_CLOSE
- taskInfo = TASK_INFO_FREEFORM
- }
- val transitionInfo = TransitionInfo(WindowManager.TRANSIT_NONE, 0)
- transitionInfo.addChange(openChange)
- transitionInfo.addChange(minimizeChange)
- transitionInfo.addChange(closeChange)
+ fun trampolineTransition_flagDisabled_hitDesktopWindowLimit_returnsLaunchMinimizeAnimator() = runOnUiThread {
+ val transitionInfo = createTransitionInfo(
+ listOf(OPEN_CHANGE, MINIMIZE_CHANGE, CLOSE_CHANGE))
val actual = helper.createAnimators(transitionInfo, finishCallback = {})
@@ -265,6 +253,12 @@
assertThat(animator.duration).isEqualTo(100)
}
+ private fun createTransitionInfo(changes: List<Change>): TransitionInfo {
+ val transitionInfo = TransitionInfo(WindowManager.TRANSIT_NONE, 0)
+ changes.forEach { transitionInfo.addChange(it) }
+ return transitionInfo
+ }
+
private companion object {
val TASK_INFO_FREEFORM =
ActivityManager.RunningTaskInfo().apply {
@@ -275,5 +269,23 @@
configuration.windowConfiguration.windowingMode =
WindowConfiguration.WINDOWING_MODE_FREEFORM
}
+
+ val OPEN_CHANGE =
+ TransitionInfo.Change(mock(), mock()).apply {
+ mode = WindowManager.TRANSIT_OPEN
+ taskInfo = TASK_INFO_FREEFORM
+ }
+
+ val CLOSE_CHANGE =
+ TransitionInfo.Change(mock(), mock()).apply {
+ mode = WindowManager.TRANSIT_CLOSE
+ taskInfo = TASK_INFO_FREEFORM
+ }
+
+ val MINIMIZE_CHANGE =
+ TransitionInfo.Change(mock(), mock()).apply {
+ mode = WindowManager.TRANSIT_TO_BACK
+ taskInfo = TASK_INFO_FREEFORM
+ }
}
}
diff --git a/res/drawable/ic_chevron_end.xml b/res/drawable/ic_chevron_end.xml
new file mode 100644
index 0000000..9ca4f3a
--- /dev/null
+++ b/res/drawable/ic_chevron_end.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:autoMirrored="true"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M504,480L320,296L376,240L616,480L376,720L320,664L504,480Z" />
+</vector>
diff --git a/res/drawable/ic_chevron_start.xml b/res/drawable/ic_chevron_start.xml
new file mode 100644
index 0000000..913da02
--- /dev/null
+++ b/res/drawable/ic_chevron_start.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:autoMirrored="true"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M560,720L320,480L560,240L616,296L432,480L616,664L560,720Z" />
+</vector>
diff --git a/res/drawable/ic_unpin.xml b/res/drawable/ic_unpin.xml
new file mode 100644
index 0000000..557b4f9
--- /dev/null
+++ b/res/drawable/ic_unpin.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <group>
+ <clip-path
+ android:pathData="M0,0h24v24h-24z"/>
+ <path
+ android:pathData="M17,3V5H16V13.175L14,11.175V5H10V7.175L7.825,5L7,4.175V3H17ZM12,23L11,22V16H6V14L8,12V10.85L1.4,4.2L2.8,2.8L21.2,21.2L19.75,22.6L13.15,16H13V22L12,23ZM8.85,14H11.15L10.05,12.9L10,12.85L8.85,14Z"
+ android:fillColor="#FF000000"/>
+ </group>
+</vector>
diff --git a/res/layout/all_apps_personal_work_tabs.xml b/res/layout/all_apps_personal_work_tabs.xml
index b6a8ed8..1435f82 100644
--- a/res/layout/all_apps_personal_work_tabs.xml
+++ b/res/layout/all_apps_personal_work_tabs.xml
@@ -34,6 +34,7 @@
android:layout_weight="1"
android:background="@drawable/all_apps_tabs_background"
android:text="@string/all_apps_personal_tab"
+ android:contentDescription="@string/all_apps_personal_tab_content_description"
android:textColor="@color/all_apps_tab_text"
android:textSize="14sp"
style="?android:attr/borderlessButtonStyle" />
@@ -46,6 +47,7 @@
android:layout_weight="1"
android:background="@drawable/all_apps_tabs_background"
android:text="@string/all_apps_work_tab"
+ android:contentDescription="@string/all_apps_work_tab_content_description"
android:textColor="@color/all_apps_tab_text"
android:textSize="14sp"
style="?android:attr/borderlessButtonStyle" />
diff --git a/res/layout/work_mode_utility_view.xml b/res/layout/work_mode_utility_view.xml
index b68ff3e..568c2c3 100644
--- a/res/layout/work_mode_utility_view.xml
+++ b/res/layout/work_mode_utility_view.xml
@@ -26,6 +26,7 @@
android:layout_height="@dimen/work_scheduler_size"
android:layout_marginBottom="@dimen/work_scheduler_bottom_margin"
android:contentDescription="@string/work_scheduler_button_content_description"
+ android:elevation="@dimen/work_fab_elevation"
android:src="@drawable/ic_schedule"
android:layout_gravity="end"
android:background="@drawable/work_scheduler_background" />
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index ef34ea7..c8ab8ff 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Moenie voorstel nie"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Vasspeldvoorspelling"</string>
<string name="bubble" msgid="3072951361014076670">"Borrel"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Speld aan taakbalk vas"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"installeer kortpaaie"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Laat \'n app toe om kortpaaie by te voeg sonder gebruikerinmenging."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"lees tuis-instellings en -kortpaaie"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Bladsy %1$d van %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Tuisskerm %1$d van %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Nuwe tuisskermbladsy"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Aktief"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Geminimeer"</string>
<string name="folder_opened" msgid="94695026776264709">"Vouer oopgemaak, <xliff:g id="WIDTH">%1$d</xliff:g> by <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Tik om die vouer toe te maak"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Tik om nuwe naam te stoor"</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Maak toe"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Persoonlik"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Werk"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Werkprofiel"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Werkprogramme het \'n kenteken en is sigbaar vir jou IT-administrateur"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"Het dit"</string>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 0d1d350..56e4d56 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"መተግበሪያውን አይጠቁሙ"</string>
<string name="pin_prediction" msgid="4196423321649756498">"የፒን ግምት"</string>
<string name="bubble" msgid="3072951361014076670">"አረፋ"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"ተግባር አሞሌ ላይ ፒን አድርግ"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"አቋራጮችን ይጭናል"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"መተግበሪያው ያለተጠቃሚ ጣልቃ ገብነት አቋራጭ እንዲያክል ያስችለዋል።"</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"የመነሻ ቅንብሮች እና አቋራጮችን ያነባል"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"ገፅ %1$d ከ%2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"መነሻ ማያ ገፅ %1$d ከ%2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"አዲስ የመነሻ ማያ ገፅ"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"ገቢር"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"አንሷል"</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>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"ዝጋ"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"የግል"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"ሥራ"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<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>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 2b7ec21..83b07ec 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"عدم اقتراح التطبيق"</string>
<string name="pin_prediction" msgid="4196423321649756498">"تثبيت التطبيق المتوقّع"</string>
<string name="bubble" msgid="3072951361014076670">"فقاعة"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"التثبيت على \"شريط التطبيقات\""</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"تثبيت اختصارات"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"للسماح لتطبيق ما بإضافة اختصارات بدون تدخل المستخدم."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"الاطلاع على الإعدادات والاختصارات على الشاشة الرئيسية"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"الصفحة %1$d من %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"الشاشة الرئيسية %1$d من %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"صفحة الشاشة الرئيسية الجديدة"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"نشط"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"تم التصغير"</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>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"إغلاق"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"شخصية"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"للعمل"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<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>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index 7125153..1e4861c 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"পৰামৰ্শ নিদিব"</string>
<string name="pin_prediction" msgid="4196423321649756498">"পূৰ্বানুমান কৰা এপ্টো পিন কৰক"</string>
<string name="bubble" msgid="3072951361014076670">"বাবল"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"টাস্কবাৰত পিন কৰক"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"শ্বৰ্টকাট ইনষ্টল কৰিব পাৰে"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"ব্য়ৱহাৰকাৰীৰ হস্তক্ষেপ অবিহনেই কোনো এপক শ্বৰ্টকাটবোৰ যোগ কৰাৰ অনুমতি দিয়ে।"</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"গৃহ স্ক্ৰীনত ছেটিং আৰু শ্বৰ্টকাটসমূহ পঢ়া"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"%2$dৰ %1$d পৃষ্ঠা"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"গৃহ স্ক্ৰীন %2$dৰ %1$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"গৃহ স্ক্ৰীনৰ নতুন পৃষ্ঠা"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"সক্ৰিয়"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"মিনিমাইজ কৰা হৈছে"</string>
<string name="folder_opened" msgid="94695026776264709">"ফ’ল্ডাৰ খোলা হ’ল, <xliff:g id="WIDTH">%1$d</xliff:g> x <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>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"বন্ধ কৰক"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"ব্যক্তিগত"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"কৰ্মস্থান"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<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>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index 7905400..916b738 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Tətbiq təklif olunmasın"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Proqnozlaşdırılan tətbiqi bərkidin"</string>
<string name="bubble" msgid="3072951361014076670">"Qabarcıq"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Tapşırıq panelinə bərkidin"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"qısayolları quraşdır"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Tətbiqə istifadəçi müdaxiləsi olmadan qısayolları əlavə etməyə icazə verir."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"Əsas səhifə ayarlarını və qısayollarını oxumaq"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Səhifə %1$d of %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Əsas Səhifə ekranı %1$d of %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Yeni əsas ekran səhifəsi"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Aktiv"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Kiçildildi"</string>
<string name="folder_opened" msgid="94695026776264709">"Qovluq açıldı, <xliff:g id="HEIGHT">%2$d</xliff:g> hündürlük ilə <xliff:g id="WIDTH">%1$d</xliff:g> enində"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Qovluq bağlamaq üçün toxunun"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Ad dəyişikliyini yadda saxlamaq üçün toxunun"</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Bağlayın"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Şəxsi"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"İş"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"İş profili"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"İş tətbiqləri nişanlanıb və İT administratorunuza görünür"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"Anladım"</string>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index d1ecaeb..c8782e2 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne predlaži aplikaciju"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Zakači predviđanje"</string>
<string name="bubble" msgid="3072951361014076670">"Oblačić"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Zakači za traku zad."</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instaliranje prečica"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Dozvoljava aplikaciji da dodaje prečice bez intervencije korisnika."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"čitanje podešavanja i prečica na početnom ekranu"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"%1$d. stranica od %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"%1$d. početni ekran od %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Nova stranica početnog ekrana"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Aktivno"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Smanjeno"</string>
<string name="folder_opened" msgid="94695026776264709">"Folder je otvoren, <xliff:g id="WIDTH">%1$d</xliff:g> puta <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Dodirnite da biste zatvorili folder"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Dodirnite da biste sačuvali preimenovanje"</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Zatvori"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Lično"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Posao"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Poslovni profil"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Poslovne aplikacije su označene značkom i IT administrator može da ih vidi"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"Važi"</string>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index 3c579cb..671cb6d 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Не прапаноўваць праграму"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Замацаваць прапанаваную праграму"</string>
<string name="bubble" msgid="3072951361014076670">"Бурбалка"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Замацаваць на панэлі"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"Стварэнне ярлыкоў"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Дазваляе праграмам дадаваць ярлыкі без умяшання карыстальніка."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"счытваць налады і ярлыкі на галоўным экране"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Старонка %1$d з %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Галоўны экран %1$d з %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Новая старонка галоўнага экрана"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Актыўна"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Згорнута"</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>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Закрыць"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Асабістыя"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Працоўныя"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Працоўны профіль"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Працоўныя праграмы пазначаны спецыяльнымі значкамі, а таксама бачныя IT-адміністратару"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"Зразумела"</string>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 98571add..a4c3faf 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Без предлагане на приложение"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Фиксиране на предвиждането"</string>
<string name="bubble" msgid="3072951361014076670">"Балонче"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Фикс. в лентата на задач."</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"инсталиране на преки пътища"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Разрешава на приложението да добавя преки пътища без намеса на потребителя."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"четене на настройките и преките пътища на началния екран"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Страница %1$d от %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Начален екран %1$d от %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Нова страница на началния екран"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Активно"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Намалено"</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>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Затваряне"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Лични"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Служебни"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<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>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index b070133..296f088 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"অ্যাপ সাজেস্ট করবেন না"</string>
<string name="pin_prediction" msgid="4196423321649756498">"আপনার প্রয়োজন হতে পারে এমন অ্যাপ পিন করুন"</string>
<string name="bubble" msgid="3072951361014076670">"বাবল"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"টাস্কবারে পিন করুন"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"শর্টকাটগুলি ইনস্টল করে"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"একটি অ্যাপ্লিকেশানকে ব্যবহারকারীর হস্তক্ষেপ ছাড়াই শর্টকাটগুলি যোগ করার অনুমতি দেয়৷"</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"হোম স্ক্রিনে সেটিংস ও শর্টকাট পড়ুন"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"%2$dটির মধ্যে %1$dটি পৃষ্ঠা"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"%2$dটির %1$d নম্বর হোম স্ক্রিন"</string>
<string name="workspace_new_page" msgid="257366611030256142">"নতুন হোম স্ক্রীনের পৃষ্ঠা"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"অ্যাক্টিভ"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"ছোট করা হয়েছে"</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>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"বন্ধ করুন"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"ব্যক্তিগত"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"অফিস"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<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>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index c0568bf..9ab5c4c 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne predlaži aplikaciju"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Zakači predviđanje"</string>
<string name="bubble" msgid="3072951361014076670">"Oblačić"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Zakači na traku zadataka"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instaliraj prečice"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Dopušta aplikaciji dodavanje prečica bez posredovanja korisnika."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"čita postavke na početnom ekranu i prečice"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Strana %1$d od %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Početni ekran %1$d od %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Nova stranica početnog ekrana"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Aktivno"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Minimizirano"</string>
<string name="folder_opened" msgid="94695026776264709">"Folder je otvoren, (š) <xliff:g id="WIDTH">%1$d</xliff:g> (v) <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Dodirnite da zatvorite folder"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Dodirnite da sačuvate promjenu naziva"</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Zatvaranje"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Lično"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Poslovno"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Radni profil"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Poslovne aplikacije su označene i vaš IT administrator ih može vidjeti"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"Razumijem"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index c5daecc..07a21ab 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"No suggereixis l\'aplicació"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Fixa la predicció"</string>
<string name="bubble" msgid="3072951361014076670">"Bombolla"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Fixa a barra tasques"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instal·la dreceres"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permet que una aplicació afegeixi dreceres sense la intervenció de l\'usuari."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"llegir la configuració i les dreceres de la pantalla d\'inici"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Pàgina %1$d de %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Pantalla d\'inici %1$d de %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Pàgina de la pantalla d\'inici nova"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Actiu"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Minimitzat"</string>
<string name="folder_opened" msgid="94695026776264709">"S\'ha obert la carpeta, <xliff:g id="WIDTH">%1$d</xliff:g> per <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Toca per tancar la carpeta"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Toca per desar el nom nou"</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Tanca"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Personal"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Treball"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil de treball"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Les aplicacions de treball tenen una insígnia i el teu administrador de TI les pot veure"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"Entesos"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index b9f0ba2..693c6e5 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Nenavrhovat aplikaci"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Připnout předpověď"</string>
<string name="bubble" msgid="3072951361014076670">"Bublat"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Připnout na panel"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalace zástupce"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Umožňuje aplikaci přidat zástupce bez zásahu uživatele."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"čtení nastavení a zkratek plochy"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Strana %1$d z %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Plocha %1$d z %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Nová stránka plochy"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Aktivní"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Minimalizováno"</string>
<string name="folder_opened" msgid="94695026776264709">"Složka otevřena, rozměry <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Klepnutím složku zavřete"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Klepnutím změnu názvu uložíte"</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Zavřít"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Osobní"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Pracovní"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Pracovní profil"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Pracovní aplikace jsou označené a váš administrátor IT je vidí"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"Rozumím"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 7979c20..bd2bf5a 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Foreslå ikke en app"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Fastgør forslaget"</string>
<string name="bubble" msgid="3072951361014076670">"Boble"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Fastgør til proceslinje"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"installere genveje"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Tillader, at en app tilføjer genveje uden brugerens indgriben."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"læs indstillinger og genveje for startskærm"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Side %1$d ud af %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Startskærm %1$d ud af %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Ny startskærm"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Aktiv"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Minimeret"</string>
<string name="folder_opened" msgid="94695026776264709">"Mappen er åben, <xliff:g id="WIDTH">%1$d</xliff:g> gange <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Tryk for at lukke mappen"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Tryk for at gemme omdøbningen"</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Luk"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Personlig"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Arbejde"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Arbejdsprofil"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Arbejdsapps har badges og kan ses af din it-administrator"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 098c9c1..ecdc73a 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"App nicht vorschlagen"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Vorgeschlagene App fixieren"</string>
<string name="bubble" msgid="3072951361014076670">"Bubble"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"An Taskleiste pinnen"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"Verknüpfungen installieren"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Ermöglicht einer App das Hinzufügen von Verknüpfungen ohne Eingreifen des Nutzers"</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"Einstellungen und Verknüpfungen auf dem Startbildschirm lesen"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Seite %1$d von %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Startbildschirm %1$d von %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Neue Startbildschirmseite"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Aktiv"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Minimiert"</string>
<string name="folder_opened" msgid="94695026776264709">"Ordner geöffnet, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Ordner zum Schließen antippen"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Neuen Namen zum Speichern antippen"</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Schließen"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Privat"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Geschäftlich"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Arbeitsprofil"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Geschäftliche Apps sind gekennzeichnet und für deinen IT-Administrator sichtbar"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 0c10f0d..330c23e 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Να μην προτείνεται"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Καρφίτσωμα πρόβλεψης"</string>
<string name="bubble" msgid="3072951361014076670">"Συννεφάκι"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Καρφ. σε γρ. εργαλείων"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"εγκατάσταση συντομεύσεων"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Επιτρέπει σε μια εφαρμογή την προσθήκη συντομεύσεων χωρίς την παρέμβαση του χρήστη."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"ανάγνωση ρυθμίσεων και συντομεύσεων αρχικής οθόνης"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Σελίδα %1$d από %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Αρχική οθόνη %1$d από %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Νέα σελίδα αρχικής οθόνης"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Ενεργό"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Ελαχιστοποιήθηκε"</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>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Κλείσιμο"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Προσωπικές"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Εργασίας"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Προφίλ εργασίας"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Οι εφαρμογές εργασιών φέρουν σήμα και είναι ορατές στον διαχειριστή IT σας"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"Το κατάλαβα"</string>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index 5a92bf4..6825d80 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Don\'t suggest app"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Pin prediction"</string>
<string name="bubble" msgid="3072951361014076670">"Bubble"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Pin to taskbar"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"install shortcuts"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Allows an app to add shortcuts without user intervention."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"read Home settings and shortcuts"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Page %1$d of %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Home screen %1$d of %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"New home screen page"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Active"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Minimised"</string>
<string name="folder_opened" msgid="94695026776264709">"Folder opened, <xliff:g id="WIDTH">%1$d</xliff:g> by <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Tap to close folder"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Tap to save rename"</string>
@@ -134,7 +135,7 @@
<string name="allow_rotation_title" msgid="7222049633713050106">"Allow home screen rotation"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"When phone is rotated"</string>
<string name="landscape_mode_title" msgid="5138814555934843926">"Landscape mode"</string>
- <string name="landscape_mode_desc" msgid="7372569859592816793">"Set phone into landscape mode"</string>
+ <string name="landscape_mode_desc" msgid="7372569859592816793">"Set phone to landscape mode"</string>
<string name="notification_dots_title" msgid="9062440428204120317">"Notification dots"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"On"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Off"</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Close"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Personal"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Work"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Work profile"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Work apps are badged and visible to your IT admin"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index a3d0e0b..f383e3f 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Don\'t suggest app"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Pin Prediction"</string>
<string name="bubble" msgid="3072951361014076670">"Bubble"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Pin to taskbar"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"install shortcuts"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Allows an app to add shortcuts without user intervention."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"read home settings and shortcuts"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Page %1$d of %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Home screen %1$d of %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"New home screen page"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Active"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Minimized"</string>
<string name="folder_opened" msgid="94695026776264709">"Folder opened, <xliff:g id="WIDTH">%1$d</xliff:g> by <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Tap to close folder"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Tap to save rename"</string>
@@ -187,6 +188,8 @@
<string name="accessibility_close" msgid="2277148124685870734">"Close"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Personal"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Work"</string>
+ <string name="all_apps_personal_tab_content_description" msgid="6286808898381807242">"Personal apps tab"</string>
+ <string name="all_apps_work_tab_content_description" msgid="3835637212347968316">"Work apps tab"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Work profile"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Work apps are badged and visible to your IT admin"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"Got it"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 5a92bf4..6825d80 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Don\'t suggest app"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Pin prediction"</string>
<string name="bubble" msgid="3072951361014076670">"Bubble"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Pin to taskbar"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"install shortcuts"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Allows an app to add shortcuts without user intervention."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"read Home settings and shortcuts"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Page %1$d of %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Home screen %1$d of %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"New home screen page"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Active"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Minimised"</string>
<string name="folder_opened" msgid="94695026776264709">"Folder opened, <xliff:g id="WIDTH">%1$d</xliff:g> by <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Tap to close folder"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Tap to save rename"</string>
@@ -134,7 +135,7 @@
<string name="allow_rotation_title" msgid="7222049633713050106">"Allow home screen rotation"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"When phone is rotated"</string>
<string name="landscape_mode_title" msgid="5138814555934843926">"Landscape mode"</string>
- <string name="landscape_mode_desc" msgid="7372569859592816793">"Set phone into landscape mode"</string>
+ <string name="landscape_mode_desc" msgid="7372569859592816793">"Set phone to landscape mode"</string>
<string name="notification_dots_title" msgid="9062440428204120317">"Notification dots"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"On"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Off"</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Close"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Personal"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Work"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Work profile"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Work apps are badged and visible to your IT admin"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 5a92bf4..6825d80 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Don\'t suggest app"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Pin prediction"</string>
<string name="bubble" msgid="3072951361014076670">"Bubble"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Pin to taskbar"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"install shortcuts"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Allows an app to add shortcuts without user intervention."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"read Home settings and shortcuts"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Page %1$d of %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Home screen %1$d of %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"New home screen page"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Active"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Minimised"</string>
<string name="folder_opened" msgid="94695026776264709">"Folder opened, <xliff:g id="WIDTH">%1$d</xliff:g> by <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Tap to close folder"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Tap to save rename"</string>
@@ -134,7 +135,7 @@
<string name="allow_rotation_title" msgid="7222049633713050106">"Allow home screen rotation"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"When phone is rotated"</string>
<string name="landscape_mode_title" msgid="5138814555934843926">"Landscape mode"</string>
- <string name="landscape_mode_desc" msgid="7372569859592816793">"Set phone into landscape mode"</string>
+ <string name="landscape_mode_desc" msgid="7372569859592816793">"Set phone to landscape mode"</string>
<string name="notification_dots_title" msgid="9062440428204120317">"Notification dots"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"On"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Off"</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Close"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Personal"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Work"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Work profile"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Work apps are badged and visible to your IT admin"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 1d337e0..a287135 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"No sugerir app"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Fijar predicción"</string>
<string name="bubble" msgid="3072951361014076670">"Burbuja"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Fijar a barra"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar accesos directos"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite que una aplicación agregue accesos directos sin que el usuario intervenga."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"leer parámetros de configuración y accesos directos de la página principal"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Página %1$d de %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Pantalla principal %1$d de %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Nueva página en la pantalla principal"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Activo"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Minimizado"</string>
<string name="folder_opened" msgid="94695026776264709">"Carpeta abierta, <xliff:g id="WIDTH">%1$d</xliff:g> por <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Presiona para cerrar la carpeta"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Presiona para guardar el cambio de nombre"</string>
@@ -187,6 +188,8 @@
<string name="accessibility_close" msgid="2277148124685870734">"Cerrar"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Personal"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Trabajo"</string>
+ <string name="all_apps_personal_tab_content_description" msgid="6286808898381807242">"Pestaña de apps personales"</string>
+ <string name="all_apps_work_tab_content_description" msgid="3835637212347968316">"Pestaña de apps de trabajo"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil de trabajo"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Las apps de trabajo tienen una insignia y el administrador de TI las puede ver"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"Entendido"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 0dbcbe5..e3ab38d 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"No sugerir aplicación"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Fijar predicción"</string>
<string name="bubble" msgid="3072951361014076670">"Burbuja"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Fijar a barra de tareas"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar accesos directos"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite que una aplicación añada accesos directos sin intervención del usuario."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"leer ajustes y accesos directos de la pantalla de inicio"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Página %1$d de %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Pantalla de inicio %1$d de %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Nueva página de pantalla de inicio"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Activa"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Minimizada"</string>
<string name="folder_opened" msgid="94695026776264709">"Carpeta abierta, <xliff:g id="WIDTH">%1$d</xliff:g> por <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Toca para cerrar la carpeta"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Toca para guardar el nuevo nombre"</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Cerrar"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Personal"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Trabajo"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil de trabajo"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Las aplicaciones de trabajo tienen una insignia, y tu administrador de TI las puede ver"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"Entendido"</string>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index f242f1e..3df323d 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Ära soovita rakendust"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Kinnita ennustus"</string>
<string name="bubble" msgid="3072951361014076670">"Mull"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Kinnita tegumiribale"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"installi otseteed"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Võimaldab rakendusel lisada otseteid kasutaja sekkumiseta."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"avakuva seadete ja otseteede lugemine"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Leht %1$d/%2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Avakuva %1$d/%2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Uus avakuva leht"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Ühendatud"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Minimeeritud"</string>
<string name="folder_opened" msgid="94695026776264709">"Kaust on avatud, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Puudutage kausta sulgemiseks"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Puudutage ümbernimetamise salvestamiseks"</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Sule"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Isiklik"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Töö"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Tööprofiil"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Töörakendustel on märk ja need on teie IT-administraatorile nähtavad"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"Selge"</string>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 1fcd004..0b30b38 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Ez iradoki aplikazioa"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Ainguratu iragarpena"</string>
<string name="bubble" msgid="3072951361014076670">"Burbuila"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Ainguratu zereginen barran"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"Instalatu lasterbideak"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Erabiltzaileak ezer egin gabe lasterbideak gehitzeko baimena ematen die aplikazioei."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"irakurri hasierako pantailako ezarpenak eta lasterbideak"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"%1$d/%2$d orria"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"%1$d/%2$d orri nagusi"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Orri nagusiaren orri berria"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Aktibo"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Minimizatuta"</string>
<string name="folder_opened" msgid="94695026776264709">"Karpeta ireki da: <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">"Karpeta ixteko, sakatu hau"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Izen berria gordetzeko, sakatu hau"</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Itxi"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Pertsonalak"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Lanekoak"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Laneko profila"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Laneko aplikazioek bereizgarriak dituzte, eta IKT saileko administratzaileak ikus ditzake"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"Ados"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index abaaa89..671c7f2 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"برنامه پیشنهاد داده نشود"</string>
<string name="pin_prediction" msgid="4196423321649756498">"سنجاق کردن پیشنهاد"</string>
<string name="bubble" msgid="3072951361014076670">"حبابک"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"سنجاق به نوار وظیفه"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"نصب میانبرها"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"به برنامه اجازه میدهد میانبرها را بدون دخالت کاربر اضافه کند."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"خواندن تنظیمات و میانبرهای صفحه اصلی"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"صفحه %1$d از %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"صفحه اصلی %1$d از %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"صفحه اصلی جدید"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"فعال"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"کوچکشده"</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>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"بستن"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"شخصی"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"کاری"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<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>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 2c43a25..49b2b57 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Älä ehdota sovellusta"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Kiinnitä sovellus"</string>
<string name="bubble" msgid="3072951361014076670">"Kupla"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Kiinnitä t.palkkiin"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"asenna pikakuvakkeita"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Antaa sovelluksen lisätä pikakuvakkeita itsenäisesti ilman käyttäjän valintaa."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"lukea aloitusnäytön asetuksia ja pikakuvakkeita"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Sivu %1$d / %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Aloitusruutu %1$d/%2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Uusi aloitusnäytön sivu"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Aktiivinen"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Pienennetty"</string>
<string name="folder_opened" msgid="94695026776264709">"Kansio avattu, koko <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Sulje kansio koskettamalla."</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Tallenna uusi nimi koskettamalla."</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Sulje"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Henkilökohtaiset"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Työsovellukset"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Työprofiili"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Työsovellukset on merkitty sellaisiksi ja näkyvät IT-järjestelmänvalvojille"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"Selvä"</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index c191aae..6c39282 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne pas suggérer d\'appli"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Épingler la prédiction"</string>
<string name="bubble" msgid="3072951361014076670">"Bulle"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Éping. (barre tâche)"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"installer des raccourcis"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permet à une appli d\'ajouter des raccourcis sans l\'intervention de l\'utilisateur."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"lire les paramètres et les raccourcis de la page d\'accueil"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Page %1$d sur %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Écran d\'accueil %1$d sur %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Nouvelle page d\'écran d\'accueil"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Actif"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Réduit"</string>
<string name="folder_opened" msgid="94695026776264709">"Dossier ouvert, <xliff:g id="WIDTH">%1$d</xliff:g> par <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Touchez pour fermer le dossier"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Touchez pour enregistrer le nouveau nom"</string>
@@ -134,7 +135,7 @@
<string name="allow_rotation_title" msgid="7222049633713050106">"Autoriser la rotation de l\'écran d\'accueil"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Lorsque vous faites pivoter le téléphone"</string>
<string name="landscape_mode_title" msgid="5138814555934843926">"Mode paysage"</string>
- <string name="landscape_mode_desc" msgid="7372569859592816793">"Configurer le téléphone en mode paysage"</string>
+ <string name="landscape_mode_desc" msgid="7372569859592816793">"Configurez le téléphone en mode paysage"</string>
<string name="notification_dots_title" msgid="9062440428204120317">"Pastilles de notification"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Activé"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Désactivé"</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Fermer"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Personnel"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Travail"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil professionnel"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Les applis professionnelles sont indiquées par un badge et elles sont visibles pour votre administrateur informatique"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index e4c40e5..73902b4 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne pas suggérer d\'appli"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Épingler la prédiction"</string>
<string name="bubble" msgid="3072951361014076670">"Bulle"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Épingler à la barre"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"installer des raccourcis"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permettre à une application d\'ajouter des raccourcis sans l\'intervention de l\'utilisateur"</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"Lire les paramètres et les raccourcis de la page d\'accueil"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Page %1$d sur %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Écran d\'accueil %1$d sur %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Nouvelle page d\'écran d\'accueil"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Actif"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Minimisé"</string>
<string name="folder_opened" msgid="94695026776264709">"Dossier ouvert, <xliff:g id="WIDTH">%1$d</xliff:g> par <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Appuyez pour fermer le dossier."</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Appuyez pour enregistrer le nouveau nom du dossier."</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Fermer"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Personnel"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Professionnel"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil professionnel"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Les applis professionnelles sont identifiées par un badge et votre administrateur informatique peut les voir"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index 4ded582..1619b14 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Non suxerir app"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Fixar predición"</string>
<string name="bubble" msgid="3072951361014076670">"Burbulla"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Fixar na barra"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar atallos"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite a unha aplicación engadir atallos sen intervención do usuario."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"ler a configuración e os atallos da pantalla de inicio"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Páxina %1$d de %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Pantalla de inicio %1$d de %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Nova páxina da pantalla de inicio"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Activa"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Minimizada"</string>
<string name="folder_opened" msgid="94695026776264709">"Abriuse o cartafol, <xliff:g id="WIDTH">%1$d</xliff:g> por <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Toca fóra para pechar o cartafol"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Toca fóra para cambiar o nome do cartafol"</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Pechar"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Persoal"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Traballo"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil de traballo"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"O administrador de TI pode ver as aplicacións do traballo e engadirlles indicadores"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"Entendido"</string>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index 5a33983..bd40d19 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"ઍપ સૂચવશો નહીં"</string>
<string name="pin_prediction" msgid="4196423321649756498">"પૂર્વાનુમાનને પિન કરો"</string>
<string name="bubble" msgid="3072951361014076670">"બબલ"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"ટાસ્કબારમાં પિન કરો"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"શૉર્ટકટ ઇન્સ્ટૉલ કરો"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"એપ્લિકેશનને વપરાશકર્તા હસ્તક્ષેપ વગર શોર્ટકટ્સ ઉમેરવાની મંજૂરી આપે છે."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"હોમ સેટિંગ અને શૉર્ટકટ વાંચો"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"%2$d માંથી %1$d પૃષ્ઠ"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"%2$d માંથી %1$d હોમ સ્ક્રીન"</string>
<string name="workspace_new_page" msgid="257366611030256142">"નવું હોમ સ્ક્રીન પૃષ્ઠ"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"સક્રિય"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"ન્યૂનતમ"</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>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"બંધ કરો"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"વ્યક્તિગત ઍપ"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"ઑફિસની ઍપ"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"ઑફિસની પ્રોફાઇલ"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"ઑફિસની ઍપને બૅજ આપેલા હોય છે અને તમારા IT ઍડમિન તેમને જોઈ શકે છે"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"સમજાઈ ગયું"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 8696ff8..15485a2 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"ऐप्लिकेशन का सुझाव न दें"</string>
<string name="pin_prediction" msgid="4196423321649756498">"सुझाए गए ऐप पिन करें"</string>
<string name="bubble" msgid="3072951361014076670">"बबल"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"टास्कबार में पिन करें"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"शॉर्टकट इंस्टॉल करें"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"ऐप को उपयोगकर्ता के हस्तक्षेप के बिना शॉर्टकट जोड़ने देती है."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"होम स्क्रीन की सेटिंग और शॉर्टकट पढ़ने की अनुमति"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"पेज %2$d में से %1$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"होम स्क्रीन %2$d में से %1$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"नया होम स्क्रीन पेज"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"चालू है"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"छोटा किया गया"</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>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"बंद करें"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"निजी ऐप्लिकेशन"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"वर्क ऐप्लिकेशन"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<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>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index d859f58..48069a2 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne predlaži aplikaciju"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Prikvači predviđenu apl."</string>
<string name="bubble" msgid="3072951361014076670">"Oblačić"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Prikvači na traku"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instaliranje prečaca"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Aplikaciji omogućuje dodavanje prečaca bez intervencije korisnika."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"čitati postavke i prečace početnog zaslona"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Stranica %1$d od %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Početni zaslon %1$d od %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Nova stranica početnog zaslona"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Aktivno"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Minimizirano"</string>
<string name="folder_opened" msgid="94695026776264709">"Mapa je otvorena, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Dodirnite da biste zatvorili mapu"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Dodirnite da biste spremili promijenjeni naziv"</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Zatvori"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Osobno"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Posao"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Poslovni profil"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Poslovne su aplikacije označene i vidljive vašem IT administratoru"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"Shvaćam"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 1a1f405..a298738 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne javasoljon appot"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Várható kitűzése"</string>
<string name="bubble" msgid="3072951361014076670">"Buborék"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Rögzítés a tálcán"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"parancsikonok telepítése"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Lehetővé teszi egy alkalmazás számára, hogy felhasználói beavatkozás nélkül adjon hozzá parancsikonokat."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"kezdőképernyő beállításainak és parancsikonjainak olvasása"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"%2$d/%1$d. oldal"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"%2$d/%1$d. kezdőképernyő"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Új kezdőképernyő oldal"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Aktív"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Kis méret"</string>
<string name="folder_opened" msgid="94695026776264709">"Mappa megnyitva – szélesség: <xliff:g id="WIDTH">%1$d</xliff:g>; magasság: <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Érintse meg a mappa bezárásához"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Koppintson ide az átnevezés mentéséhez"</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Bezárás"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Személyes"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Munkahelyi"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Munkaprofil"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"A munkahelyi alkalmazások jelvénnyel vannak megjelölve, és ezeket láthatja a rendszergazda"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"Értem"</string>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index 036e9c9..7d805a6 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Չառաջարկել"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Ամրացնել առաջարկվող հավելվածը"</string>
<string name="bubble" msgid="3072951361014076670">"Ամպիկ"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Ամրացնել վահանակում"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"Դյուրանցումների տեղադրում"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Հավելվածին թույլ է տալիս ավելացնել դյուրանցումներ՝ առանց օգտագործողի միջամտության:"</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"կարդալ հիմնական էկրանի կարգավորումներն ու դյուրանցումները"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Էջ %1$d՝ %2$d-ից"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Հիմնական էկրան %1$d` %2$d-ից"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Հիմնական էկրանի նոր էջ"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Ակտիվ է"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Նվազեցվել է"</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>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Փակել"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Անձնական"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Աշխատանքային"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<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>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 2ba2997..047c074 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Jangan sarankan apl"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Pin Prediksi"</string>
<string name="bubble" msgid="3072951361014076670">"Balon"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Sematkan ke taskbar"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"memasang pintasan"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Mengizinkan aplikasi menambahkan pintasan tanpa campur tangan pengguna."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"membaca setelan dan pintasan layar utama"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Halaman %1$d dari %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Layar utama %1$d dari %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Halaman layar utama baru"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Aktif"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Diperkecil"</string>
<string name="folder_opened" msgid="94695026776264709">"Folder dibuka, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Ketuk untuk menutup folder"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Ketuk untuk menyimpan ganti nama"</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Tutup"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Pribadi"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Kerja"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil kerja"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Aplikasi kerja diberi badge dan terlihat oleh admin IT"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"Oke"</string>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index 9316f90..19b967f 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Ekki fá tillögu að forriti"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Festa tillögu"</string>
<string name="bubble" msgid="3072951361014076670">"Blaðra"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Festa á forritastiku"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"setja upp flýtileiðir"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Leyfir forriti að bæta við flýtileiðum án íhlutunar notanda."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"lesa stillingar og flýtileiðir heimaskjás"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Síða %1$d af %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Heimaskjár %1$d af %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Ný síða á heimaskjá"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Virkt"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Minnkað"</string>
<string name="folder_opened" msgid="94695026776264709">"Mappa opnuð, <xliff:g id="WIDTH">%1$d</xliff:g> sinnum <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Ýttu til að loka möppunni"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Ýttu til að vista breytt heiti"</string>
@@ -187,6 +188,8 @@
<string name="accessibility_close" msgid="2277148124685870734">"Loka"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Persónulegt"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Vinna"</string>
+ <string name="all_apps_personal_tab_content_description" msgid="6286808898381807242">"Flipi forrita til einkanota"</string>
+ <string name="all_apps_work_tab_content_description" msgid="3835637212347968316">"Flipi vinnuforrita"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Vinnusnið"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Vinnuforrit eru merkt og kerfisstjórinn getur séð þau"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"Ég skil"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 3145133..fd747d1 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Non suggerire app"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Blocca previsione"</string>
<string name="bubble" msgid="3072951361014076670">"Fumetto"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Fissa alla barra app"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"Aggiunta di scorciatoie"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Consente a un\'app di aggiungere scorciatoie automaticamente."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"leggere le impostazioni e le scorciatoie nella schermata Home"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Pagina %1$d di %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Schermata Home %1$d di %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Nuova pagina Schermata Home"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Attiva"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Ridotta a icona"</string>
<string name="folder_opened" msgid="94695026776264709">"Cartella aperta, <xliff:g id="WIDTH">%1$d</xliff:g> per <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Tocca per chiudere la cartella"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Tocca per salvare il nuovo nome"</string>
@@ -187,6 +188,8 @@
<string name="accessibility_close" msgid="2277148124685870734">"Esci"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Personali"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Lavoro"</string>
+ <string name="all_apps_personal_tab_content_description" msgid="6286808898381807242">"Scheda App personali"</string>
+ <string name="all_apps_work_tab_content_description" msgid="3835637212347968316">"Scheda App di lavoro"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Profilo di lavoro"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Le app di lavoro sono contrassegnate con un badge e visibili all\'amministratore IT"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 34bfbad..e829738 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"בלי להציע את האפליקציה"</string>
<string name="pin_prediction" msgid="4196423321649756498">"הצמדת החיזוי"</string>
<string name="bubble" msgid="3072951361014076670">"בועה"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"הצמדה לשורת המשימות"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"התקנת קיצורי דרך"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"מאפשר לאפליקציה להוסיף קיצורי דרך ללא התערבות המשתמש."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"קריאת ההגדרות וקיצורי הדרך בדף הבית"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"דף %1$d מתוך %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"מסך הבית %1$d מתוך %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"מסך הבית חדש"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"פעיל"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"ממוזער"</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>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"סגירה"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"אישי"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"עבודה"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"פרופיל עבודה"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"האפליקציות לעבודה מתויגות ומוצגות למנהל ה-IT"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"הבנתי"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 3d42652..1e530cd 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"アプリを表示しない"</string>
<string name="pin_prediction" msgid="4196423321649756498">"アプリの候補を固定"</string>
<string name="bubble" msgid="3072951361014076670">"ふきだし"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"タスクバーに固定"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"ショートカットのインストール"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"ユーザー操作なしでショートカットを追加することをアプリに許可します。"</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"ホームの設定とショートカットの読み取り"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"%1$d/%2$dページ"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"ホーム画面: %1$d/%2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"新しいホーム画面ページ"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"有効"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"最小化"</string>
<string name="folder_opened" msgid="94695026776264709">"フォルダが開いています。<xliff:g id="WIDTH">%1$d</xliff:g>x<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>
@@ -134,7 +135,7 @@
<string name="allow_rotation_title" msgid="7222049633713050106">"ホーム画面の回転を許可"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"スマートフォンの向きに合わせます"</string>
<string name="landscape_mode_title" msgid="5138814555934843926">"横表示"</string>
- <string name="landscape_mode_desc" msgid="7372569859592816793">"スマートフォンを横表示にしてください"</string>
+ <string name="landscape_mode_desc" msgid="7372569859592816793">"スマートフォンを横表示にします"</string>
<string name="notification_dots_title" msgid="9062440428204120317">"通知ドット"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"ON"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"OFF"</string>
@@ -187,6 +188,8 @@
<string name="accessibility_close" msgid="2277148124685870734">"閉じる"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"個人用"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"仕事用"</string>
+ <string name="all_apps_personal_tab_content_description" msgid="6286808898381807242">"個人用アプリのタブ"</string>
+ <string name="all_apps_work_tab_content_description" msgid="3835637212347968316">"仕事用アプリのタブ"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"仕事用プロファイル"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"仕事用アプリはバッジ付きで表示され、IT 管理者に公開されます"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index 6a6d30d..4cd473b 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"არ შემომთავაზო აპი"</string>
<string name="pin_prediction" msgid="4196423321649756498">"ჩამაგრების პროგნოზირება"</string>
<string name="bubble" msgid="3072951361014076670">"ბუშტი"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"ამოცანათა ზოლში ჩამაგრება"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"მალსახმობების დაყენება"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"აპისთვის მალსახმობების დამოუკიდებლად დამატების უფლების მიცემა."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"მთავარი ეკრანის პარამეტრებისა და მალსახმობების წაკითხვა"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"გვერდი %1$d %2$d-დან"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"მთავარი ეკრანი %1$d, %2$d-დან"</string>
<string name="workspace_new_page" msgid="257366611030256142">"მთავარი ეკრანის ახალი გვერდი"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"აქტიური"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"დაპატარავებული"</string>
<string name="folder_opened" msgid="94695026776264709">"საქაღალდე გახსნილია, <xliff:g id="WIDTH">%1$d</xliff:g> x <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>
@@ -187,6 +188,8 @@
<string name="accessibility_close" msgid="2277148124685870734">"დახურვა"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"პირადი"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"სამსახური"</string>
+ <string name="all_apps_personal_tab_content_description" msgid="6286808898381807242">"პირადი აპების ჩანართი"</string>
+ <string name="all_apps_work_tab_content_description" msgid="3835637212347968316">"სამსახურის აპების ჩანართი"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"სამსახურის პროფილი"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"სამსახურის აპები ბეჯით არის მონიშნული და ხილულია თქვენი IT ადმინისტრატორისთვის"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"გასაგებია"</string>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index 4f457ee..c60376b 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Қолданба ұсынбау"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Болжамды бекіту"</string>
<string name="bubble" msgid="3072951361014076670">"Қалқыма терезе"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Тапсырмалар жолағына бекіту"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"таңбаша орнату"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Қолданбаға пайдаланушының қатысуынсыз төте пернелерді қосу мүмкіндігін береді."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"негізгі экран параметрлері мен таңбашаларын оқу"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"%1$d бет, барлығы %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"%1$d негізгі экран, барлығы %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Жаңа негізгі экран беті"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Белсенді"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Кішірейтілген"</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>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Жабу"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Жеке"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Жұмыс"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<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>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index 6f3fb5c..5dbae5f 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"កុំណែនាំកម្មវិធី"</string>
<string name="pin_prediction" msgid="4196423321649756498">"ខ្ទាស់ការព្យាករ"</string>
<string name="bubble" msgid="3072951361014076670">"ពពុះ"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"ខ្ទាស់ទៅរបារកិច្ចការ"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"ដំឡើងផ្លូវកាត់"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"អនុញ្ញាតឲ្យកម្មវិធីបន្ថែមផ្លូវកាត់ ដោយមិនចាំបាច់អំពើពីអ្នកប្រើ។"</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"អានការកំណត់ និងផ្លូវកាត់របស់អេក្រង់ដើម"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"ទំព័រ %1$d នៃ %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"អេក្រង់ដើម %1$d នៃ %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"ទំព័រអេក្រង់ដើមថ្មី"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"សកម្ម"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"បានបង្រួម"</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>
@@ -187,6 +188,8 @@
<string name="accessibility_close" msgid="2277148124685870734">"បិទ"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"ផ្ទាល់ខ្លួន"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"ការងារ"</string>
+ <string name="all_apps_personal_tab_content_description" msgid="6286808898381807242">"ផ្ទាំងកម្មវិធីផ្ទាល់ខ្លួន"</string>
+ <string name="all_apps_work_tab_content_description" msgid="3835637212347968316">"ផ្ទាំងកម្មវិធីការងារ"</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>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 5cb996c..cd5d59d 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"ಆ್ಯಪ್ ಅನ್ನು ಸೂಚಿಸಬೇಡಿ"</string>
<string name="pin_prediction" msgid="4196423321649756498">"ಮುನ್ನೋಟ ಪಿನ್ ಮಾಡಿ"</string>
<string name="bubble" msgid="3072951361014076670">"ಬಬಲ್"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"ಟಾಸ್ಕ್ಬಾರ್ಗೆ ಪಿನ್ ಮಾಡಿ"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"ಶಾರ್ಟ್ಕಟ್ಗಳನ್ನು ಸ್ಥಾಪಿಸಿ"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"ಬಳಕೆದಾರರ ಹಸ್ತಕ್ಷೇಪವಿಲ್ಲದೆ ಶಾರ್ಟ್ಕಟ್ಗಳನ್ನು ಸೇರಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"ಹೋಮ್ ಸ್ಕ್ರೀನ್ ಸೆಟ್ಟಿಂಗ್ಗಳು ಮತ್ತು ಶಾರ್ಟ್ಕಟ್ಗಳನ್ನು ಓದಿ"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"%2$d ರಲ್ಲಿ %1$d ಪುಟ"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"%2$d ರಲ್ಲಿ %1$d ಮುಖಪುಟದ ಸ್ಕ್ರೀನ್"</string>
<string name="workspace_new_page" msgid="257366611030256142">"ಹೊಸ ಮುಖಪುಟ ಸ್ಕ್ರೀನ್"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"ಸಕ್ರಿಯವಾಗಿದೆ"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"ಮಿನಿಮೈಸ್ ಮಾಡಲಾಗಿದೆ"</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>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"ಮುಚ್ಚಿರಿ"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"ವೈಯಕ್ತಿಕ"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"ಕೆಲಸ"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"ಕೆಲಸದ ಪ್ರೊಫೈಲ್"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"ಕೆಲಸಕ್ಕೆ ಸಂಬಂಧಿಸಿದ ಆ್ಯಪ್ಗಳನ್ನು ಬ್ಯಾಡ್ಜ್ ಮಾಡಲಾಗಿದೆ ಮತ್ತು ಅವುಗಳು ನಿಮ್ಮ IT ನಿರ್ವಾಹಕರಿಗೆ ಗೋಚರಿಸುತ್ತವೆ"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"ಸರಿ"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index e5cae63..24053c8 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"앱 제안 받지 않음"</string>
<string name="pin_prediction" msgid="4196423321649756498">"예상 앱 고정"</string>
<string name="bubble" msgid="3072951361014076670">"풍선"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"태스크 바에 고정"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"바로가기 설치"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"앱이 사용자의 작업 없이 바로가기를 추가할 수 있도록 합니다."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"홈 설정 및 바로가기 읽기"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"페이지 %1$d/%2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"홈 화면 %1$d/%2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"새로운 홈 화면 페이지"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"활성"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"최소화"</string>
<string name="folder_opened" msgid="94695026776264709">"폴더 열림(<xliff:g id="WIDTH">%1$d</xliff:g>X<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>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"닫기"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"개인"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"직장"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"직장 프로필"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"직장 앱에는 배지가 있으며, IT 관리자는 직장 앱을 확인할 수 있습니다"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"확인"</string>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index b98b61d..2ad0668 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Cунушталбасын"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Божомолдонгон колдонмону кадап коюу"</string>
<string name="bubble" msgid="3072951361014076670">"Көбүкчө"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Тапшырмалар панелине кадоо"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"тез чакырмаларды орнотуу"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Колдонмого колдонуучуга кайрылбастан тез чакырма кошууга уруксат берет."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"үйдүн параметрлерин жана ыкчам баскычтарын окуу"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"%2$d ичинен %1$d барак"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Үй экраны %2$d ичинен %1$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Жаңы башкы экран барагы"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Жигердүү"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Кичирейтилди"</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>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Жабуу"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Жеке колдонмолор"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Жумуш колдонмолору"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Жумуш профили"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Жумуш колдонмолору белгиленип, аларды IT администраторлору көрөт"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"Түшүндүм"</string>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index 457c185..2aaf40b 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"ຢ່າແນະນຳແອັບ"</string>
<string name="pin_prediction" msgid="4196423321649756498">"ປັກໝຸດການຄາດເດົາ"</string>
<string name="bubble" msgid="3072951361014076670">"ຟອງ"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"ປັກໝຸດໃສ່ແຖບໜ້າວຽກ"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"ຕິດຕັ້ງທາງລັດ"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"ອະນຸຍາດໃຫ້ແອັບຯ ເພີ່ມທາງລັດໂດຍບໍ່ຕ້ອງຮັບການຢືນຢັນຈາກຜູ່ໃຊ້."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"ອ່ານການຕັ້ງຄ່າໜ້າຫຼັກ ແລະ ທາງລັດ"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"ໜ້າ %1$d ຈາກ %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"ໜ້າຈໍຫຼັກ %1$d ໃນ %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"ໜ້າຂອງໜ້າຈໍຫຼັກໃໝ່"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"ນຳໃຊ້ຢູ່"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"ຫຍໍ້ລົງແລ້ວ"</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>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"ປິດ"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"ສ່ວນຕົວ"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"ວຽກ"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<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>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 1a4a3f2..aeba444 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Nesiūlyti programos"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Prisegti numatymą"</string>
<string name="bubble" msgid="3072951361014076670">"Debesėlis"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Pris. prie užd. j."</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"įdiegti sparčiuosius klavišus"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Programai leidžiama pridėti sparčiuosius klavišus be naudotojo įsikišimo."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"skaityti pagrindinio ekrano nustatymus ir sparčiuosius klavišus"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"%1$d psl. iš %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"%1$d pagrindinis ekranas iš %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Naujas pagrindinio ekrano puslapis"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Aktyvi"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Sumažinta"</string>
<string name="folder_opened" msgid="94695026776264709">"Atidarytas aplankas, <xliff:g id="WIDTH">%1$d</xliff:g> ir <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Palieskite, kad uždarytumėte aplanką"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Palieskite, kad išsaugotumėte pakeistą pavadinimą"</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Uždaryti"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Asmeninės"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Darbo"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Darbo profilis"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Darbo programos yra pažymėtos ženkleliu ir matomos IT administratoriui"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"Supratau"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 857b492..e1b9b34 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Neieteikt lietotni"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Piespraust prognozēto lietotni"</string>
<string name="bubble" msgid="3072951361014076670">"Burbulis"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Pie uzdevumu joslas"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalēt saīsnes"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Ļauj lietotnei pievienot saīsnes, nejautājot lietotājam."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"sākuma ekrāna iestatījumu un saīšņu lasīšana"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"%1$d. lapa no %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Sākuma ekrāns: %1$d no %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Jauna sākuma ekrāna lapa"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Aktīva"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Minimizēta"</string>
<string name="folder_opened" msgid="94695026776264709">"Atvērta mape: <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Pieskarieties, lai aizvērtu mapi."</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Pieskarieties, lai saglabātu jauno nosaukumu."</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Aizvērt"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Personīgās lietotnes"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Darba lietotnes"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Darba profils"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Darba lietotnēm ir pievienota emblēma, un tās ir redzamas jūsu IT administratoram"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"Labi"</string>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 450d5d6..8ab50b8 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Не предлагај апл."</string>
<string name="pin_prediction" msgid="4196423321649756498">"Закачи го предвидувањето"</string>
<string name="bubble" msgid="3072951361014076670">"Балонче"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Закачи на лентата"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"инсталирање кратенки"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Овозможува апликацијата да додава кратенки без интервенција на корисникот."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"да чита поставки и кратенки на почетна страница"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Страница %1$d од %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Екран на почетна страница %1$d од %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Нова страница на почетен екран"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Активно"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Минимизирано"</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>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Затвори"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Лично"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"За работа"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Работен профил"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Работните апликации имаат значка и се видливи за IT-администраторот"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"Сфатив"</string>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index 4d65869..d6df3ac 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"ആപ്പ് നിർദ്ദേശിക്കേണ്ട"</string>
<string name="pin_prediction" msgid="4196423321649756498">"പ്രവചനം പിൻ ചെയ്യുക"</string>
<string name="bubble" msgid="3072951361014076670">"ബബിൾ"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"ടാസ്ക്ബാറിൽ പിൻ ചെയ്യൂ"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"കുറുക്കുവഴികൾ ഇൻസ്റ്റാളുചെയ്യുക"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"ഉപയോക്തൃ ഇടപെടൽ ഇല്ലാതെ കുറുക്കുവഴികൾ ചേർക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"ഹോം ക്രമീകരണവും കുറുക്കുവഴികളും വായിക്കുക"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"പേജ് %1$d / %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"ഹോം സ്ക്രീൻ %1$d / %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"പുതിയ ഹോം സ്ക്രീൻ പേജ്"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"സജീവം"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"ചെറുതാക്കി"</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>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"അടയ്ക്കൂ"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"വ്യക്തിപരം"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Work"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<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>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index 9e40f08..7803296 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Апп бүү санал болго"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Таамаглалыг бэхлэх"</string>
<string name="bubble" msgid="3072951361014076670">"Бөмбөлөг"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Ажлын хэсэгт бэхлэх"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"товчлол суулгах"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Апп нь хэрэглэгчийн оролцоогүйгээр товчлолыг нэмэж чадна"</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"нүүрний тохиргоо болон товчлолыг унших"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"%2$d-н %1$d хуудас"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"%2$d-н Нүүр дэлгэц %1$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Шинэ үндсэн нүүр хуудас"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Идэвхтэй"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Жижгэрүүлсэн"</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>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Хаах"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Хувийн"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Ажил"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Ажлын профайл"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Ажлын аппуудыг тэмдэглэсэн бөгөөд танай IT админд харагдана"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"Ойлголоо"</string>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index 8907bcf..b0cda77 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"ॲप सुचवू नका"</string>
<string name="pin_prediction" msgid="4196423321649756498">"पूर्वानुमान पिन करा"</string>
<string name="bubble" msgid="3072951361014076670">"बबल"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"टास्कबारवर पिन करा"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"शॉर्टकट इंस्टॉल करा"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"वापरकर्ता हस्तक्षेपाशिवाय शॉर्टकट जोडण्यास अॅप ला अनुमती देते."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"होम सेटिंग्ज आणि शॉर्टकट वाचा"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"%2$d पैकी %1$d पेज"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"%2$d पैकी %1$d मुख्य स्क्रीन"</string>
<string name="workspace_new_page" msgid="257366611030256142">"नवीन होम स्क्रीन पेज"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"अॅक्टिव्ह"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"लहान केलेले"</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>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"बंद करा"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"वैयक्तिक"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"कार्य"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<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>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index 90245af..c4d97d5 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Jangan cadangkan apl"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Sematkan Ramalan"</string>
<string name="bubble" msgid="3072951361014076670">"Gelembung"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Semat pada bar tugas"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"pasang pintasan"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Membenarkan apl menambah pintasan tanpa campur tangan pengguna."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"membaca tetapan dan pintasan skrin utama"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Halaman %1$d daripada %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Skrin Laman Utama %1$d daripada %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Halaman skrin utama baharu"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Aktif"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Minimumkan"</string>
<string name="folder_opened" msgid="94695026776264709">"Folder dibuka, <xliff:g id="WIDTH">%1$d</xliff:g> kali <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Ketik untuk menutup folder"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Ketik untuk menyimpan penamaan semula"</string>
@@ -187,6 +188,8 @@
<string name="accessibility_close" msgid="2277148124685870734">"Tutup"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Peribadi"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Kerja"</string>
+ <string name="all_apps_personal_tab_content_description" msgid="6286808898381807242">"Tab apl peribadi"</string>
+ <string name="all_apps_work_tab_content_description" msgid="3835637212347968316">"Tab apl kerja"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil kerja"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Apl kerja mempunyai lencana dan kelihatan kepada pentadbir IT anda"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index d6410da..75e87f4 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"အက်ပ်အကြံမပြုပါနှင့်"</string>
<string name="pin_prediction" msgid="4196423321649756498">"ခန့်မှန်းချက်ကို ပင်ထိုးရန်"</string>
<string name="bubble" msgid="3072951361014076670">"ပူဖောင်းကွက်"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Taskbar ၌ပင်ထိုးရန်"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"ဖြတ်လမ်းလင့်ခ်များ ထည့်သွင်းခြင်း"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"အသုံးပြုသူ လုပ်ဆောင်မှုမရှိပဲ အပ်ပလီကေးရှင်းကို အတိုကောက်မှတ်သားမှုများ ပြုလုပ်ခွင့် ပေးခြင်း"</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"ပင်မဆက်တင်နှင့် ဖြတ်လမ်းလင့်ခ်များ ဖတ်ခြင်း"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"စာမျက်နှာ %1$d မှ %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"ပင်မစာမျက်နှာ %1$d မှ %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"ပင်မမျက်နှာပြင် စာမျက်နှာသစ်"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"သုံးနေသည်"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"ချုံ့ထားသည်"</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>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"ပိတ်ရန်"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"ကိုယ်ပိုင်"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"အလုပ်"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"အလုပ်ပရိုဖိုင်"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"အလုပ်သုံးအက်ပ်များကို တံဆိပ်တပ်ထားပြီး သင်၏ IT စီမံခန့်ခွဲသူက မြင်နိုင်ပါသည်"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"ရပါပြီ"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index bdce1f4..d7497cd 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Ikke foreslå app"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Fest forslaget"</string>
<string name="bubble" msgid="3072951361014076670">"Boble"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Fest til oppgavelinjen"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"installere snarveier"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Gir apper tillatelse til å legge til snarveier uten innblanding fra brukeren."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"lese startsideinnstillinger og -snarveier"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Side %1$d av %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Startside %1$d av %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Ny side på startskjermen"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Aktiv"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Minimert"</string>
<string name="folder_opened" msgid="94695026776264709">"Mappen er åpnet – <xliff:g id="WIDTH">%1$d</xliff:g> ganger <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Trykk for å lukke mappen"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Trykk for å lagre det nye navnet"</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Lukk"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Personlig"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Jobb"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Jobbprofil"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Jobbapper er merket og synlige for IT-administratoren"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"Greit"</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index de509df..bbe8233 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -97,11 +97,10 @@
<string name="app_info_drop_target_label" msgid="692894985365717661">"एपसम्बन्धी जानकारी"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"निजी प्रोफाइलमा इन्स्टल गर्नुहोस्"</string>
<string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"एप अनइन्स्टल गर्नुहोस्"</string>
- <string name="install_drop_target_label" msgid="2539096853673231757">"स्थापना गर्नुहोस्"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"इन्स्टल गर्नुहोस्"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"एप सिफारिस नगर्नुहोस्"</string>
<string name="pin_prediction" msgid="4196423321649756498">"सिफारिस गरिएको एप पिन गर्नुहोस्"</string>
<string name="bubble" msgid="3072951361014076670">"बबल"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"टास्कबारमा पिन गर्नुहोस्"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"सर्टकट स्थापना गर्नेहोस्"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"प्रयोगकर्ताको हस्तक्षेप बिना एउटा एपलाई सर्टकटमा थप्नको लागि अनुमति दिनुहोस्।"</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"होम स्क्रिनका सेटिङ र सर्टकटहरू रिड गर्नुहोस्"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"पृष्ठ %2$d को %1$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"होम स्क्रिन %1$d को %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"नयाँ होम स्क्रिन पृष्ठ"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"सक्रिय"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"सानो पारिएको"</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>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"बन्द गर्नुहोस्"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"व्यक्तिगत"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"कामसम्बन्धी"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"कार्य प्रोफाइल"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"कामसम्बन्धी एपहरूमा ब्याज अङ्कित हुन्छ र तपाईंका IT एड्मिन ती एप हेर्न सक्छन्"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"बुझेँ"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 92b0c6d..971eee9 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Geen app voorstellen"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Voorspelling vastzetten"</string>
<string name="bubble" msgid="3072951361014076670">"Bubbel"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Vastzetten op taakbalk"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"Snelle links instellen"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Een app toestaan snelkoppelingen toe te voegen zonder tussenkomst van de gebruiker."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"instellingen en snelkoppelingen op startscherm lezen"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Pagina %1$d van %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Startscherm %1$d van %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Nieuwe startschermpagina"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Actief"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Geminimaliseerd"</string>
<string name="folder_opened" msgid="94695026776264709">"Map geopend, <xliff:g id="WIDTH">%1$d</xliff:g> bij <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Tik om de map te sluiten"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Tik om de gewijzigde naam op te slaan"</string>
@@ -134,7 +135,7 @@
<string name="allow_rotation_title" msgid="7222049633713050106">"Draaien van startscherm toestaan"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Als de telefoon gedraaid is"</string>
<string name="landscape_mode_title" msgid="5138814555934843926">"Liggende modus"</string>
- <string name="landscape_mode_desc" msgid="7372569859592816793">"Telefoon instellen op liggende modus"</string>
+ <string name="landscape_mode_desc" msgid="7372569859592816793">"Stel telefoon in op liggende modus"</string>
<string name="notification_dots_title" msgid="9062440428204120317">"Meldingsstipjes"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Aan"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Uit"</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Sluiten"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Privé"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Werk"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Werkprofiel"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Werk-apps hebben badges en zijn zichtbaar voor je IT-beheerder"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index 3eb680f..d5b87dc 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"ଆପ ପରାମର୍ଶ ଦିଅନ୍ତୁ ନାହିଁ"</string>
<string name="pin_prediction" msgid="4196423321649756498">"ପୂର୍ବାନୁମାନକୁ ପିନ୍ କରନ୍ତୁ"</string>
<string name="bubble" msgid="3072951361014076670">"ବବଲ"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"ଟାସ୍କବାରରେ ପିନ କର"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"ସର୍ଟକଟ୍ ଇନଷ୍ଟଲ୍ କରନ୍ତୁ"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"ୟୁଜରଙ୍କ ବିନା ହସ୍ତକ୍ଷେପରେ ଶର୍ଟକଟ୍ ଯୋଡ଼ିବାକୁ ଆପକୁ ଅନୁମତି ଦିଏ।"</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"ହୋମ ସେଟିଂସ ଏବଂ ସର୍ଟକଟଗୁଡ଼ିକୁ ପଢ଼ନ୍ତୁ"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"ମୋଟ %2$dରୁ %1$d ନମ୍ବର ପୃଷ୍ଠା"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"%2$dରୁ %1$d ହୋମ ସ୍କ୍ରିନ"</string>
<string name="workspace_new_page" msgid="257366611030256142">"ନୂଆ ହୋମ ସ୍କ୍ରିନ ପେଜ"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"ସକ୍ରିୟ"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"ଛୋଟ କରାଯାଇଛି"</string>
<string name="folder_opened" msgid="94695026776264709">"<xliff:g id="HEIGHT">%2$d</xliff:g> / <xliff:g id="WIDTH">%1$d</xliff:g>ର ଫୋଲ୍ଡର ଖୋଲାଗଲା"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"ଫୋଲ୍ଡର୍ ବନ୍ଦ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"ନାମ ବଦଳାଇବା ସେଭ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ"</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"ବ୍ୟକ୍ତିଗତ"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"ୱାର୍କ"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"ୱର୍କ ପ୍ରୋଫାଇଲ୍"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"ୱାର୍କ ଆପ୍ସକୁ ବେଜ କରାଯାଇଛି ଏବଂ ସେଗୁଡ଼ିକ ଆପଣଙ୍କ IT ଆଡମିନଙ୍କୁ ଦେଖାଯାଉଛି"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"ବୁଝିଗଲି"</string>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 17ecb78..a9b089b 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"ਐਪ ਦਾ ਸੁਝਾਅ ਨਾ ਦਿਓ"</string>
<string name="pin_prediction" msgid="4196423321649756498">"ਪੂਰਵ-ਅਨੁਮਾਨ ਪਿੰਨ ਕਰੋ"</string>
<string name="bubble" msgid="3072951361014076670">"ਬਬਲ"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"ਟਾਸਕਬਾਰ \'ਤੇ ਪਿੰਨ ਕਰੋ"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"ਸ਼ਾਰਟਕੱਟ ਸਥਾਪਤ ਕਰੋ"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"ਇੱਕ ਐਪ ਨੂੰ ਵਰਤੋਂਕਾਰ ਦੇ ਦਖ਼ਲ ਤੋਂ ਬਿਨਾਂ ਸ਼ਾਰਟਕੱਟ ਸ਼ਾਮਲ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"ਹੋਮ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ਾਰਟਕੱਟ ਪੜ੍ਹੋ"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"ਸਫ਼ਾ %2$d ਦਾ %1$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"ਹੋਮ ਸਕ੍ਰੀਨ %2$d ਦੀ %1$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"ਨਵਾਂ ਹੋਮ ਸਕ੍ਰੀਨ ਸਫ਼ਾ"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"ਕਿਰਿਆਸ਼ੀਲ"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"ਛੋਟਾ ਕੀਤਾ ਗਿਆ"</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>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"ਬੰਦ ਕਰੋ"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"ਨਿੱਜੀ"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"ਕੰਮ ਸੰਬੰਧੀ"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<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>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 1dad001..7860464 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Nie proponuj aplikacji"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Przypnij podpowiedź"</string>
<string name="bubble" msgid="3072951361014076670">"Dymek"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Przypnij na pasku zadań"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"Instalowanie skrótów"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Pozwala aplikacji dodawać skróty bez interwencji użytkownika."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"Odczytuje ustawienia i skróty na ekranie głównym"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Strona %1$d z %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Ekran główny %1$d z %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Nowa strona ekranu głównego"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Aktywna"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Zminimalizowana"</string>
<string name="folder_opened" msgid="94695026776264709">"Folder otwarty, <xliff:g id="WIDTH">%1$d</xliff:g> na <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Kliknij, by zamknąć folder"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Kliknij, by zapisać nową nazwę"</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Zamknij"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Osobiste"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Służbowe"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil służbowy"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Aplikacje służbowe mają plakietki i są widoczne dla administratora IT"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index e016f20..24235d1 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Não sugerir app"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Fixar previsão"</string>
<string name="bubble" msgid="3072951361014076670">"Balão"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Afixar na barra tar."</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar atalhos"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite a uma app adicionar atalhos sem a intervenção do utilizador."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"ler definições e atalhos do ecrã Principal"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Página %1$d de %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Ecrã principal %1$d de %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Nova página do ecrã principal"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Ativa"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Minimizada"</string>
<string name="folder_opened" msgid="94695026776264709">"Pasta aberta, <xliff:g id="WIDTH">%1$d</xliff:g> por <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Tocar para fechar a pasta"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Tocar para guardar o nome novo"</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Fechar"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Pessoal"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Trabalho"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil de trabalho"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"As apps de trabalho têm um emblema e estão visíveis para o seu administrador de TI"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index dc792f1..76e6bcf 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Não sugerir esse app"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Fixar previsão"</string>
<string name="bubble" msgid="3072951361014076670">"Balão"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Fixar na barra de tarefas"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar atalhos"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite que um app adicione atalhos sem intervenção do usuário."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"ler configurações e atalhos da tela inicial"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Página %1$d de %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Tela inicial %1$d de %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Nova página na tela inicial"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Ativo"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Minimizado"</string>
<string name="folder_opened" msgid="94695026776264709">"Pasta aberta, <xliff:g id="WIDTH">%1$d</xliff:g> por <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Toque para fechar a pasta"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Toque para salvar o novo nome"</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Fechar"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Pessoais"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Trabalho"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil de trabalho"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Os apps de trabalho são identificados e ficam visíveis para o adm. de TI"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"Ok"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 03e26e1..035e8cd 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Nu sugera aplicația"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Fixează predicția"</string>
<string name="bubble" msgid="3072951361014076670">"Balon"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Fixează pe bara de activități"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalează comenzi rapide"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite unei aplicații să adauge comenzi rapide fără intervenția utilizatorului."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"citește setările și comenzile rapide de pe ecranul de pornire"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Pagina %1$d din %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Ecranul de pornire %1$d din %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Pagină nouă pe ecranul de pornire"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Activă"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Minimizată"</string>
<string name="folder_opened" msgid="94695026776264709">"Dosar deschis, <xliff:g id="WIDTH">%1$d</xliff:g> pe <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Atinge pentru a închide dosarul"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Atinge pentru a salva noul nume"</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Închide"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Personale"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Profesionale"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil de serviciu"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Aplicațiile pentru lucru sunt marcate și vizibile pentru administratorul IT"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index f019544..a0230e2 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Не рекомендовать"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Закрепить рекомендацию"</string>
<string name="bubble" msgid="3072951361014076670">"Подсказка"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Закрепить на панели"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"Создание ярлыков"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Приложение сможет самостоятельно добавлять ярлыки."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"Доступ к данным о настройках и ярлыках на главном экране"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Стр. %1$d из %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Главный экран %1$d из %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Новый экран"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Активно"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Свернуто"</string>
<string name="folder_opened" msgid="94695026776264709">"Папка открыта, <xliff:g id="WIDTH">%1$d</xliff:g> x <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>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Закрыть"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Личные"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Рабочие"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<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>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index fe27579..db885f4 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"යෙදුම යෝජනා නොකරන්න"</string>
<string name="pin_prediction" msgid="4196423321649756498">"පුරෝකථනය අමුණන්න"</string>
<string name="bubble" msgid="3072951361014076670">"බුබුළ"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"කාර්ය තීරුවට අමුණන්න"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"කෙටිමං ස්ථාපනය කරන්න"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"පරිශීලක මැදිහත්වීමෙන් තොරව කෙටිමං එක් කිරීමට යෙදුමකට අවසර දෙයි."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"මුල් පිටු සැකසීම් සහ කෙටි මං කියවන්න"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"%2$d හි %1$d පිටුව"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"මුල් පිටු තිරය %2$d හි %1$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"නව මුල් පිටුව"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"සක්රිය"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"කුඩා කරන ලදි"</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>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"වසන්න"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"පුද්ගලික"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"කාර්යාලය"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"කාර්යාල පැතිකඩ"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"කාර්යාල යෙදුම්වලට ලාංඡන යොදා ඇති අතර ඔබගේ IT පරිපාලකට දෘශ්යමාන වේ"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"තේරුණා"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 7410051..ba174ad 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Nenavrhovať aplikáciu"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Pripnúť predpoveď"</string>
<string name="bubble" msgid="3072951361014076670">"Bublina"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Pripnúť na panel"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"inštalácia odkazov"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Povoľuje aplikácii pridať odkazy bez zásahu používateľa."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"čítanie nastavení a odkazov plochy"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Stránka %1$d z %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Plocha %1$d z %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Nová stránka plochy"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Aktívne"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Minimalizované"</string>
<string name="folder_opened" msgid="94695026776264709">"Otvorený priečinok, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Priečinok zavriete klepnutím"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Nový názov uložíte klepnutím"</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Zavrieť"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Osobné"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Pracovné"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Pracovný profil"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Pracovné aplikácie majú odznak a zobrazujú sa správcovi IT"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"Dobre"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 9e2307a..9fabdcf 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne predlagaj"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Predvidevanje pripenjanja"</string>
<string name="bubble" msgid="3072951361014076670">"Mehurček"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Pripni v opravilno vrstico"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"namestitev bližnjic"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Aplikaciji dovoli dodajanje bližnjic brez posredovanja uporabnika."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"branje nastavitev in bližnjic na začetnem zaslonu"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Stran %1$d od %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Začetni zaslon %1$d od %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Nova stran na začetnem zaslonu"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Aktivno"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Minimirano"</string>
<string name="folder_opened" msgid="94695026776264709">"Mapa je odprta, <xliff:g id="WIDTH">%1$d</xliff:g> krat <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Dotaknite se, da zaprete mapo"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Dotaknite se, da shranite preimenovanje"</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Zapri"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Osebno"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Delo"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Delovni profil"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Delovne aplikacije so označene z značko in vidne skrbniku za IT."</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"Razumem"</string>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index eb6660c..2109538 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Mos sugjero aplikacion"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Gozhdo parashikimin"</string>
<string name="bubble" msgid="3072951361014076670">"Flluskë"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Gozhdoje te shiriti i detyrave"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalimi i shkurtoreve"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Lejon një aplikacion të shtojë shkurtore pa ndërhyrjen e përdoruesit."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"lexo cilësimet dhe shkurtoret e ekranit bazë"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Faqja: %1$d nga gjithsej %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Ekrani bazë: %1$d nga gjithsej %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Faqja e ekranit të ri kryesor"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Aktiv"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Minimizuar"</string>
<string name="folder_opened" msgid="94695026776264709">"Dosja u hap, <xliff:g id="WIDTH">%1$d</xliff:g> me <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Trokit për të mbyllur dosjen"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Trokit për të ruajtur riemërtimin"</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Mbyll"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Personale"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Punë"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Profili i punës"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Aplikacionet e punës janë të shënuara dhe të dukshme për administratorin e teknologjisë së informacionit"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"E kuptova"</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index e97e39b..fea7de5 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Не предлажи апликацију"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Закачи предвиђање"</string>
<string name="bubble" msgid="3072951361014076670">"Облачић"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Закачи за траку зад."</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"инсталирање пречица"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Дозвољава апликацији да додаје пречице без интервенције корисника."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"читање подешавања и пречица на почетном екрану"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"%1$d. страница од %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"%1$d. почетни екран од %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Нова страница почетног екрана"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Активно"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Смањено"</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>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Затвори"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Лично"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Посао"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<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>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index d2bca52..f2d0ed9 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Föreslå inte app"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Fäst förslag"</string>
<string name="bubble" msgid="3072951361014076670">"Bubbla"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Fäst i aktivitetsfält"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"installera genvägar"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Tillåter att en app lägger till genvägar utan åtgärd från användaren."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"läsa inställningar och genvägar på startskärmen"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Sidan %1$d av %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Startskärmen %1$d av %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Ny sida på startskärmen"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Aktivt"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Minimerat"</string>
<string name="folder_opened" msgid="94695026776264709">"Mappen är öppen, <xliff:g id="WIDTH">%1$d</xliff:g> gånger <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Tryck för att stänga mappen"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Tryck för att spara namnändringen"</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Stäng"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Privat"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Arbete"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Jobbprofil"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Jobbappar är märkta och synliga för IT-administratören"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index bd0029c..6a5aa13 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Isipendekeze programu"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Bandika Utabiri"</string>
<string name="bubble" msgid="3072951361014076670">"Kiputo"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Bandika kwa upauzana"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"kuweka njia za mkato"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Huruhusu programu kuongeza njia za mkato bila mtumiaji kuingilia kati."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"kusoma mipangilio ya skrini ya kwanza na njia za mkato"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Ukurasa%1$d wa %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Skrini ya mwanzo %1$d ya %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Ukurasa mpya wa skrini ya kwanza"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Inatumika"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Imepunguzwa"</string>
<string name="folder_opened" msgid="94695026776264709">"Folda imefunguliwa, <xliff:g id="WIDTH">%1$d</xliff:g> kwa <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Gusa ili ufunge folda"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Gusa ili ubadilishe jina"</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Funga"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Binafsi"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Kazini"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Wasifu wa kazini"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Programu za kazini zina beji na msimamizi wako wa TEHAMA anaziona"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"Nimeelewa"</string>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index 30dbbb3..890e280 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"பரிந்துரைக்காதே"</string>
<string name="pin_prediction" msgid="4196423321649756498">"கணிக்கப்பட்டதைப் பின் செய்"</string>
<string name="bubble" msgid="3072951361014076670">"குமிழ்"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"செயல்பட்டியில் பின் செய்"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"குறுக்குவழிகளை நிறுவுதல்"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"பயனரின் அனுமதி இல்லாமல் குறுக்குவழிகளைச் சேர்க்கப் ஆப்ஸை அனுமதிக்கிறது."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"முகப்புத் திரையின் அமைப்புகளையும் ஷார்ட்கட்களையும் படித்தல்"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"பக்கம் %1$d / %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"முகப்புத் திரை %1$d of %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"புதிய முகப்புத் திரை பக்கம்"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"செயலில் உள்ளது"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"சிறிதாக்கப்பட்டது"</string>
<string name="folder_opened" msgid="94695026776264709">"திறக்கப்பட்ட ஃபோல்டர், <xliff:g id="WIDTH">%1$d</xliff:g> x <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>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"மூடும் பட்டன்"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"தனிப்பட்டவை"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"பணி"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"பணிக் கணக்கு"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"பணி ஆப்ஸில் பேட்ஜ் இடப்பட்டிருக்கும், உங்கள் IT நிர்வாகியால் பணி ஆப்ஸைப் பார்க்க முடியும்"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"முடிந்தது"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 4b25912..63d25f7 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"యాప్ సూచించకు"</string>
<string name="pin_prediction" msgid="4196423321649756498">"సూచనను పిన్ చేయండి"</string>
<string name="bubble" msgid="3072951361014076670">"బబుల్"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"టాస్క్బార్కు పిన్"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"షార్ట్కట్లను ఇన్స్టాల్ చేయడం"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"వినియోగదారు ప్రమేయం లేకుండా షార్ట్కట్లను జోడించడానికి యాప్ను అనుమతిస్తుంది."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"హోమ్ సెట్టింగ్లు, షార్ట్కట్లను చదవండి"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"%2$dలో %1$dవ పేజీ"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"%2$dలో %1$dవ హోమ్ స్క్రీన్"</string>
<string name="workspace_new_page" msgid="257366611030256142">"కొత్త హోమ్ స్క్రీన్ పేజీ"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"యాక్టివ్"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"కుదించబడింది"</string>
<string name="folder_opened" msgid="94695026776264709">"ఫోల్డర్ తెరవబడింది, <xliff:g id="WIDTH">%1$d</xliff:g> X <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>
@@ -187,6 +188,8 @@
<string name="accessibility_close" msgid="2277148124685870734">"మూసివేస్తుంది"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"వ్యక్తిగతం"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"వర్క్"</string>
+ <string name="all_apps_personal_tab_content_description" msgid="6286808898381807242">"వ్యక్తిగత యాప్ల ట్యాబ్"</string>
+ <string name="all_apps_work_tab_content_description" msgid="3835637212347968316">"వర్క్ యాప్ల ట్యాబ్"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"కార్యాలయ ప్రొఫైల్"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"వర్క్ యాప్లకు బ్యాడ్జ్ ఉంటుంది, అవి మీ IT అడ్మిన్కు కనిపిస్తాయి"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"అర్థమైంది"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index f5fd93d..1aa4a97 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"ไม่ต้องแนะนำแอป"</string>
<string name="pin_prediction" msgid="4196423321649756498">"ปักหมุดแอปที่คาดการณ์ไว้"</string>
<string name="bubble" msgid="3072951361014076670">"บับเบิล"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"ปักหมุดไปยังแถบงาน"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"ติดตั้งทางลัด"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"อนุญาตให้แอปเพิ่มทางลัดโดยไม่ต้องให้ผู้ใช้จัดการ"</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"อ่านการตั้งค่าและทางลัดในหน้าแรก"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"หน้า %1$d จาก %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"หน้าจอหลัก %1$d จาก %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"หน้าใหม่ในหน้าจอหลัก"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"ใช้งานอยู่"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"ลดขนาดเล็กสุด"</string>
<string name="folder_opened" msgid="94695026776264709">"เปิดโฟลเดอร์ <xliff:g id="WIDTH">%1$d</xliff:g> x <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>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"ปิด"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"ส่วนตัว"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"งาน"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<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>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 4d9a340..27b171b 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Huwag magmungkahi"</string>
<string name="pin_prediction" msgid="4196423321649756498">"I-pin ang Hula"</string>
<string name="bubble" msgid="3072951361014076670">"Bubble"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"I-pin sa taskbar"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"i-install ang mga shortcut"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Pinapayagan ang isang app na magdagdag ng mga shortcut nang walang panghihimasok ng user."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"basahin ang mga setting at shortcut ng home"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Pahina %1$d ng %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Home screen %1$d ng %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Bagong page ng home screen"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Aktibo"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Na-minimize"</string>
<string name="folder_opened" msgid="94695026776264709">"Binuksan ang folder, <xliff:g id="WIDTH">%1$d</xliff:g> by <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"I-tap upang isara ang folder"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"I-tap upang i-save ang bagong pangalan"</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Isara"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Personal"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Trabaho"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Profile sa trabaho"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"May badge at nakikita ng iyong IT admin ang mga app para sa trabaho"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 3fdae59..a1bc2b7 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Uygulamayı önerme"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Tahmini Sabitle"</string>
<string name="bubble" msgid="3072951361014076670">"Balon"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Çubuğa sabitle"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"kısayolları yükle"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Uygulamaya, kullanıcı müdahalesi olmadan kısayol ekleme izni verir."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"ana ekran ayarlarını ve kısayollarını oku"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Sayfa %1$d / %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Ana ekran %1$d / %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Yeni ana ekran sayfası"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Etkin"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Simge durumuna küçültülmüş"</string>
<string name="folder_opened" msgid="94695026776264709">"Klasör açıldı, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Klasörü kapatmak için dokunun"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Yeni adın kaydedilmesi için dokunun"</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Kapat"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Kişisel"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"İş"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"İş profili"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"İş uygulamaları rozetle işaretlenmiş olup BT yöneticisi tarafından görülebilir"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"Anladım"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index fd3855d..389dd75 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Не пропонувати додаток"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Закріпити передбачений додаток"</string>
<string name="bubble" msgid="3072951361014076670">"Повідомлення"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"На панель завдань"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"створення ярликів"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Дозволяє програмі самостійно додавати ярлики."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"читати налаштування та ярлики головного екрана"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Сторінка %1$d з %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Головний екран %1$d з %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Нова сторінка головного екрана"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Додаток активний"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Додаток згорнуто"</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>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Закрити"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Особисті додатки"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Робочі додатки"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<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">"OK"</string>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index 855a8c3..724cbcd 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"ایپ تجویز نہ کریں"</string>
<string name="pin_prediction" msgid="4196423321649756498">"پیشگوئی پن کریں"</string>
<string name="bubble" msgid="3072951361014076670">"بلبلہ"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"ٹاسک بار میں پن کریں"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"شارٹ کٹس انسٹال کریں"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"کسی ایپ کو صارف کی مداخلت کے بغیر شارٹ کٹس شامل کرنے کی اجازت دیتا ہے۔"</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"ہوم ترتیبات اور شارٹ کٹس کو پڑھیں"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"صفحہ %1$d از %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"ہوم اسکرین %1$d از %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"نیا ہوم اسکرین صفحہ"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"فعال"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"کم کر دیا گیا"</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>
@@ -187,6 +188,8 @@
<string name="accessibility_close" msgid="2277148124685870734">"بند کریں"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"ذاتی"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"دفتری"</string>
+ <string name="all_apps_personal_tab_content_description" msgid="6286808898381807242">"ذاتی ایپس کا ٹیب"</string>
+ <string name="all_apps_work_tab_content_description" msgid="3835637212347968316">"ورک ایپس کا ٹیب"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"دفتری پروفائل"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"ورک ایپس پر بَیج لگا ہوتا ہے اور آپ کا IT منتظم انہیں دیکھ سکتا ہے"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"سمجھ آ گئی"</string>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index f96b6a2..67c96ef 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Tavsiya qilinmasin"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Tavsiyani mahkamlash"</string>
<string name="bubble" msgid="3072951361014076670">"Pufaklar"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Panelga qadash"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"yorliqlar yaratish"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Ilovalarga foydalanuvchidan so‘ramasdan yorliqlar qo‘shishga ruxsat beradi."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"Bosh ekrandagi sozlamalar va yorliqlarni koʻrish"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"%2$ddan %1$d ta sahifa"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Uy ekrani %2$ddan %1$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Yangi bosh ekran sahifasi"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Faol"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Yigʻilgan"</string>
<string name="folder_opened" msgid="94695026776264709">"Jild ochildi, <xliff:g id="WIDTH">%1$d</xliff:g> ga <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Jildni yopish uchun ustiga bosing"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"O‘zgarishni saqlash uchun ustiga bosing"</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Yopish"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Shaxsiy"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Ish"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Ish profili"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Ishga oid ilovalarning maxsus belgisi bor hamda ular administratoringizga koʻrinadi"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 1d886b2..3074222 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Không gợi ý ứng dụng"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Ghim ứng dụng dự đoán"</string>
<string name="bubble" msgid="3072951361014076670">"Bong bóng"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Ghim vào thanh tác vụ"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"cài đặt lối tắt"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Cho phép ứng dụng thêm lối tắt mà không cần sự can thiệp của người dùng."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"đọc lối tắt và các chế độ cài đặt trên màn hình chính"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Trang %1$d / %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Màn hình chính %1$d / %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Trang màn hình chính mới"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Đang hoạt động"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Đã thu nhỏ"</string>
<string name="folder_opened" msgid="94695026776264709">"Đã mở thư mục, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Nhấn để đóng thư mục"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Nhấn để lưu đổi tên"</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Đóng"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Cá nhân"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Công việc"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Hồ sơ công việc"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Các ứng dụng công việc được gắn huy hiệu và quản trị viên CNTT có thể nhìn thấy"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index febc8c2..bed166c 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -62,7 +62,7 @@
<string name="widget_button_text" msgid="2880537293434387943">"微件"</string>
<string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"搜索"</string>
<string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"清除搜索框中的文字"</string>
- <string name="no_widgets_available" msgid="4337693382501046170">"无法使用微件和快捷方式"</string>
+ <string name="no_widgets_available" msgid="4337693382501046170">"没有可用的微件和快捷方式"</string>
<string name="no_search_results" msgid="3787956167293097509">"未找到任何微件或快捷方式"</string>
<string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"个人"</string>
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"工作"</string>
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"不要推荐此应用"</string>
<string name="pin_prediction" msgid="4196423321649756498">"固定预测的应用"</string>
<string name="bubble" msgid="3072951361014076670">"气泡框"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"固定到任务栏"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"安装快捷方式"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"允许应用自行添加快捷方式。"</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"读取主屏幕设置和快捷方式"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"第%1$d页,共%2$d页"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"主屏幕:第%1$d屏,共%2$d屏"</string>
<string name="workspace_new_page" msgid="257366611030256142">"主屏幕新页面"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"活跃"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"已最小化"</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>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"关闭"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"个人"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"工作"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"工作资料"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"工作应用都有相应的标志,且您的 IT 管理员可以看到它们"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"知道了"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 5a573a1..07c6386 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"不要提供應用程式建議"</string>
<string name="pin_prediction" msgid="4196423321649756498">"固定預測"</string>
<string name="bubble" msgid="3072951361014076670">"氣泡"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"固定至工作列"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"安裝捷徑"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"允許應用程式無需使用者許可也可新增捷徑。"</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"讀取主畫面設定和捷徑"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"第 %1$d 頁,共 %2$d 頁"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"主畫面 %1$d,共 %2$d 個"</string>
<string name="workspace_new_page" msgid="257366611030256142">"新主畫面頁面"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"運作中"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"已縮到最小"</string>
<string name="folder_opened" msgid="94695026776264709">"資料夾已開啟 (<xliff:g id="WIDTH">%1$d</xliff:g> x <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>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"關閉"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"個人"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"工作"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"工作設定檔"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"工作應用程式均加有標誌。你的 IT 管理員可以看到這些應用程式"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"知道了"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 6cf783c..c3ffbc1 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"不要建議此應用程式"</string>
<string name="pin_prediction" msgid="4196423321649756498">"固定預測的應用程式"</string>
<string name="bubble" msgid="3072951361014076670">"泡泡"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"固定到工作列"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"安裝捷徑"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"允許應用程式自動新增捷徑。"</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"讀取主畫面設定和捷徑"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"第 %1$d 頁,共 %2$d 頁"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"主畫面:第 %1$d 頁,共 %2$d 頁"</string>
<string name="workspace_new_page" msgid="257366611030256142">"新的主畫面頁面"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"運作中"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"最小化"</string>
<string name="folder_opened" msgid="94695026776264709">"資料夾已開啟 (<xliff:g id="WIDTH">%1$d</xliff:g> x <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>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"關閉"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"個人"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"工作"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"工作資料夾"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"工作應用程式會加上標記,且你的 IT 管理員可以看到這類應用程式"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"我知道了"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 58d1188..d08a21e 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -101,7 +101,6 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Ungaphakamisi uhlelo lokusebenza"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Ukubikezela Iphinikhodi"</string>
<string name="bubble" msgid="3072951361014076670">"Ibhamuza"</string>
- <string name="pin_to_taskbar" msgid="1281337899690299038">"Phina kutaskbar"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"faka izinqamuleli"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Ivumela uhlelo lokusebenza ukufaka izinqamuleli ngaphandle kokungenelela komsebenzisi."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"funda amasethingi wasekhaya nezinqamuleli"</string>
@@ -118,6 +117,8 @@
<string name="default_scroll_format" msgid="7475544710230993317">"Ikhasi elingu-%1$d kwangu-%2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Isikrini sasekhaya esingu-%1$d se-%2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Ikhasi elisha lesikrini sasekhaya"</string>
+ <string name="app_running_state_description" msgid="5645053189564740904">"Kuyasebenza"</string>
+ <string name="app_minimized_state_description" msgid="710740620044902509">"Kuncishisiwe"</string>
<string name="folder_opened" msgid="94695026776264709">"Ifolda ivuliwe, <xliff:g id="WIDTH">%1$d</xliff:g> nge-<xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Thepa ukuze uvale ifolda"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Thepha ukuze ulondoloze ukuqamba kabusha"</string>
@@ -187,6 +188,10 @@
<string name="accessibility_close" msgid="2277148124685870734">"Vala"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Okomuntu siqu"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Umsebenzi"</string>
+ <!-- no translation found for all_apps_personal_tab_content_description (6286808898381807242) -->
+ <skip />
+ <!-- no translation found for all_apps_work_tab_content_description (3835637212347968316) -->
+ <skip />
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Iphrofayela yomsebenzi"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Ama-app omsebenzi anebheji futhi ayabonakala kumphathi wakho we-IT"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"Ngiyezwa"</string>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index a22f943..1ecac33 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -19,6 +19,7 @@
<!-- Attributes used for launcher theme -->
<attr name="allAppsScrimColor" format="color" />
+ <attr name="allAppsScrimColorOverBlur" format="color" />
<attr name="allappsHeaderProtectionColor" format="color" />
<attr name="allAppsNavBarScrimColor" format="color" />
<attr name="allAppsTheme" format="reference" />
diff --git a/res/values/config.xml b/res/values/config.xml
index 4ea9e2c..3b48c9e 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -80,9 +80,6 @@
<string name="local_colors_extraction_class" translatable="false"></string>
<string name="search_session_manager_class" translatable="false"></string>
- <!-- Filters for widgets displayed in the widget picker -->
- <string name="widgets_filter_data_provider_class" translatable="false"></string>
-
<!-- Scalable Grid configuration -->
<!-- This is a float because it is converted to dp later in DeviceProfile -->
<dimen name="hotseat_bar_bottom_space_default">48</dimen>
@@ -117,6 +114,8 @@
<!-- Expressive Dismiss -->
<item name="expressive_dismiss_task_trans_y_damping_ratio" type="dimen" format="float">0.6</item>
<item name="expressive_dismiss_task_trans_y_stiffness" type="dimen" format="float">900</item>
+ <item name="expressive_dismiss_task_trans_x_damping_ratio" type="dimen" format="float">0.8</item>
+ <item name="expressive_dismiss_task_trans_x_stiffness" type="dimen" format="float">900</item>
<!-- Taskbar -->
<!-- This is a float because it is converted to dp later in DeviceProfile -->
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 7aa709d..a15c130 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -23,6 +23,11 @@
<dimen name="dynamic_grid_left_right_margin">8dp</dimen>
<!-- Minimum amount of next page visible in spring loaded mode -->
<dimen name="dynamic_grid_spring_loaded_min_next_space_visible">48dp</dimen>
+ <item name="aspect_ratio_portrait" format="float" type="dimen">1.0</item>
+ <!-- 1.05 was the constant we found to validate square-ish devices
+ to load portrait orientation specs for responsive grids -->
+ <item name="aspect_ratio_portrait_and_square" format="float" type="dimen">1.05</item>
+ <item name="aspect_ratio_landscape" format="float" type="dimen">10</item>
<dimen name="dynamic_grid_cell_border_spacing">16dp</dimen>
<dimen name="cell_layout_padding">10.77dp</dimen>
@@ -440,7 +445,6 @@
<dimen name="taskbar_running_app_indicator_height">0dp</dimen>
<dimen name="taskbar_running_app_indicator_width">0dp</dimen>
<dimen name="taskbar_running_app_indicator_top_margin">0dp</dimen>
- <dimen name="taskbar_minimized_app_indicator_width">0dp</dimen>
<!-- Transient taskbar (placeholders to compile in Launcher3 without Quickstep) -->
<dimen name="transient_taskbar_padding">0dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 56befd6..a626097 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -235,8 +235,6 @@
<string name="pin_prediction">Pin Prediction</string>
<!-- Label for bubbling a launcher item. [CHAR_LIMIT=20] -->
<string name="bubble">Bubble</string>
- <!-- Label for pinning an item to the taskbar. [CHAR_LIMIT=20] -->
- <string name="pin_to_taskbar">Pin to taskbar</string>
<!-- Permissions: -->
<skip />
@@ -325,6 +323,8 @@
<string name="edit_home_screen">Edit Home Screen</string>
<!-- Text for settings button [CHAR LIMIT=20]-->
<string name="settings_button_text">Home settings</string>
+ <!-- Text for app menu button [CHAR LIMIT=30]-->
+ <string name="all_apps_home_screen">Apps</string>
<!-- Message shown when a feature is disabled by the administrator -->
<string name="msg_disabled_by_admin">Disabled by your admin</string>
@@ -471,6 +471,12 @@
<!-- Accessibility action to show quick actions menu for an icon. [CHAR_LIMIT=30] -->
<string name="action_deep_shortcut">Shortcut Menu</string>
+ <!-- Accessibility name for the app widget resize frame. -->
+ <string name="widget_frame_name">Widget Resize Frame for <xliff:g id="string" example="Clock">%1$s</xliff:g></string>
+
+ <!-- Accessibility action to close the widget resize frame. [CHAR_LIMIT=30] -->
+ <string name="action_close">Close</string>
+
<!-- Accessibility action to dismiss a notification in the shortcuts menu for an icon. [CHAR_LIMIT=30] -->
<string name="action_dismiss_notification">Dismiss</string>
@@ -479,9 +485,12 @@
<!-- Label of tab to indicate personal apps -->
<string name="all_apps_personal_tab">Personal</string>
-
<!-- Label of tab to indicate work apps -->
<string name="all_apps_work_tab">Work</string>
+ <!-- Content description of personal tab to indicate personal apps -->
+ <string name="all_apps_personal_tab_content_description">Personal apps tab</string>
+ <!-- Content description of work tab to indicate work apps -->
+ <string name="all_apps_work_tab_content_description">Work apps tab</string>
<!-- This string is in the work profile tab when a user has All Apps open on their phone. This is a label for a toggle to turn the work profile on and off. "Work profile" means a separate profile on a user's phone that's specifically for their work apps and managed by their company. "Work" is used as an adjective.-->
<string name="work_profile_toggle_label">Work profile</string>
@@ -540,4 +549,5 @@
<string name="ps_add_button_label">Install</string>
<!-- Content description for install app icon -->
<string name="ps_add_button_content_description">Install apps to Private Space</string>
+ <string name="ps_app_content_description">Add files and more to Private Space</string>
</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 04421c0..39206d3 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -40,6 +40,7 @@
<style name="LauncherTheme" parent="@style/DynamicColorsBaseLauncherTheme">
<item name="android:textColorSecondary">#DE000000</item>
<item name="allAppsScrimColor">@color/materialColorSurfaceDim</item>
+ <item name="allAppsScrimColorOverBlur">#33000000</item>
<item name="allappsHeaderProtectionColor">@color/materialColorSurfaceContainerHighest</item>
<item name="allAppsNavBarScrimColor">#66FFFFFF</item>
<item name="popupColorPrimary">@color/popup_color_primary_light</item>
@@ -114,6 +115,7 @@
<item name="android:colorControlHighlight">#19FFFFFF</item>
<item name="android:colorPrimary">#FF212121</item>
<item name="allAppsScrimColor">@color/materialColorSurfaceDim</item>
+ <item name="allAppsScrimColorOverBlur">#52000000</item>
<item name="allappsHeaderProtectionColor">@color/materialColorSurfaceContainerLow</item>
<item name="allAppsNavBarScrimColor">#80000000</item>
<item name="popupColorPrimary">@color/popup_color_primary_dark</item>
diff --git a/res/xml/default_workspace_7x3.xml b/res/xml/default_workspace_7x3.xml
new file mode 100644
index 0000000..d17491e
--- /dev/null
+++ b/res/xml/default_workspace_7x3.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.
+-->
+<!-- Google-specific version of Launcher3/res/xml/default_workspace.xml -->
+<favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3" >
+
+ <!-- Hotseat (We use the screen as the position of the item in the hotseat) -->
+ <!-- Dialer Messaging Play Chrome Camera -->
+ <favorite
+ launcher:container="-101"
+ launcher:screen="0"
+ launcher:x="0"
+ launcher:y="0"
+ launcher:className="com.google.android.dialer.extensions.GoogleDialtactsActivity"
+ launcher:packageName="com.google.android.dialer" />
+
+ <favorite
+ launcher:container="-101"
+ launcher:screen="1"
+ launcher:x="1"
+ launcher:y="0"
+ launcher:className="com.google.android.apps.messaging.ui.ConversationListActivity"
+ launcher:packageName="com.google.android.apps.messaging" />
+
+ <favorite
+ launcher:container="-101"
+ launcher:screen="2"
+ launcher:x="2"
+ launcher:y="0"
+ launcher:className="com.android.vending.AssetBrowserActivity"
+ launcher:packageName="com.android.vending" />
+
+ <favorite
+ launcher:container="-101"
+ launcher:screen="3"
+ launcher:x="3"
+ launcher:y="0"
+ launcher:className="com.google.android.apps.chrome.Main"
+ launcher:packageName="com.android.chrome" />
+
+ <!-- Resolve camera intent if GoogleCamera is not available e.g. on emulator -->
+ <resolve
+ launcher:container="-101"
+ launcher:screen="4"
+ launcher:x="4"
+ launcher:y="0" >
+ <favorite
+ launcher:className="com.android.camera.CameraLauncher"
+ launcher:packageName="com.google.android.GoogleCamera" />
+ <favorite launcher:uri="#Intent;action=android.media.action.STILL_IMAGE_CAMERA;end" />
+ <favorite launcher:uri="#Intent;action=android.intent.action.CAMERA_BUTTON;end" />
+ </resolve>
+
+ <!-- Bottom row -->
+ <!-- [space] [space] [space] [space] [space] -->
+
+</favorites>
diff --git a/res/xml/default_workspace_8x3.xml b/res/xml/default_workspace_8x3.xml
new file mode 100644
index 0000000..d17491e
--- /dev/null
+++ b/res/xml/default_workspace_8x3.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.
+-->
+<!-- Google-specific version of Launcher3/res/xml/default_workspace.xml -->
+<favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3" >
+
+ <!-- Hotseat (We use the screen as the position of the item in the hotseat) -->
+ <!-- Dialer Messaging Play Chrome Camera -->
+ <favorite
+ launcher:container="-101"
+ launcher:screen="0"
+ launcher:x="0"
+ launcher:y="0"
+ launcher:className="com.google.android.dialer.extensions.GoogleDialtactsActivity"
+ launcher:packageName="com.google.android.dialer" />
+
+ <favorite
+ launcher:container="-101"
+ launcher:screen="1"
+ launcher:x="1"
+ launcher:y="0"
+ launcher:className="com.google.android.apps.messaging.ui.ConversationListActivity"
+ launcher:packageName="com.google.android.apps.messaging" />
+
+ <favorite
+ launcher:container="-101"
+ launcher:screen="2"
+ launcher:x="2"
+ launcher:y="0"
+ launcher:className="com.android.vending.AssetBrowserActivity"
+ launcher:packageName="com.android.vending" />
+
+ <favorite
+ launcher:container="-101"
+ launcher:screen="3"
+ launcher:x="3"
+ launcher:y="0"
+ launcher:className="com.google.android.apps.chrome.Main"
+ launcher:packageName="com.android.chrome" />
+
+ <!-- Resolve camera intent if GoogleCamera is not available e.g. on emulator -->
+ <resolve
+ launcher:container="-101"
+ launcher:screen="4"
+ launcher:x="4"
+ launcher:y="0" >
+ <favorite
+ launcher:className="com.android.camera.CameraLauncher"
+ launcher:packageName="com.google.android.GoogleCamera" />
+ <favorite launcher:uri="#Intent;action=android.media.action.STILL_IMAGE_CAMERA;end" />
+ <favorite launcher:uri="#Intent;action=android.intent.action.CAMERA_BUTTON;end" />
+ </resolve>
+
+ <!-- Bottom row -->
+ <!-- [space] [space] [space] [space] [space] -->
+
+</favorites>
diff --git a/res/xml/device_profiles.xml b/res/xml/device_profiles.xml
index 1d0dbff..5fac66f 100644
--- a/res/xml/device_profiles.xml
+++ b/res/xml/device_profiles.xml
@@ -207,4 +207,45 @@
</grid-option>
+ <grid-option
+ launcher:name="fixed_landscape_mode"
+ launcher:numFolderRows="2"
+ launcher:numFolderColumns="3"
+ launcher:numFolderRowsLandscape="2"
+ launcher:folderStyle="@style/FolderStyleDefault"
+ launcher:numHotseatIcons="4"
+ launcher:numExtendedHotseatIcons="8"
+ launcher:numAllAppsColumns="8"
+ launcher:numExtendedAllAppsColumns="8"
+ launcher:workspaceSpecsId="@xml/spec_handheld_workspace_3_row"
+ launcher:allAppsSpecsId="@xml/spec_handheld_all_apps_3_row"
+ launcher:folderSpecsId="@xml/spec_handheld_folder_3_row"
+ launcher:hotseatSpecsId="@xml/spec_handheld_hotseat_3_row"
+ launcher:workspaceCellSpecsId="@xml/spec_handheld_workspace_cell_3_row"
+ launcher:allAppsCellSpecsId="@xml/spec_all_apps_cell_match_workspace"
+ launcher:isScalable="true"
+ launcher:inlineQsb="landscape"
+ launcher:devicePaddingId="@xml/paddings_3_row"
+ launcher:gridSizeSpecsId="@xml/spec_col_count_3_row"
+ launcher:isFixedLandscape="true"
+ launcher:defaultLayoutId="@xml/default_workspace_8x3"
+ launcher:deviceCategory="phone">
+
+ <display-option
+ launcher:name="Fixed Landscape"
+ launcher:minWidthDps="387"
+ launcher:minHeightDps="750"
+ launcher:minCellHeight="108"
+ launcher:minCellWidth="61"
+ launcher:borderSpace="16"
+ launcher:horizontalMargin="22"
+ launcher:iconImageSize="57"
+ launcher:iconSizeLandscape="59"
+ launcher:iconTextSize="12"
+ launcher:allAppsBorderSpace="16"
+ launcher:allAppsCellHeight="104"
+ launcher:canBeDefault="true" />
+
+ </grid-option>
+
</profiles>
\ No newline at end of file
diff --git a/res/xml/paddings_3_row.xml b/res/xml/paddings_3_row.xml
new file mode 100644
index 0000000..147c9d1
--- /dev/null
+++ b/res/xml/paddings_3_row.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<device-paddings xmlns:launcher="http://schemas.android.com/apk/res-auto" >
+
+ <device-padding
+ launcher:maxEmptySpace="100dp">
+ <workspaceTopPadding
+ launcher:a="0"
+ launcher:b="0"/>
+ <workspaceBottomPadding
+ launcher:a="0"
+ launcher:b="0"/>
+ <hotseatBottomPadding
+ launcher:a="0"
+ launcher:b="0"/>
+ </device-padding>
+
+ <device-padding
+ launcher:maxEmptySpace="160dp">
+ <workspaceTopPadding
+ launcher:a="0"
+ launcher:b="0"/>
+ <workspaceBottomPadding
+ launcher:a="0"
+ launcher:b="0"/>
+ <hotseatBottomPadding
+ launcher:a="0"
+ launcher:b="0"/>
+ </device-padding>
+
+ <device-padding
+ launcher:maxEmptySpace="9999dp">
+ <workspaceTopPadding
+ launcher:a="0"
+ launcher:b="0"/>
+ <workspaceBottomPadding
+ launcher:a="0"
+ launcher:b="0"/>
+ <hotseatBottomPadding
+ launcher:a="0"
+ launcher:b="0"/>
+ </device-padding>
+</device-paddings>
\ No newline at end of file
diff --git a/res/xml/spec_all_apps_cell_match_workspace.xml b/res/xml/spec_all_apps_cell_match_workspace.xml
new file mode 100644
index 0000000..5843490
--- /dev/null
+++ b/res/xml/spec_all_apps_cell_match_workspace.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<cellSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
+ <specs launcher:maxAspectRatio="@dimen/aspect_ratio_landscape">
+ <cellSpec
+ launcher:dimensionType="height"
+ launcher:maxAvailableSize="9999dp">
+ <iconDrawablePadding launcher:matchWorkspace="true" />
+ <iconSize launcher:matchWorkspace="true" />
+ <iconTextSize launcher:matchWorkspace="true" />
+ </cellSpec>
+ </specs>
+</cellSpecs>
\ No newline at end of file
diff --git a/res/xml/spec_col_count_3_row.xml b/res/xml/spec_col_count_3_row.xml
new file mode 100644
index 0000000..bbde542
--- /dev/null
+++ b/res/xml/spec_col_count_3_row.xml
@@ -0,0 +1,33 @@
+<?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.
+ -->
+<GridSizeSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
+ <GridSize
+ launcher:numGridColumns="7"
+ launcher:numGridRows="3"
+ launcher:dbFile="launcher_7_by_3.db"
+ launcher:defaultLayoutId="@xml/default_workspace_7x3"
+ launcher:minDeviceWidthPx="0"
+ launcher:minDeviceHeightPx="0"
+ />
+ <GridSize
+ launcher:numGridColumns="8"
+ launcher:numGridRows="3"
+ launcher:dbFile="launcher_8_by_3.db"
+ launcher:defaultLayoutId="@xml/default_workspace_8x3"
+ launcher:minDeviceWidthPx="0"
+ launcher:minDeviceHeightPx="2093"
+ />
+</GridSizeSpecs>
\ No newline at end of file
diff --git a/res/xml/spec_handheld_all_apps_3_row.xml b/res/xml/spec_handheld_all_apps_3_row.xml
new file mode 100644
index 0000000..00b3310
--- /dev/null
+++ b/res/xml/spec_handheld_all_apps_3_row.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<allAppsSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
+ <!-- landscape -->
+ <specs launcher:maxAspectRatio="@dimen/aspect_ratio_landscape">
+ <allAppsSpec
+ launcher:dimensionType="height"
+ launcher:maxAvailableSize="9999dp">
+ <startPadding launcher:fixedSize="0dp" />
+ <endPadding launcher:fixedSize="0dp" />
+ <gutter launcher:matchWorkspace="true" />
+ <cellSize launcher:matchWorkspace="true" />
+ </allAppsSpec>
+
+ <allAppsSpec
+ launcher:dimensionType="width"
+ launcher:maxAvailableSize="9999dp">
+ <startPadding launcher:ofRemainderSpace="0.5" />
+ <endPadding launcher:ofRemainderSpace="0.5" />
+ <gutter launcher:matchWorkspace="true" />
+ <cellSize launcher:matchWorkspace="true" />
+ </allAppsSpec>
+ </specs>
+</allAppsSpecs>
diff --git a/res/xml/spec_handheld_folder_3_row.xml b/res/xml/spec_handheld_folder_3_row.xml
new file mode 100644
index 0000000..2614d96
--- /dev/null
+++ b/res/xml/spec_handheld_folder_3_row.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Handheld folders 7x3 -->
+<folderSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
+ <!-- landscape -->
+ <specs launcher:maxAspectRatio="@dimen/aspect_ratio_landscape">
+ <folderSpec launcher:dimensionType="width" launcher:maxAvailableSize="9999dp">
+ <startPadding launcher:fixedSize="16dp" />
+ <endPadding launcher:fixedSize="16dp" />
+ <gutter launcher:fixedSize="16dp" />
+ <cellSize launcher:fixedSize="66dp" />
+ </folderSpec>
+ <folderSpec launcher:dimensionType="height" launcher:maxAvailableSize="9999dp">
+ <startPadding launcher:fixedSize="16dp" />
+ <!-- mapped to footer height size -->
+ <endPadding launcher:fixedSize="48dp" />
+ <gutter launcher:fixedSize="0dp" />
+ <cellSize launcher:fixedSize="80dp" />
+ </folderSpec>
+ </specs>
+</folderSpecs>
diff --git a/res/xml/spec_handheld_hotseat_3_row.xml b/res/xml/spec_handheld_hotseat_3_row.xml
new file mode 100644
index 0000000..bd47c90
--- /dev/null
+++ b/res/xml/spec_handheld_hotseat_3_row.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<hotseatSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
+ <!-- landscape -->
+ <specs launcher:maxAspectRatio="@dimen/aspect_ratio_landscape">
+ <hotseatSpec
+ launcher:maxAvailableSize="9999dp"
+ launcher:dimensionType="width">
+ <hotseatQsbSpace launcher:fixedSize="0dp" />
+ <edgePadding launcher:fixedSize="0dp" />
+ </hotseatSpec>
+ </specs>
+</hotseatSpecs>
\ No newline at end of file
diff --git a/res/xml/spec_handheld_workspace_3_row.xml b/res/xml/spec_handheld_workspace_3_row.xml
new file mode 100644
index 0000000..8e024d3
--- /dev/null
+++ b/res/xml/spec_handheld_workspace_3_row.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<workspaceSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
+ <!-- landscape -->
+ <specs launcher:maxAspectRatio="@dimen/aspect_ratio_landscape">
+ <workspaceSpec
+ launcher:dimensionType="height"
+ launcher:maxAvailableSize="9999dp">
+ <startPadding launcher:ofAvailableSpace="0.01" />
+ <endPadding launcher:ofAvailableSpace="0.055" />
+ <gutter launcher:ofAvailableSpace="0.02" />
+ <cellSize launcher:ofRemainderSpace="1" />
+ </workspaceSpec>
+ <workspaceSpec
+ launcher:dimensionType="width"
+ launcher:maxAvailableSize="9999dp">
+ <startPadding launcher:ofAvailableSpace="0.0660867583" />
+ <endPadding launcher:ofAvailableSpace="0.0660867583" />
+ <gutter launcher:ofAvailableSpace="0.0125" />
+ <cellSize launcher:ofRemainderSpace="1" />
+ </workspaceSpec>
+ </specs>
+</workspaceSpecs>
diff --git a/res/xml/spec_handheld_workspace_cell_3_row.xml b/res/xml/spec_handheld_workspace_cell_3_row.xml
new file mode 100644
index 0000000..607dbb9
--- /dev/null
+++ b/res/xml/spec_handheld_workspace_cell_3_row.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<cellSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
+ <!-- portrait TODO: remove portrait specs-->
+ <specs launcher:maxAspectRatio="@dimen/aspect_ratio_portrait">
+ <cellSpec
+ launcher:dimensionType="height"
+ launcher:maxAvailableSize="9999dp">
+ <iconDrawablePadding launcher:fixedSize="0dp" />
+ <iconSize launcher:fixedSize="@dimen/iconSize52dp" />
+ <iconTextSize launcher:fixedSize="12sp" />
+ </cellSpec>
+ </specs>
+ <!-- landscape -->
+ <specs launcher:maxAspectRatio="@dimen/aspect_ratio_landscape">
+ <cellSpec
+ launcher:dimensionType="height"
+ launcher:maxAvailableSize="9999dp">
+ <iconDrawablePadding launcher:fixedSize="4dp" />
+ <iconSize launcher:fixedSize="@dimen/iconSize52dp" />
+ <iconTextSize launcher:fixedSize="12sp" />
+ </cellSpec>
+ </specs>
+</cellSpecs>
\ 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 0583d6d..cdeab95 100644
--- a/shared/src/com/android/launcher3/testing/shared/TestProtocol.java
+++ b/shared/src/com/android/launcher3/testing/shared/TestProtocol.java
@@ -125,6 +125,8 @@
"is-predictive-back-swipe-enabled";
public static final String REQUEST_ENABLE_TASKBAR_NAVBAR_UNIFICATION =
"enable-taskbar-navbar-unification";
+ public static final String REQUEST_TASKBAR_SHOWN_ON_HOME =
+ "taskbar-shown-on-home";
public static final String REQUEST_NUM_ALL_APPS_COLUMNS = "num-all-apps-columns";
public static final String REQUEST_IS_TWO_PANELS = "is-two-panel";
public static final String REQUEST_CELL_LAYOUT_BOARDER_HEIGHT = "cell-layout-boarder-height";
diff --git a/src/android/os/BinderUtils.kt b/src/android/os/BinderUtils.kt
new file mode 100644
index 0000000..0536283
--- /dev/null
+++ b/src/android/os/BinderUtils.kt
@@ -0,0 +1,51 @@
+/*
+ * 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 android.os
+
+import com.android.launcher3.util.Executors.MAIN_EXECUTOR
+import com.android.launcher3.util.WeakCleanupSet
+import com.android.launcher3.util.WeakCleanupSet.OnOwnerDestroyedCallback
+
+/** Utility methods related to Binder */
+object BinderUtils {
+
+ /** Creates a binder wrapper which is tied to the [lifecycle] */
+ @JvmStatic
+ fun <T : Binder> T.wrapLifecycle(cleanupSet: WeakCleanupSet): Binder =
+ LifecycleBinderWrapper(this, cleanupSet)
+
+ private class LifecycleBinderWrapper<T : Binder>(
+ private var realBinder: T?,
+ cleanupSet: WeakCleanupSet,
+ ) : Binder(realBinder?.interfaceDescriptor), OnOwnerDestroyedCallback {
+
+ init {
+ MAIN_EXECUTOR.execute { cleanupSet.addOnOwnerDestroyedCallback(this) }
+ }
+
+ override fun queryLocalInterface(descriptor: String): IInterface? =
+ realBinder?.queryLocalInterface(descriptor)
+
+ override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean =
+ realBinder?.transact(code, data, reply, flags)
+ ?: throw RemoteException("Original binder cleaned up")
+
+ override fun onOwnerDestroyed() {
+ realBinder = null
+ }
+ }
+}
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index b51e850..213d88f 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -216,6 +216,13 @@
AppWidgetResizeFrame frame = (AppWidgetResizeFrame) launcher.getLayoutInflater()
.inflate(R.layout.app_widget_resize_frame, dl, false);
frame.setupForWidget(widget, cellLayout, dl);
+ // Save widget item info as tag on resize frame; so that, the accessibility delegate can
+ // attach actions that typically happen on widget (e.g. resize, move) also on the resize
+ // frame.
+ frame.setTag(widget.getTag());
+ frame.setAccessibilityDelegate(launcher.getAccessibilityDelegate());
+ frame.setContentDescription(launcher.asContext().getString(R.string.widget_frame_name,
+ widget.getContentDescription()));
((DragLayer.LayoutParams) frame.getLayoutParams()).customPosition = true;
dl.addView(frame);
@@ -235,6 +242,13 @@
}
}
+ /**
+ * Retrieves the view where accessibility actions happen.
+ */
+ public View getViewForAccessibility() {
+ return mWidgetView;
+ }
+
private void setupForWidget(LauncherAppWidgetHostView widgetView, CellLayout cellLayout,
DragLayer dragLayer) {
mCellLayout = cellLayout;
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 84c8040..2426a61 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -37,6 +37,10 @@
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleRegistry;
+import androidx.savedstate.SavedStateRegistry;
+import androidx.savedstate.SavedStateRegistryController;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.logging.StatsLogManager;
@@ -47,9 +51,11 @@
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.LifecycleHelper;
import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.ViewCache;
+import com.android.launcher3.util.WeakCleanupSet;
import com.android.launcher3.util.WindowBounds;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.ScrimView;
@@ -97,11 +103,15 @@
private final ArrayList<MultiWindowModeChangedListener> mMultiWindowModeChangedListeners =
new ArrayList<>();
+ private final SavedStateRegistryController mSavedStateRegistryController =
+ SavedStateRegistryController.create(this);
+ private final LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
+ private final WeakCleanupSet mCleanupSet = new WeakCleanupSet(this);
+
protected DeviceProfile mDeviceProfile;
protected SystemUiController mSystemUiController;
private StatsLogManager mStatsLogManager;
-
public static final int ACTIVITY_STATE_STARTED = 1 << 0;
public static final int ACTIVITY_STATE_RESUMED = 1 << 1;
@@ -178,6 +188,12 @@
private ActionMode mCurrentActionMode;
+ public BaseActivity() {
+ mSavedStateRegistryController.performAttach();
+ registerActivityLifecycleCallbacks(
+ new LifecycleHelper(this, mSavedStateRegistryController, mLifecycleRegistry));
+ }
+
@Override
public ViewCache getViewCache() {
return mViewCache;
@@ -478,6 +494,23 @@
protected void reapplyUi() {}
+ @NonNull
+ @Override
+ public SavedStateRegistry getSavedStateRegistry() {
+ return mSavedStateRegistryController.getSavedStateRegistry();
+ }
+
+ @NonNull
+ @Override
+ public Lifecycle getLifecycle() {
+ return mLifecycleRegistry;
+ }
+
+ @Override
+ public WeakCleanupSet getOwnerCleanupSet() {
+ return mCleanupSet;
+ }
+
public static <T extends BaseActivity> T fromContext(Context context) {
if (context instanceof BaseActivity) {
return (T) context;
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 730ad78..30e3a2b 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -20,11 +20,13 @@
import static android.graphics.fonts.FontStyle.FONT_WEIGHT_NORMAL;
import static android.text.Layout.Alignment.ALIGN_NORMAL;
+import static com.android.app.animation.Interpolators.EMPHASIZED;
import static com.android.launcher3.BubbleTextView.RunningAppState.RUNNING;
import static com.android.launcher3.BubbleTextView.RunningAppState.NOT_RUNNING;
import static com.android.launcher3.BubbleTextView.RunningAppState.MINIMIZED;
import static com.android.launcher3.Flags.enableContrastTiles;
import static com.android.launcher3.Flags.enableCursorHoverStates;
+import static com.android.launcher3.allapps.AlphabeticalAppsList.PRIVATE_SPACE_PACKAGE;
import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
import static com.android.launcher3.icons.BitmapInfo.FLAG_NO_BADGE;
import static com.android.launcher3.icons.BitmapInfo.FLAG_SKIP_USER_BADGE;
@@ -37,6 +39,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -102,6 +105,7 @@
import java.text.NumberFormat;
import java.util.HashMap;
import java.util.Locale;
+import java.util.Objects;
/**
* TextView that draws a bubble behind the text. We cannot use a LineBackgroundSpan
@@ -130,6 +134,9 @@
StringMatcherUtility.StringMatcher.getInstance();
private static final int BOLD_TEXT_ADJUSTMENT = FONT_WEIGHT_BOLD - FONT_WEIGHT_NORMAL;
+ public static final int LINE_INDICATOR_ANIM_DURATION = 150;
+ private static final float MINIMIZED_APP_INDICATOR_SCALE = 0.5f;
+
private static final int[] STATE_PRESSED = new int[]{android.R.attr.state_pressed};
private float mScaleForReorderBounce = 1f;
@@ -165,6 +172,36 @@
}
};
+ private static final Property<BubbleTextView, Integer> LINE_INDICATOR_COLOR_PROPERTY =
+ new Property<>(Integer.class, "lineIndicatorColor") {
+
+ @Override
+ public Integer get(BubbleTextView bubbleTextView) {
+ return bubbleTextView.mLineIndicatorColor;
+ }
+
+ @Override
+ public void set(BubbleTextView bubbleTextView, Integer color) {
+ bubbleTextView.mLineIndicatorColor = color;
+ bubbleTextView.invalidate();
+ }
+ };
+
+ private static final Property<BubbleTextView, Float> LINE_INDICATOR_SCALE_PROPERTY =
+ new Property<>(Float.TYPE, "lineIndicatorScale") {
+
+ @Override
+ public Float get(BubbleTextView bubbleTextView) {
+ return bubbleTextView.mLineIndicatorScale;
+ }
+
+ @Override
+ public void set(BubbleTextView bubbleTextView, Float scale) {
+ bubbleTextView.mLineIndicatorScale = scale;
+ bubbleTextView.invalidate();
+ }
+ };
+
private final MultiTranslateDelegate mTranslateDelegate = new MultiTranslateDelegate(this);
protected final ActivityContext mActivity;
private FastBitmapDrawable mIcon;
@@ -202,7 +239,6 @@
// These fields, related to showing running apps, are only used for Taskbar.
private final int mRunningAppIndicatorWidth;
- private final int mMinimizedAppIndicatorWidth;
private final int mRunningAppIndicatorHeight;
private final int mRunningAppIndicatorTopMargin;
private final Paint mRunningAppIndicatorPaint;
@@ -210,6 +246,12 @@
private RunningAppState mRunningAppState;
private final int mRunningAppIndicatorColor;
private final int mMinimizedAppIndicatorColor;
+ @ViewDebug.ExportedProperty(category = "launcher")
+ private int mLineIndicatorColor;
+ @ViewDebug.ExportedProperty(category = "launcher")
+ private float mLineIndicatorScale;
+ private int mLineIndicatorAnimStartDelay;
+ private Animator mLineIndicatorAnim;
private final String mMinimizedStateDescription;
private final String mRunningStateDescription;
@@ -294,8 +336,6 @@
mRunningAppIndicatorWidth =
getResources().getDimensionPixelSize(R.dimen.taskbar_running_app_indicator_width);
- mMinimizedAppIndicatorWidth =
- getResources().getDimensionPixelSize(R.dimen.taskbar_minimized_app_indicator_width);
mRunningAppIndicatorHeight =
getResources().getDimensionPixelSize(R.dimen.taskbar_running_app_indicator_height);
mRunningAppIndicatorTopMargin =
@@ -344,6 +384,11 @@
mForceHideDot = false;
setBackground(null);
+ mLineIndicatorColor = Color.TRANSPARENT;
+ mLineIndicatorScale = 0;
+ mLineIndicatorAnimStartDelay = 0;
+ cancelLineIndicatorAnim();
+
setTag(null);
if (mIconLoadRequest != null) {
mIconLoadRequest.cancel();
@@ -436,9 +481,50 @@
/** Updates whether the app this view represents is currently running. */
@UiThread
- public void updateRunningState(RunningAppState runningAppState) {
+ public void updateRunningState(RunningAppState runningAppState, boolean animate) {
+ if (runningAppState.equals(mRunningAppState)) {
+ return;
+ }
mRunningAppState = runningAppState;
- invalidate();
+ cancelLineIndicatorAnim();
+
+ int color = switch (mRunningAppState) {
+ case NOT_RUNNING -> Color.TRANSPARENT;
+ case RUNNING -> mRunningAppIndicatorColor;
+ case MINIMIZED -> mMinimizedAppIndicatorColor;
+ };
+ float scale = switch (mRunningAppState) {
+ case NOT_RUNNING -> 0;
+ case RUNNING -> 1;
+ case MINIMIZED -> MINIMIZED_APP_INDICATOR_SCALE;
+ };
+
+ if (!animate) {
+ mLineIndicatorColor = color;
+ mLineIndicatorScale = scale;
+ invalidate();
+ return;
+ }
+
+ AnimatorSet lineIndicatorAnim = new AnimatorSet();
+ mLineIndicatorAnim = lineIndicatorAnim;
+ Animator colorAnimator = ObjectAnimator.ofArgb(this, LINE_INDICATOR_COLOR_PROPERTY, color);
+ Animator scaleAnimator = ObjectAnimator.ofFloat(this, LINE_INDICATOR_SCALE_PROPERTY, scale);
+ lineIndicatorAnim.playTogether(colorAnimator, scaleAnimator);
+
+ lineIndicatorAnim.setInterpolator(EMPHASIZED);
+ lineIndicatorAnim.setStartDelay(mLineIndicatorAnimStartDelay);
+ lineIndicatorAnim.setDuration(LINE_INDICATOR_ANIM_DURATION).start();
+ }
+
+ public void setLineIndicatorAnimStartDelay(int lineIndicatorAnimStartDelay) {
+ mLineIndicatorAnimStartDelay = lineIndicatorAnimStartDelay;
+ }
+
+ private void cancelLineIndicatorAnim() {
+ if (mLineIndicatorAnim != null) {
+ mLineIndicatorAnim.cancel();
+ }
}
/**
@@ -505,7 +591,9 @@
}
private void setNonPendingIcon(ItemInfoWithIcon info) {
- int flags = shouldUseTheme() ? FLAG_THEMED : 0;
+ // Set nonPendingIcon acts as a restart which should refresh the flag state when applicable.
+ int flags = Objects.equals(info.getTargetPackage(), PRIVATE_SPACE_PACKAGE)
+ ? info.bitmap.creationFlags : shouldUseTheme() ? FLAG_THEMED : 0;
// Remove badge on icons smaller than 48dp.
if (mHideBadge || mDisplay == DISPLAY_SEARCH_RESULT_SMALL) {
flags |= FLAG_NO_BADGE;
@@ -790,19 +878,18 @@
/** Draws a line under the app icon if this is representing a running app in Desktop Mode. */
protected void drawRunningAppIndicatorIfNecessary(Canvas canvas) {
- if (mRunningAppState == NOT_RUNNING || mDisplay != DISPLAY_TASKBAR) {
+ if (mDisplay != DISPLAY_TASKBAR
+ || mLineIndicatorScale == 0
+ || mLineIndicatorColor == Color.TRANSPARENT) {
return;
}
getIconBounds(mRunningAppIconBounds);
Utilities.scaleRectAboutCenter(mRunningAppIconBounds, ICON_VISIBLE_AREA_FACTOR);
- final boolean isMinimized = mRunningAppState == MINIMIZED;
final int indicatorTop = mRunningAppIconBounds.bottom + mRunningAppIndicatorTopMargin;
- final int indicatorWidth =
- isMinimized ? mMinimizedAppIndicatorWidth : mRunningAppIndicatorWidth;
+ final float indicatorWidth = mRunningAppIndicatorWidth * mLineIndicatorScale;
final float cornerRadius = mRunningAppIndicatorHeight / 2f;
- mRunningAppIndicatorPaint.setColor(
- isMinimized ? mMinimizedAppIndicatorColor : mRunningAppIndicatorColor);
+ mRunningAppIndicatorPaint.setColor(mLineIndicatorColor);
canvas.drawRoundRect(
mRunningAppIconBounds.centerX() - indicatorWidth / 2f,
@@ -983,11 +1070,14 @@
@Override
public void setTextColor(ColorStateList colors) {
- mTextColor = (shouldDrawAppContrastTile() && !TextUtils.isEmpty(getText()))
- ? PillColorProvider.getInstance(
- getContext()).getAppTitleTextPaint().getColor()
- : colors.getDefaultColor();
- mTextColorStateList = colors;
+ if (shouldDrawAppContrastTile()) {
+ mTextColor = PillColorProvider.getInstance(
+ getContext()).getAppTitleTextPaint().getColor();
+ } else {
+ mTextColor = colors.getDefaultColor();
+ mTextColorStateList = colors;
+ }
+
if (Float.compare(mTextAlpha, 1) == 0) {
super.setTextColor(colors);
} else {
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index c85ca49..090208a 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -30,8 +30,8 @@
import static com.android.launcher3.testing.shared.ResourceUtils.pxFromDp;
import static com.android.launcher3.testing.shared.ResourceUtils.roundPxValueFromFloat;
import static com.android.wm.shell.Flags.enableBubbleBar;
-import static com.android.wm.shell.Flags.enableTinyTaskbar;
import static com.android.wm.shell.Flags.enableBubbleBarOnPhones;
+import static com.android.wm.shell.Flags.enableTinyTaskbar;
import android.annotation.SuppressLint;
import android.content.Context;
@@ -508,9 +508,11 @@
bottomSheetOpenDuration = res.getInteger(R.integer.config_bottomSheetOpenDuration);
bottomSheetCloseDuration = res.getInteger(R.integer.config_bottomSheetCloseDuration);
- if (isTablet) {
+ if (shouldShowAllAppsOnSheet()) {
bottomSheetWorkspaceScale = workspaceContentScale;
- if (isMultiDisplay) {
+ if (Flags.allAppsBlur()) {
+ bottomSheetDepth = 2f;
+ } else if (isMultiDisplay) {
// TODO(b/259893832): Revert to use maxWallpaperScale to calculate bottomSheetDepth
// when screen recorder bug is fixed.
if (enableScalingRevealHomeAnimation()) {
@@ -1830,10 +1832,17 @@
workspacePageIndicatorHeight - mWorkspacePageIndicatorOverlapWorkspace;
}
int paddingTop = workspaceTopPadding + (mIsScalableGrid ? 0 : edgeMarginPx);
- // On isFixedLandscapeMode on phones we already have padding because of the camera hole
- int paddingSide = inv.isFixedLandscape ? 0 : desiredWorkspaceHorizontalMarginPx;
+ int paddingLeft = desiredWorkspaceHorizontalMarginPx;
+ int paddingRight = desiredWorkspaceHorizontalMarginPx;
- padding.set(paddingSide, paddingTop, paddingSide, paddingBottom);
+ // In fixed Landscape we don't need padding on the side next to the cutout because
+ // the cutout is already adding padding to all of Launcher, we only need on the other
+ // side
+ if (inv.isFixedLandscape) {
+ paddingLeft = isSeascape() ? desiredWorkspaceHorizontalMarginPx : 0;
+ paddingRight = isSeascape() ? 0 : desiredWorkspaceHorizontalMarginPx;
+ }
+ padding.set(paddingLeft, paddingTop, paddingRight, paddingBottom);
}
insetPadding(workspacePadding, cellLayoutPaddingPx);
}
@@ -1931,7 +1940,24 @@
hotseatBarPadding.set(mHotseatBarWorkspaceSpacePx, paddingTop,
mInsets.right + mHotseatBarEdgePaddingPx, paddingBottom);
}
- } else if (isTaskbarPresent || inv.isFixedLandscape) {
+ } else if (inv.isFixedLandscape) {
+ // Center the QSB vertically with hotseat
+ int hotseatBarBottomPadding = getHotseatBarBottomPadding();
+ int hotseatPlusQSBWidth = getHotseatRequiredWidth();
+ int qsbWidth = getAdditionalQsbSpace();
+ int availableWidthPxForHotseat = availableWidthPx - Math.abs(workspacePadding.width())
+ - Math.abs(cellLayoutPaddingPx.width());
+ int remainingSpaceOnSide = (availableWidthPxForHotseat - hotseatPlusQSBWidth) / 2;
+
+ hotseatBarPadding.set(
+ (remainingSpaceOnSide + qsbWidth) + mInsets.left + workspacePadding.left
+ + cellLayoutPaddingPx.left,
+ hotseatBarSizePx - hotseatBarBottomPadding - hotseatCellHeightPx,
+ remainingSpaceOnSide + mInsets.right + workspacePadding.right
+ + cellLayoutPaddingPx.right,
+ hotseatBarBottomPadding
+ );
+ } else if (isTaskbarPresent) {
// Center the QSB vertically with hotseat
int hotseatBarBottomPadding = getHotseatBarBottomPadding();
int hotseatBarTopPadding =
@@ -1950,11 +1976,6 @@
}
startSpacing += getAdditionalQsbSpace();
- if (inv.isFixedLandscape) {
- endSpacing += mInsets.right;
- startSpacing += mInsets.left;
- }
-
hotseatBarPadding.top = hotseatBarTopPadding;
hotseatBarPadding.bottom = hotseatBarBottomPadding;
boolean isRtl = Utilities.isRtl(context.getResources());
@@ -2164,7 +2185,8 @@
}
public boolean isSeascape() {
- return rotationHint == Surface.ROTATION_270 && isVerticalBarLayout();
+ return rotationHint == Surface.ROTATION_270
+ && (isVerticalBarLayout() || inv.isFixedLandscape);
}
public boolean shouldFadeAdjacentWorkspaceScreens() {
diff --git a/src/com/android/launcher3/DropTargetHandler.kt b/src/com/android/launcher3/DropTargetHandler.kt
index 0cc7fc7..3c162a2 100644
--- a/src/com/android/launcher3/DropTargetHandler.kt
+++ b/src/com/android/launcher3/DropTargetHandler.kt
@@ -72,9 +72,10 @@
AbstractFloatingView.TYPE_WIDGET_RESIZE_FRAME,
)
var pageItem: ItemInfo = item
- if (item.container <= 0) {
- val v = mLauncher.workspace.getHomescreenIconByItemId(item.container)
- v?.let { pageItem = v.tag as ItemInfo }
+ if (item.container >= 0) {
+ mLauncher.workspace.getViewByItemId(item.container)?.let {
+ pageItem = it.tag as ItemInfo
+ }
}
val pageIds =
if (pageItem.container == LauncherSettings.Favorites.CONTAINER_DESKTOP)
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 7b41586..5c9392d 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -71,9 +71,11 @@
import static com.android.launcher3.LauncherState.NO_SCALE;
import static com.android.launcher3.LauncherState.SPRING_LOADED;
import static com.android.launcher3.Utilities.postAsyncCallback;
+import static com.android.launcher3.Workspace.mapOverCellLayouts;
import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
import static com.android.launcher3.config.FeatureFlags.FOLDABLE_SINGLE_PAGE;
import static com.android.launcher3.config.FeatureFlags.MULTI_SELECT_EDIT_MODE;
+import static com.android.launcher3.icons.BitmapRenderer.createHardwareBitmap;
import static com.android.launcher3.logging.KeyboardStateManager.KeyboardState.HIDE;
import static com.android.launcher3.logging.KeyboardStateManager.KeyboardState.SHOW;
import static com.android.launcher3.logging.StatsLogManager.EventEnum;
@@ -125,7 +127,6 @@
import android.content.res.Configuration;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Bitmap;
-import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -163,7 +164,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
-import androidx.annotation.StringRes;
import androidx.annotation.UiThread;
import androidx.annotation.VisibleForTesting;
import androidx.core.os.BuildCompat;
@@ -172,7 +172,6 @@
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.allapps.ActivityAllAppsContainerView;
-import com.android.launcher3.allapps.AllAppsRecyclerView;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.allapps.DiscoveryBounce;
import com.android.launcher3.anim.AnimationSuccessListener;
@@ -253,7 +252,6 @@
import com.android.launcher3.util.TouchController;
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.views.ActivityContext;
-import com.android.launcher3.views.ComposeInitializer;
import com.android.launcher3.views.FloatingIconView;
import com.android.launcher3.views.FloatingSurfaceView;
import com.android.launcher3.views.OptionsPopupView;
@@ -545,7 +543,7 @@
mFocusHandler, new CellLayout(mWorkspace.getContext(), mWorkspace));
mPopupDataProvider = new PopupDataProvider(this);
- mWidgetPickerDataProvider = new WidgetPickerDataProvider();
+ mWidgetPickerDataProvider = new WidgetPickerDataProvider(this);
PillColorProvider.getInstance(mWorkspace.getContext()).registerObserver();
boolean internalStateHandled = ACTIVITY_TRACKER.handleCreate(this);
@@ -579,7 +577,6 @@
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
setContentView(getRootView());
- ComposeInitializer.initCompose(this);
if (mOnInitialBindListener != null) {
getRootView().getViewTreeObserver().addOnPreDrawListener(mOnInitialBindListener);
@@ -1475,8 +1472,7 @@
// Adding a shortcut to a Folder.
FolderIcon folderIcon = findFolderIcon(container);
if (folderIcon != null) {
- FolderInfo folderInfo = (FolderInfo) folderIcon.getTag();
- folderInfo.add(info, args.rank, false);
+ folderIcon.getFolder().addFolderContent(info, args.rank, false);
} else {
Log.e(TAG, "Could not find folder with id " + container + " to add shortcut.");
}
@@ -1485,7 +1481,7 @@
@Override
public @Nullable FolderIcon findFolderIcon(final int folderIconId) {
- return (FolderIcon) mWorkspace.getHomescreenIconByItemId(folderIconId);
+ return (FolderIcon) mWorkspace.getViewByItemId(folderIconId);
}
/**
@@ -1795,7 +1791,6 @@
SettingsCache.INSTANCE.get(this).unregister(TOUCHPAD_NATURAL_SCROLLING,
mNaturalScrollingChangedListener);
ScreenOnTracker.INSTANCE.get(this).removeListener(mScreenOnListener);
- mWorkspace.removeFolderListeners();
PluginManagerWrapper.INSTANCE.get(this).removePluginListener(this);
mModel.removeCallbacks(this);
@@ -1803,6 +1798,7 @@
mAppWidgetHolder.stopListening();
mAppWidgetHolder.destroy();
+ mWidgetPickerDataProvider.destroy();
TextKeyListener.getInstance().release();
mModelCallbacks.clearPendingBinds();
@@ -1883,7 +1879,8 @@
if (dropView != null && dropView.containsAppWidgetHostView()) {
// Extracting Bitmap from dropView instead of its content view produces the correct
// bitmap.
- widgetPreviewBitmap = getBitmapFromView(dropView);
+ widgetPreviewBitmap = createHardwareBitmap(
+ dropView.getWidth(), dropView.getHeight(), dropView::draw);
}
}
@@ -2053,10 +2050,11 @@
public boolean removeItem(View v, final ItemInfo itemInfo, boolean deleteFromDb,
@Nullable final String reason) {
if (itemInfo instanceof WorkspaceItemInfo) {
- View collectionIcon = mWorkspace.getHomescreenIconByItemId(itemInfo.container);
- if (collectionIcon instanceof FolderIcon) {
+ View collectionIcon = mWorkspace.getViewByItemId(itemInfo.container);
+ if (collectionIcon instanceof FolderIcon folderIcon) {
// Remove the shortcut from the folder before removing it from launcher
- ((FolderInfo) collectionIcon.getTag()).remove((WorkspaceItemInfo) itemInfo, true);
+ Folder folder = folderIcon.getFolder();
+ folder.removeFolderContent(true, itemInfo);
} else if (collectionIcon instanceof AppPairIcon appPairIcon) {
removeItem(appPairIcon, appPairIcon.getInfo(), deleteFromDb,
"removing app pair because one of its member apps was removed");
@@ -2067,9 +2065,6 @@
getModelWriter().deleteItemFromDatabase(itemInfo, reason);
}
} else if (itemInfo instanceof CollectionInfo ci) {
- if (v instanceof FolderIcon) {
- ((FolderIcon) v).removeListeners();
- }
mWorkspace.removeWorkspaceItem(v);
if (deleteFromDb) {
getModelWriter().deleteCollectionAndContentsFromDatabase(ci);
@@ -2432,52 +2427,25 @@
}
/**
- * Similar to {@link #getFirstMatch} but optimized to finding a suitable view for the app close
- * animation.
+ * Finds the first view on homescreen matching the provided parameters, optimized to finding a
+ * suitable view for the app close animation.
*
* @param svi The StableViewInfo of the preferred item to match to if it exists or null
* @param packageName The package name of the app to match.
* @param user The user of the app to match.
- * @param supportsAllAppsState If true and we are in All Apps state, looks for view in All Apps.
- * Else we only looks on the workspace.
*/
- public @Nullable View getFirstMatchForAppClose(
- @Nullable StableViewInfo svi, String packageName,
- UserHandle user, boolean supportsAllAppsState) {
+ public @Nullable View getFirstHomeElementForAppClose(
+ @Nullable StableViewInfo svi, String packageName, UserHandle user) {
final Predicate<ItemInfo> preferredItem = svi == null ? i -> false : svi::matches;
- final Predicate<ItemInfo> packageAndUserAndApp = info ->
- info != null
- && info.itemType == ITEM_TYPE_APPLICATION
- && info.user.equals(user)
- && info.getTargetComponent() != null
- && TextUtils.equals(info.getTargetComponent().getPackageName(),
- packageName);
-
- if (supportsAllAppsState && isInState(LauncherState.ALL_APPS)) {
- AllAppsRecyclerView activeRecyclerView = mAppsView.getActiveRecyclerView();
- View v = getFirstMatch(Collections.singletonList(activeRecyclerView),
- preferredItem, packageAndUserAndApp);
-
- if (v != null && activeRecyclerView.computeVerticalScrollOffset() > 0) {
- RectF locationBounds = new RectF();
- FloatingIconView.getLocationBoundsForView(this, v, false, locationBounds,
- new Rect());
- if (locationBounds.top < mAppsView.getHeaderBottom()) {
- // Icon is covered by scrim, return null to play fallback animation.
- return null;
- }
- }
-
- return v;
- }
+ final Predicate<ItemInfo> packageAndUserAndApp = info -> info != null
+ && info.itemType == ITEM_TYPE_APPLICATION
+ && info.user.equals(user)
+ && TextUtils.equals(info.getTargetPackage(), packageName);
// Look for the item inside the folder at the current page
Folder folder = Folder.getOpen(this);
if (folder != null) {
- View v = getFirstMatch(Collections.singletonList(
- folder.getContent().getCurrentCellLayout().getShortcutsAndWidgets()),
- preferredItem,
- packageAndUserAndApp);
+ View v = folder.getFirstMatch(preferredItem, packageAndUserAndApp);
if (v == null) {
folder.close(isStarted() && !isForceInvisible());
} else {
@@ -2485,72 +2453,19 @@
}
}
- List<ViewGroup> containers = new ArrayList<>(mWorkspace.getPanelCount() + 1);
- containers.add(mWorkspace.getHotseat().getShortcutsAndWidgets());
- mWorkspace.forEachVisiblePage(page
- -> containers.add(((CellLayout) page).getShortcutsAndWidgets()));
+ List<CellLayout> containers = new ArrayList<>(mWorkspace.getPanelCount() + 1);
+ containers.add(mWorkspace.getHotseat());
+ mWorkspace.forEachVisiblePage(page -> containers.add((CellLayout) page));
+ CellLayout[] containerArray = containers.toArray(new CellLayout[0]);
+ LauncherBindableItemsContainer visibleContainer =
+ op -> mapOverCellLayouts(containerArray, op);
// Order: Preferred item by itself or in folder, then by matching package/user
- return getFirstMatch(containers, preferredItem, forFolderMatch(preferredItem),
+ return visibleContainer.getFirstMatch(
+ preferredItem, forFolderMatch(preferredItem),
packageAndUserAndApp, forFolderMatch(packageAndUserAndApp));
}
- /**
- * Finds the first view matching the ordered operators across the given viewgroups in order.
- * @param containers List of ViewGroups to scan, in order of preference.
- * @param operators List of operators, in order starting from best matching operator.
- */
- @Nullable
- private static View getFirstMatch(Iterable<ViewGroup> containers,
- final Predicate<ItemInfo>... operators) {
- for (Predicate<ItemInfo> operator : operators) {
- for (ViewGroup container : containers) {
- View match = mapOverViewGroup(container, operator);
- if (match != null) {
- return match;
- }
- }
- }
- return null;
- }
-
- /** Convert a {@link View} to {@link Bitmap}. */
- private static Bitmap getBitmapFromView(@Nullable View view) {
- if (view == null) {
- return null;
- }
- Bitmap returnedBitmap =
- Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(returnedBitmap);
- view.draw(canvas);
- return returnedBitmap;
- }
-
- /**
- * Returns the first view matching the operator in the given ViewGroups, or null if none.
- * Forward iteration matters.
- */
- @Nullable
- private static View mapOverViewGroup(ViewGroup container, Predicate<ItemInfo> op) {
- final int itemCount = container.getChildCount();
- for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) {
- View item = container.getChildAt(itemIdx);
- if (item.getVisibility() != View.VISIBLE) {
- continue;
- }
- if (item instanceof ViewGroup viewGroup) {
- View view = mapOverViewGroup(viewGroup, op);
- if (view != null) {
- return view;
- }
- }
- if (item.getTag() instanceof ItemInfo itemInfo && op.test(itemInfo)) {
- return item;
- }
- }
- return null;
- }
-
private ValueAnimator createNewAppBounceAnimation(View v, int i) {
ValueAnimator bounceAnim = new PropertyListBuilder().alpha(1).scale(1).build(v)
.setDuration(ItemInstallQueue.NEW_SHORTCUT_BOUNCE_DURATION);
@@ -2559,10 +2474,6 @@
return bounceAnim;
}
- private void announceForAccessibility(@StringRes int stringResId) {
- getDragLayer().announceForAccessibility(getString(stringResId));
- }
-
/**
* Informs us that the overlay (-1 screen, typically), has either become visible or invisible.
*/
@@ -2629,9 +2540,8 @@
* See {@code LauncherBindingDelegate}
*/
@Override
- public void bindAllWidgets(@NonNull final List<WidgetsListBaseEntry> allWidgets,
- @NonNull final List<WidgetsListBaseEntry> defaultWidgets) {
- mModelCallbacks.bindAllWidgets(allWidgets, defaultWidgets);
+ public void bindAllWidgets(@NonNull final List<WidgetsListBaseEntry> allWidgets) {
+ mModelCallbacks.bindAllWidgets(allWidgets);
}
@Override
diff --git a/src/com/android/launcher3/LauncherModel.kt b/src/com/android/launcher3/LauncherModel.kt
index 892a218..02d70ae 100644
--- a/src/com/android/launcher3/LauncherModel.kt
+++ b/src/com/android/launcher3/LauncherModel.kt
@@ -28,11 +28,12 @@
import com.android.launcher3.icons.IconCache
import com.android.launcher3.model.AddWorkspaceItemsTask
import com.android.launcher3.model.AllAppsList
-import com.android.launcher3.model.BaseLauncherBinder
+import com.android.launcher3.model.BaseLauncherBinder.BaseLauncherBinderFactory
import com.android.launcher3.model.BgDataModel
import com.android.launcher3.model.CacheDataUpdatedTask
import com.android.launcher3.model.ItemInstallQueue
import com.android.launcher3.model.LoaderTask
+import com.android.launcher3.model.LoaderTask.LoaderTaskFactory
import com.android.launcher3.model.ModelDbController
import com.android.launcher3.model.ModelDelegate
import com.android.launcher3.model.ModelInitializer
@@ -43,7 +44,8 @@
import com.android.launcher3.model.ReloadStringCacheTask
import com.android.launcher3.model.ShortcutsChangedTask
import com.android.launcher3.model.UserLockStateChangedTask
-import com.android.launcher3.model.WidgetsFilterDataProvider
+import com.android.launcher3.model.UserManagerState
+import com.android.launcher3.model.WorkspaceItemSpaceFinder
import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.model.data.WorkspaceItemInfo
import com.android.launcher3.pm.UserCache
@@ -71,30 +73,23 @@
@Inject
constructor(
@ApplicationContext private val context: Context,
- private val appProvider: Provider<LauncherAppState>,
+ private val taskControllerProvider: Provider<ModelTaskController>,
private val iconCache: IconCache,
private val prefs: LauncherPrefs,
private val installQueue: ItemInstallQueue,
- appFilter: AppFilter,
@Named("ICONS_DB") dbFileName: String?,
initializer: ModelInitializer,
lifecycle: DaggerSingletonTracker,
val modelDelegate: ModelDelegate,
+ private val mBgAllAppsList: AllAppsList,
+ private val mBgDataModel: BgDataModel,
+ private val loaderFactory: LoaderTaskFactory,
+ private val binderFactory: BaseLauncherBinderFactory,
+ private val spaceFinderFactory: Provider<WorkspaceItemSpaceFinder>,
) {
- private val widgetsFilterDataProvider = WidgetsFilterDataProvider.newInstance(context)
-
private val mCallbacksList = ArrayList<BgDataModel.Callbacks>(1)
- // < only access in worker thread >
- private val mBgAllAppsList = AllAppsList(iconCache, appFilter)
-
- /**
- * All the static data should be accessed on the background thread, A lock should be acquired on
- * this object when accessing any data from this model.
- */
- private val mBgDataModel = BgDataModel()
-
val modelDbController = ModelDbController(context)
private val mLock = Any()
@@ -142,7 +137,7 @@
/** Adds the provided items to the workspace. */
fun addAndBindAddedWorkspaceItems(itemList: List<Pair<ItemInfo?, Any?>?>) {
callbacks.forEach { it.preAddApps() }
- enqueueModelUpdateTask(AddWorkspaceItemsTask(itemList))
+ enqueueModelUpdateTask(AddWorkspaceItemsTask(itemList, spaceFinderFactory.get()))
}
fun getWriter(
@@ -151,11 +146,6 @@
owner: BgDataModel.Callbacks?,
) = ModelWriter(context, this, mBgDataModel, verifyChanges, cellPosMapper, owner)
- /** Returns the [WidgetsFilterDataProvider] that manages widget filters. */
- fun getWidgetsFilterDataProvider(): WidgetsFilterDataProvider {
- return widgetsFilterDataProvider
- }
-
/** Called when the icon for an app changes, outside of package event */
@WorkerThread
fun onAppIconChanged(packageName: String, user: UserHandle) {
@@ -176,10 +166,7 @@
/** Called when the model is destroyed */
fun destroy() {
mModelDestroyed = true
- MODEL_EXECUTOR.execute {
- modelDelegate.destroy()
- widgetsFilterDataProvider.destroy()
- }
+ MODEL_EXECUTOR.execute { modelDelegate.destroy() }
}
fun reloadStringCache() {
@@ -310,13 +297,7 @@
// Clear any pending bind-runnables from the synchronized load process.
callbacksList.forEach { MAIN_EXECUTOR.execute(it::clearPendingBinds) }
- val launcherBinder =
- BaseLauncherBinder(
- appProvider.get(),
- mBgDataModel,
- mBgAllAppsList,
- callbacksList,
- )
+ val launcherBinder = binderFactory.createBinder(callbacksList)
if (bindDirectly) {
// Divide the set of loaded items into those that we are binding synchronously,
// and everything else that is to be bound normally (asynchronously).
@@ -328,15 +309,7 @@
launcherBinder.bindWidgets()
return true
} else {
- val task =
- LoaderTask(
- appProvider.get(),
- mBgAllAppsList,
- mBgDataModel,
- this.modelDelegate,
- launcherBinder,
- widgetsFilterDataProvider,
- )
+ val task = loaderFactory.newLoaderTask(launcherBinder, UserManagerState())
mLoaderTask = task
// Always post the loader task, instead of running directly
@@ -437,15 +410,7 @@
/** Called when the labels for the widgets has updated in the icon cache. */
fun onWidgetLabelsUpdated(updatedPackages: HashSet<String?>, user: UserHandle) {
enqueueModelUpdateTask { taskController, dataModel, _ ->
- dataModel.widgetsModel.onPackageIconsUpdated(updatedPackages, user, appProvider.get())
- taskController.bindUpdatedWidgets(dataModel)
- }
- }
-
- /** Called when the widget filters are refreshed and available to bind to the model. */
- fun onWidgetFiltersLoaded() {
- enqueueModelUpdateTask { taskController, dataModel, _ ->
- dataModel.widgetsModel.updateWidgetFilters(widgetsFilterDataProvider)
+ dataModel.widgetsModel.onPackageIconsUpdated(updatedPackages, user)
taskController.bindUpdatedWidgets(dataModel)
}
}
@@ -459,17 +424,7 @@
// Loader has not yet run.
return@execute
}
- task.execute(
- ModelTaskController(
- appProvider.get(),
- mBgDataModel,
- mBgAllAppsList,
- this,
- MAIN_EXECUTOR,
- ),
- mBgDataModel,
- mBgAllAppsList,
- )
+ task.execute(taskControllerProvider.get(), mBgDataModel, mBgAllAppsList)
}
}
@@ -496,7 +451,7 @@
fun refreshAndBindWidgetsAndShortcuts(packageUser: PackageUserKey?) {
enqueueModelUpdateTask { taskController, dataModel, _ ->
- dataModel.widgetsModel.update(taskController.app, packageUser)
+ dataModel.widgetsModel.update(packageUser)
taskController.bindUpdatedWidgets(dataModel)
}
}
diff --git a/src/com/android/launcher3/ModelCallbacks.kt b/src/com/android/launcher3/ModelCallbacks.kt
index d01f35d..32b47d0 100644
--- a/src/com/android/launcher3/ModelCallbacks.kt
+++ b/src/com/android/launcher3/ModelCallbacks.kt
@@ -234,11 +234,8 @@
PopupContainerWithArrow.dismissInvalidPopup(launcher)
}
- override fun bindAllWidgets(
- allWidgets: List<WidgetsListBaseEntry>,
- defaultWidgets: List<WidgetsListBaseEntry>,
- ) {
- launcher.widgetPickerDataProvider.setWidgets(allWidgets, defaultWidgets)
+ override fun bindAllWidgets(allWidgets: List<WidgetsListBaseEntry>) {
+ launcher.widgetPickerDataProvider.setWidgets(allWidgets)
}
/** Returns the ids of the workspaces to bind. */
diff --git a/src/com/android/launcher3/PillColorPorovider.kt b/src/com/android/launcher3/PillColorPorovider.kt
index 347c5d6..e7f37bd 100644
--- a/src/com/android/launcher3/PillColorPorovider.kt
+++ b/src/com/android/launcher3/PillColorPorovider.kt
@@ -58,13 +58,8 @@
}
fun setup() {
- appTitlePillPaint.color =
- context.resources.getColor(
- R.color.material_color_surface_container_lowest,
- context.theme,
- )
- appTitleTextPaint.color =
- context.resources.getColor(R.color.material_color_on_surface, context.theme)
+ appTitlePillPaint.color = context.getColor(R.color.materialColorSurfaceContainer)
+ appTitleTextPaint.color = context.getColor(R.color.materialColorOnSurface)
isMatchaEnabledInternal = Settings.Secure.getInt(context.contentResolver, MATCHA_SETTING, 0)
isMatchaEnabled = isMatchaEnabledInternal != 0
}
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index cb3a0bc..d6ae3a6 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -18,6 +18,7 @@
import static com.android.launcher3.BuildConfig.WIDGET_ON_FIRST_SCREEN;
import static com.android.launcher3.Flags.enableSmartspaceAsAWidget;
+import static com.android.launcher3.graphics.ShapeDelegate.DEFAULT_PATH_SIZE;
import static com.android.launcher3.icons.BitmapInfo.FLAG_THEMED;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
@@ -39,6 +40,7 @@
import android.graphics.LightingColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
+import android.graphics.Path;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
@@ -98,6 +100,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Locale;
+import java.util.Objects;
import java.util.function.Predicate;
/**
@@ -274,8 +277,12 @@
*/
public static void mapCoordInSelfToDescendant(View descendant, View root, float[] coord) {
sMatrix.reset();
+ //TODO(b/307488755) when implemented this check should be removed
+ if (!Objects.equals(descendant.getWindowId(), root.getWindowId())) {
+ return;
+ }
View v = descendant;
- while(v != root) {
+ while (v != root) {
sMatrix.postTranslate(-v.getScrollX(), -v.getScrollY());
sMatrix.postConcat(v.getMatrix());
sMatrix.postTranslate(v.getLeft(), v.getTop());
@@ -632,7 +639,7 @@
Drawable badge = null;
if ((info instanceof ItemInfoWithIcon iiwi) && !iiwi.getMatchingLookupFlag().useLowRes()) {
- badge = iiwi.bitmap.getBadgeDrawable(context, useTheme);
+ badge = iiwi.bitmap.getBadgeDrawable(context, useTheme, getIconShapeOrNull(context));
}
if (info instanceof PendingAddShortcutInfo) {
@@ -659,8 +666,11 @@
// Only fetch badge if the icon is on workspace
if (info.id != ItemInfo.NO_ID && badge == null) {
badge = appState.getIconCache().getShortcutInfoBadge(si).newIcon(
- context, ThemeManager.INSTANCE.get(context).isIconThemeEnabled()
- ? FLAG_THEMED : 0);
+ context,
+ ThemeManager.INSTANCE.get(context).isIconThemeEnabled()
+ ? FLAG_THEMED : 0,
+ getIconShapeOrNull(context)
+ );
}
}
} else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
@@ -706,10 +716,11 @@
if (badge == null) {
badge = BitmapInfo.LOW_RES_INFO.withFlags(
- UserCache.INSTANCE.get(context)
- .getUserInfo(info.user)
- .applyBitmapInfoFlags(FlagOp.NO_OP))
- .getBadgeDrawable(context, useTheme);
+ UserCache.INSTANCE.get(context)
+ .getUserInfo(info.user)
+ .applyBitmapInfoFlags(FlagOp.NO_OP)
+ )
+ .getBadgeDrawable(context, useTheme, getIconShapeOrNull(context));
if (badge == null) {
badge = new ColorDrawable(Color.TRANSPARENT);
}
@@ -939,4 +950,18 @@
}
return null;
}
+
+ /**
+ * Returns current icon shape to use for badges if flag is on, otherwise null.
+ */
+ @Nullable
+ public static Path getIconShapeOrNull(Context context) {
+ if (Flags.enableLauncherIconShapes()) {
+ return ThemeManager.INSTANCE.get(context)
+ .getIconShape()
+ .getPath(DEFAULT_PATH_SIZE);
+ } else {
+ return null;
+ }
+ }
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 1b5e2e6..59f84ab 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -67,6 +67,7 @@
import android.widget.FrameLayout;
import android.widget.Toast;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.core.view.ViewCompat;
@@ -136,7 +137,6 @@
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
-import java.util.stream.Collectors;
/**
* The workspace is a wide area with a wallpaper and a finite number of pages.
@@ -658,7 +658,6 @@
}
// Remove the pages and clear the screen models
- removeFolderListeners();
removeAllViews();
mScreenOrder.clear();
mWorkspaceScreens.clear();
@@ -1913,7 +1912,7 @@
boolean aboveShortcut = Folder.willAccept(dropOverView.getTag())
&& ((ItemInfo) dropOverView.getTag()).container != CONTAINER_HOTSEAT_PREDICTION;
- boolean willBecomeShortcut = Folder.willAcceptItemType(info.itemType);
+ boolean willBecomeShortcut = FolderInfo.willAcceptItemType(info.itemType);
return (aboveShortcut && willBecomeShortcut);
}
@@ -1993,8 +1992,8 @@
fi.performCreateAnimation(destInfo, v, sourceInfo, d, folderLocation, scale);
} else {
fi.prepareCreateAnimation(v);
- fi.addItem(destInfo);
- fi.addItem(sourceInfo);
+ fi.getFolder().addFolderContent(destInfo);
+ fi.getFolder().addFolderContent(sourceInfo);
}
return true;
}
@@ -3163,7 +3162,7 @@
+ "Workspace#onDropCompleted. Please file a bug. ");
}
}
- View cell = getHomescreenIconByItemId(d.originalDragInfo.id);
+ View cell = getViewByItemId(d.originalDragInfo.id);
if (d.cancelled && cell != null) {
cell.setVisibility(VISIBLE);
}
@@ -3208,21 +3207,6 @@
});
}
- /**
- * Removes all folder listeners
- */
- public void removeFolderListeners() {
- mapOverItems(new ItemOperator() {
- @Override
- public boolean evaluate(ItemInfo info, View view) {
- if (view instanceof FolderIcon) {
- ((FolderIcon) view).removeListeners();
- }
- return false;
- }
- });
- }
-
public boolean isDropEnabled() {
return true;
}
@@ -3312,29 +3296,9 @@
return layouts;
}
- public View getHomescreenIconByItemId(final int id) {
- return getFirstMatch((info, v) -> info != null && info.id == id);
- }
-
public LauncherAppWidgetHostView getWidgetForAppWidgetId(final int appWidgetId) {
- return (LauncherAppWidgetHostView) getFirstMatch((info, v) ->
- (info instanceof LauncherAppWidgetInfo) &&
- ((LauncherAppWidgetInfo) info).appWidgetId == appWidgetId);
- }
-
- public View getFirstMatch(final ItemOperator operator) {
- final View[] value = new View[1];
- mapOverItems(new ItemOperator() {
- @Override
- public boolean evaluate(ItemInfo info, View v) {
- if (operator.evaluate(info, v)) {
- value[0] = v;
- return true;
- }
- return false;
- }
- });
- return value[0];
+ return (LauncherAppWidgetHostView) mapOverItems((info, v) ->
+ (info instanceof LauncherAppWidgetInfo lawi) && lawi.appWidgetId == appWidgetId);
}
void clearDropTargets() {
@@ -3368,15 +3332,15 @@
if (child instanceof DropTarget) {
mDragController.removeDropTarget((DropTarget) child);
}
- } else if (child instanceof FolderIcon) {
+ } else if (child instanceof FolderIcon folderIcon) {
FolderInfo folderInfo = (FolderInfo) info;
- List<ItemInfo> matches = folderInfo.getContents().stream()
+ ItemInfo[] matches = folderInfo.getContents().stream()
.filter(matcher)
- .collect(Collectors.toList());
- if (!matches.isEmpty()) {
- folderInfo.removeAll(matches, false);
- if (((FolderIcon) child).getFolder().isOpen()) {
- ((FolderIcon) child).getFolder().close(false /* animate */);
+ .toArray(ItemInfo[]::new);
+ if (matches.length > 0) {
+ folderIcon.getFolder().removeFolderContent(false, matches);
+ if (folderIcon.getFolder().isOpen()) {
+ folderIcon.getFolder().close(false /* animate */);
}
}
} else if (info instanceof AppPairInfo api) {
@@ -3393,31 +3357,26 @@
}
@Override
- public void mapOverItems(ItemOperator op) {
- for (CellLayout layout : getWorkspaceAndHotseatCellLayouts()) {
- if (mapOverCellLayout(layout, op) != null) {
- return;
- }
- }
+ public View mapOverItems(@NonNull ItemOperator op) {
+ return mapOverCellLayouts(getWorkspaceAndHotseatCellLayouts(), op);
}
/**
- * Perform {param operator} over all the items in a given {param layout}.
- *
- * @return The first item that satisfies the operator or null.
+ * Perform {param op} over all the items in the provided {param layouts} until a match is found
*/
- public View mapOverCellLayout(CellLayout layout, ItemOperator operator) {
- // TODO(b/128460496) Potential race condition where layout is not yet loaded
- if (layout == null) {
- return null;
- }
- ShortcutAndWidgetContainer container = layout.getShortcutsAndWidgets();
- // map over all the shortcuts on the workspace
- final int itemCount = container.getChildCount();
- for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) {
- View item = container.getChildAt(itemIdx);
- if (operator.evaluate((ItemInfo) item.getTag(), item)) {
- return item;
+ public static View mapOverCellLayouts(CellLayout[] layouts, ItemOperator op) {
+ for (CellLayout layout : layouts) {
+ // TODO(b/128460496) Potential race condition where layout is not yet loaded
+ if (layout == null) continue;
+
+ ShortcutAndWidgetContainer container = layout.getShortcutsAndWidgets();
+ // map over all the shortcuts on the layout
+ final int itemCount = container.getChildCount();
+ for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) {
+ View item = container.getChildAt(itemIdx);
+ if (op.evaluate((ItemInfo) item.getTag(), item)) {
+ return item;
+ }
}
}
return null;
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 78b53a9..df34ccf 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -25,6 +25,7 @@
import androidx.annotation.Nullable;
import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.AppWidgetResizeFrame;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.ButtonDropTarget;
import com.android.launcher3.CellLayout;
@@ -82,6 +83,7 @@
protected static final int MOVE_TO_WORKSPACE = R.id.action_move_to_workspace;
protected static final int RESIZE = R.id.action_resize;
public static final int DEEP_SHORTCUTS = R.id.action_deep_shortcuts;
+ public static final int CLOSE = R.id.action_close;
public LauncherAccessibilityDelegate(Launcher launcher) {
super(launcher);
@@ -104,6 +106,8 @@
RESIZE, R.string.action_resize, KeyEvent.KEYCODE_R));
mActions.put(DEEP_SHORTCUTS, new LauncherAction(DEEP_SHORTCUTS,
R.string.action_deep_shortcut, KeyEvent.KEYCODE_S));
+ mActions.put(CLOSE, new LauncherAction(CLOSE,
+ R.string.action_close, KeyEvent.KEYCODE_X));
}
private static boolean isNotInShortcutMenu(@Nullable View view) {
@@ -137,6 +141,10 @@
}
}
+ if (host instanceof AppWidgetResizeFrame) {
+ out.add(mActions.get(CLOSE));
+ }
+
if (supportAddToWorkSpace(item)) {
out.add(mActions.get(ADD_TO_WORKSPACE));
}
@@ -183,22 +191,28 @@
}
return dragCondition != null;
} else if (action == MOVE) {
- return beginAccessibleDrag(host, item, fromKeyboard);
+ final View itemView = (host instanceof AppWidgetResizeFrame)
+ ? ((AppWidgetResizeFrame) host).getViewForAccessibility()
+ : host;
+ return beginAccessibleDrag(itemView, item, fromKeyboard);
} else if (action == ADD_TO_WORKSPACE) {
return addToWorkspace(item, true /*accessibility*/, null /*finishCallback*/);
} else if (action == MOVE_TO_WORKSPACE) {
return moveToWorkspace(item);
} else if (action == RESIZE) {
+ final View itemView = (host instanceof AppWidgetResizeFrame)
+ ? ((AppWidgetResizeFrame) host).getViewForAccessibility()
+ : host;
final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) item;
- List<OptionItem> actions = getSupportedResizeActions(host, info);
+ List<OptionItem> actions = getSupportedResizeActions(itemView, info);
Rect pos = new Rect();
- mContext.getDragLayer().getDescendantRectRelativeToSelf(host, pos);
+ mContext.getDragLayer().getDescendantRectRelativeToSelf(itemView, pos);
ArrowPopup popup = OptionsPopupView.show(mContext, new RectF(pos), actions, false);
popup.requestFocus();
popup.addOnCloseCallback(() -> {
- host.requestFocus();
- host.sendAccessibilityEvent(TYPE_VIEW_FOCUSED);
- host.performAccessibilityAction(ACTION_ACCESSIBILITY_FOCUS, null);
+ itemView.requestFocus();
+ itemView.sendAccessibilityEvent(TYPE_VIEW_FOCUSED);
+ itemView.performAccessibilityAction(ACTION_ACCESSIBILITY_FOCUS, null);
AbstractFloatingView.closeOpenViews(mContext, /* animate= */ false,
AbstractFloatingView.TYPE_WIDGET_RESIZE_FRAME);
});
@@ -208,6 +222,11 @@
: (host instanceof BubbleTextHolder
? ((BubbleTextHolder) host).getBubbleText() : null);
return btv != null && PopupContainerWithArrow.showForIcon(btv) != null;
+ } else if (action == CLOSE) {
+ if (host instanceof AppWidgetResizeFrame) {
+ AbstractFloatingView.closeOpenViews(mContext, /* animate= */ false,
+ AbstractFloatingView.TYPE_WIDGET_RESIZE_FRAME);
+ }
} else {
for (ButtonDropTarget dropTarget : mContext.getDropTargetBar().getDropTargets()) {
if (dropTarget.supportsAccessibilityDrop(item, host)
@@ -222,6 +241,10 @@
private List<OptionItem> getSupportedResizeActions(View host, LauncherAppWidgetInfo info) {
List<OptionItem> actions = new ArrayList<>();
+ if (host instanceof AppWidgetResizeFrame) {
+ return getSupportedResizeActions(
+ ((AppWidgetResizeFrame) host).getViewForAccessibility(), info);
+ }
AppWidgetProviderInfo providerInfo = ((LauncherAppWidgetHostView) host).getAppWidgetInfo();
if (providerInfo == null) {
return actions;
@@ -496,7 +519,7 @@
Folder folder = Folder.getOpen(mContext);
folder.close(true);
WorkspaceItemInfo info = (WorkspaceItemInfo) item;
- folder.getInfo().remove(info, false);
+ folder.removeFolderContent(false, info);
final int[] coordinates = new int[2];
final int screenId = findSpaceOnWorkspace(item, coordinates);
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index fafa60b..f60896e 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -181,6 +181,7 @@
private ScrimView mScrimView;
private int mHeaderColor;
private int mBottomSheetBackgroundColor;
+ private float mBottomSheetBackgroundAlpha = 1f;
private int mTabsProtectionAlpha;
@Nullable private AllAppsTransitionController mAllAppsTransitionController;
@@ -311,7 +312,17 @@
0,
0 // Bottom left
};
- mBottomSheetBackgroundColor = getContext().getColor(R.color.materialColorSurfaceDim);
+ if (Flags.allAppsBlur()) {
+ int resId = Utilities.isDarkTheme(getContext())
+ ? android.R.color.system_accent1_800 : android.R.color.system_accent1_100;
+ int layerAbove = ColorUtils.setAlphaComponent(getResources().getColor(resId, null),
+ (int) (0.4f * 255));
+ int layerBelow = ColorUtils.setAlphaComponent(Color.WHITE, (int) (0.1f * 255));
+ mBottomSheetBackgroundColor = ColorUtils.compositeColors(layerAbove, layerBelow);
+ } else {
+ mBottomSheetBackgroundColor = getContext().getColor(R.color.materialColorSurfaceDim);
+ }
+ mBottomSheetBackgroundAlpha = Color.alpha(mBottomSheetBackgroundColor) / 255.0f;
updateBackgroundVisibility(mActivityContext.getDeviceProfile());
mSearchUiManager.initializeSearch(this);
}
@@ -1152,7 +1163,7 @@
if (!grid.isVerticalBarLayout() || FeatureFlags.enableResponsiveWorkspace()) {
int topPadding = grid.allAppsPadding.top;
- if (isSearchBarFloating() && !grid.isTablet) {
+ if (isSearchBarFloating() && !grid.shouldShowAllAppsOnSheet()) {
topPadding += getResources().getDimensionPixelSize(
R.dimen.all_apps_additional_top_padding_floating_search);
}
@@ -1401,7 +1412,7 @@
// Draw full background panel for tablets.
if (hasBottomSheet) {
mHeaderPaint.setColor(mBottomSheetBackgroundColor);
- mHeaderPaint.setAlpha(255);
+ mHeaderPaint.setAlpha((int) (mBottomSheetBackgroundAlpha * 255));
mTmpRectF.set(
leftWithScale,
@@ -1424,6 +1435,10 @@
return;
}
+ if (hasBottomSheet) {
+ mHeaderPaint.setAlpha((int) (mHeaderPaint.getAlpha() * mBottomSheetBackgroundAlpha));
+ }
+
// Draw header on background panel
final float headerBottomNoScale =
getHeaderBottom() + getVisibleContainerView().getPaddingTop();
@@ -1455,7 +1470,11 @@
mHeaderPaint.setColor(Color.BLUE);
mHeaderPaint.setAlpha(255);
} else {
- mHeaderPaint.setAlpha((int) (getAlpha() * mTabsProtectionAlpha));
+ float tabAlpha = getAlpha() * mTabsProtectionAlpha;
+ if (hasBottomSheet) {
+ tabAlpha *= mBottomSheetBackgroundAlpha;
+ }
+ mHeaderPaint.setAlpha((int) tabAlpha);
}
float left = 0f;
float right = canvas.getWidth();
@@ -1507,7 +1526,7 @@
public int getHeaderBottom() {
int bottom = (int) getTranslationY() + mHeader.getClipTop();
if (isSearchBarFloating()) {
- if (mActivityContext.getDeviceProfile().isTablet) {
+ if (mActivityContext.getDeviceProfile().shouldShowAllAppsOnSheet()) {
return bottom + mBottomSheetBackground.getTop();
}
return bottom;
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 4cc31d2..350f763 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -290,7 +290,8 @@
private void onScaleProgressChanged() {
final float scaleProgress = mAllAppScale.value;
SCALE_PROPERTY.set(mLauncher.getAppsView(), scaleProgress);
- if (!mLauncher.getAppsView().isSearching() || !mLauncher.getDeviceProfile().isTablet) {
+ if (!mLauncher.getAppsView().isSearching()
+ || !mLauncher.getDeviceProfile().shouldShowAllAppsOnSheet()) {
mLauncher.getScrimView().setScrimHeaderScale(scaleProgress);
}
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 709b52a..f223eaa 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -15,10 +15,14 @@
*/
package com.android.launcher3.allapps;
+import static android.multiuser.Flags.enableMovingContentIntoPrivateSpace;
+
import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_BOTTOM_VIEW_TO_SCROLL_TO;
+import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_MASK_PRIVATE_SPACE_HEADER;
import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_BOTTOM_LEFT;
import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_BOTTOM_RIGHT;
import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_NOTHING;
+import static com.android.launcher3.icons.BitmapInfo.FLAG_NO_BADGE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_PREINSTALLED_APPS_COUNT;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_USER_INSTALLED_APPS_COUNT;
@@ -60,6 +64,7 @@
AllAppsStore.OnUpdateListener {
public static final String TAG = "AlphabeticalAppsList";
+ public static final String PRIVATE_SPACE_PACKAGE = "com.android.privatespace";
private final WorkProfileManager mWorkProviderManager;
@@ -382,7 +387,7 @@
private int addPrivateSpaceApps(int position) {
// Add Install Apps Button first.
- if (Flags.privateSpaceAppInstallerButton()) {
+ if (Flags.privateSpaceAppInstallerButton() && !enableMovingContentIntoPrivateSpace()) {
mPrivateProviderManager.addPrivateSpaceInstallAppButton(mAdapterItems);
position++;
}
@@ -419,6 +424,30 @@
// Add system apps.
position = addAppsWithSections(split.get(false), position);
+ if (enableMovingContentIntoPrivateSpace()) {
+ // Look for the private space app via package and move it after header.
+ int headerIndex = -1;
+ int privateSpaceAppIndex = -1;
+ for (int i = 0; i < mAdapterItems.size(); i++) {
+ BaseAllAppsAdapter.AdapterItem currentItem = mAdapterItems.get(i);
+ if (currentItem.viewType == VIEW_TYPE_MASK_PRIVATE_SPACE_HEADER) {
+ headerIndex = i;
+ }
+ if (currentItem.itemInfo != null && Objects.equals(
+ currentItem.itemInfo.getTargetPackage(), PRIVATE_SPACE_PACKAGE)) {
+ currentItem.itemInfo.bitmap.creationFlags |= FLAG_NO_BADGE;
+ currentItem.itemInfo.contentDescription =
+ mPrivateProviderManager.getPsAppContentDesc();
+ privateSpaceAppIndex = i;
+ }
+ }
+ if (headerIndex != -1 && privateSpaceAppIndex != -1) {
+ BaseAllAppsAdapter.AdapterItem movedItem =
+ mAdapterItems.remove(privateSpaceAppIndex);
+ // Move the icon after the header.
+ mAdapterItems.add(headerIndex + 1, movedItem);
+ }
+ }
return position;
}
diff --git a/src/com/android/launcher3/allapps/PrivateProfileManager.java b/src/com/android/launcher3/allapps/PrivateProfileManager.java
index 609edd2..1bc1b17 100644
--- a/src/com/android/launcher3/allapps/PrivateProfileManager.java
+++ b/src/com/android/launcher3/allapps/PrivateProfileManager.java
@@ -142,6 +142,7 @@
private PrivateSpaceSettingsButton mPrivateSpaceSettingsButton;
@Nullable
private ConstraintLayout mFloatingMaskView;
+ private final String mPrivateSpaceAppContentDesc;
private final String mLockedStateContentDesc;
private final String mUnLockedStateContentDesc;
@@ -157,6 +158,8 @@
UI_HELPER_EXECUTOR.post(() -> initializeInBackgroundThread(appContext));
mPsHeaderHeight = mAllApps.getContext().getResources().getDimensionPixelSize(
R.dimen.ps_header_height);
+ mPrivateSpaceAppContentDesc = mAllApps.getContext()
+ .getString(R.string.ps_app_content_description);
mLockedStateContentDesc = mAllApps.getContext()
.getString(R.string.ps_container_lock_button_content_description);
mUnLockedStateContentDesc = mAllApps.getContext()
@@ -929,6 +932,10 @@
return mPsHeaderHeight;
}
+ String getPsAppContentDesc() {
+ return mPrivateSpaceAppContentDesc;
+ }
+
boolean isPrivateSpaceItem(BaseAllAppsAdapter.AdapterItem item) {
return getItemInfoMatcher().test(item.itemInfo) || item.decorationInfo != null
|| (item.itemInfo instanceof PrivateSpaceInstallAppButtonInfo);
diff --git a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
index 150761f..c499097 100644
--- a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
+++ b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
@@ -28,6 +28,8 @@
import com.android.launcher3.graphics.ThemeManager;
import com.android.launcher3.icons.LauncherIcons.IconPool;
import com.android.launcher3.model.ItemInstallQueue;
+import com.android.launcher3.model.LoaderCursor.LoaderCursorFactory;
+import com.android.launcher3.model.WidgetsFilterDataProvider;
import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.util.ApiWrapper;
@@ -84,6 +86,9 @@
RemoveAnimationSettingsTracker getRemoveAnimationSettingsTracker();
LauncherAppState getLauncherAppState();
GridCustomizationsProxy getGridCustomizationsProxy();
+ WidgetsFilterDataProvider getWidgetsFilterDataProvider();
+
+ LoaderCursorFactory getLoaderCursorFactory();
/** Builder for LauncherBaseAppComponent. */
interface Builder {
diff --git a/src/com/android/launcher3/debug/TestEventEmitter.java b/src/com/android/launcher3/debug/TestEventEmitter.java
index ed3b4bb..db69bc2 100644
--- a/src/com/android/launcher3/debug/TestEventEmitter.java
+++ b/src/com/android/launcher3/debug/TestEventEmitter.java
@@ -34,7 +34,9 @@
RESIZE_FRAME_SHOWING("RESIZE_FRAME_SHOWING"),
WORKSPACE_FINISH_LOADING("WORKSPACE_FINISH_LOADING"),
SPRING_LOADED_STATE_STARTED("SPRING_LOADED_STATE_STARTED"),
- SPRING_LOADED_STATE_COMPLETED("SPRING_LOADED_STATE_COMPLETED");
+ SPRING_LOADED_STATE_COMPLETED("SPRING_LOADED_STATE_COMPLETED"),
+
+ LAUNCHER_STATE_COMPLETED("LAUNCHER_STATE_COMPLETED");
TestEvent(String event) {
}
diff --git a/src/com/android/launcher3/dot/FolderDotInfo.java b/src/com/android/launcher3/dot/FolderDotInfo.java
index 54800a0..cb91db7 100644
--- a/src/com/android/launcher3/dot/FolderDotInfo.java
+++ b/src/com/android/launcher3/dot/FolderDotInfo.java
@@ -30,6 +30,10 @@
private int mNumNotifications;
+ public void reset() {
+ mNumNotifications = 0;
+ }
+
public void addDotInfo(DotInfo dotToAdd) {
if (dotToAdd == null) {
return;
@@ -39,15 +43,6 @@
mNumNotifications, MIN_COUNT, DotInfo.MAX_COUNT);
}
- public void subtractDotInfo(DotInfo dotToSubtract) {
- if (dotToSubtract == null) {
- return;
- }
- mNumNotifications -= dotToSubtract.getNotificationKeys().size();
- mNumNotifications = Utilities.boundToRange(
- mNumNotifications, MIN_COUNT, DotInfo.MAX_COUNT);
- }
-
@Override
public int getNotificationCount() {
return mNumNotifications;
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index 613b430..284faba 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -16,6 +16,7 @@
package com.android.launcher3.dragndrop;
+import static com.android.launcher3.Flags.removeAppsRefreshOnRightClick;
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_NOT_PINNABLE;
import android.graphics.Point;
@@ -521,17 +522,21 @@
mDragObject.dragComplete = true;
if (mIsInPreDrag) {
- if (dropTarget != null) {
- dropTarget.onDragExit(mDragObject);
+ if (removeAppsRefreshOnRightClick()) {
+ mDragObject.cancelled = true;
+ } else {
+ if (dropTarget != null) {
+ dropTarget.onDragExit(mDragObject);
+ }
+ return;
}
- return;
}
// Drop onto the target.
boolean accepted = false;
if (dropTarget != null) {
dropTarget.onDragExit(mDragObject);
- if (dropTarget.acceptDrop(mDragObject)) {
+ if (!mIsInPreDrag && dropTarget.acceptDrop(mDragObject)) {
if (flingAnimation != null) {
flingAnimation.run();
} else {
@@ -540,7 +545,7 @@
accepted = true;
}
}
- final View dropTargetAsView = dropTarget instanceof View ? (View) dropTarget : null;
+ final View dropTargetAsView = dropTarget.getDropView();
dispatchDropComplete(dropTargetAsView, accepted);
}
diff --git a/src/com/android/launcher3/dragndrop/LauncherDragController.java b/src/com/android/launcher3/dragndrop/LauncherDragController.java
index 4aa3673..dd433c0 100644
--- a/src/com/android/launcher3/dragndrop/LauncherDragController.java
+++ b/src/com/android/launcher3/dragndrop/LauncherDragController.java
@@ -15,7 +15,10 @@
*/
package com.android.launcher3.dragndrop;
+import static android.view.View.VISIBLE;
+
import static com.android.launcher3.AbstractFloatingView.TYPE_DISCOVERY_BOUNCE;
+import static com.android.launcher3.Flags.removeAppsRefreshOnRightClick;
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
import static com.android.launcher3.LauncherState.EDIT_MODE;
import static com.android.launcher3.LauncherState.NORMAL;
@@ -25,6 +28,7 @@
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.view.HapticFeedbackConstants;
+import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.Nullable;
@@ -33,10 +37,13 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
+import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.accessibility.DragViewStateAnnouncer;
+import com.android.launcher3.dragndrop.DragOptions.PreDragCondition;
import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.util.TouchUtil;
import com.android.launcher3.widget.util.WidgetDragScaleUtils;
/**
@@ -47,6 +54,9 @@
private static final boolean PROFILE_DRAWING_DURING_DRAG = false;
private final FlingToDeleteHelper mFlingToDeleteHelper;
+ /** Whether or not the drag operation is triggered by mouse right click. */
+ private boolean mIsInMouseRightClick = false;
+
public LauncherDragController(Launcher launcher) {
super(launcher);
mFlingToDeleteHelper = new FlingToDeleteHelper(launcher);
@@ -69,6 +79,28 @@
android.os.Debug.startMethodTracing("Launcher");
}
+ if (removeAppsRefreshOnRightClick() && mIsInMouseRightClick
+ && options.preDragCondition == null
+ && originalView instanceof View v) {
+ options.preDragCondition = new PreDragCondition() {
+
+ @Override
+ public boolean shouldStartDrag(double distanceDragged) {
+ return false;
+ }
+
+ @Override
+ public void onPreDragStart(DragObject dragObject) {
+ // Set it to visible so the text of FolderIcon would not flash (avoid it from
+ // being invisible and then visible)
+ v.setVisibility(VISIBLE);
+ }
+
+ @Override
+ public void onPreDragEnd(DragObject dragObject, boolean dragStarted) { }
+ };
+ }
+
mActivity.hideKeyboard();
AbstractFloatingView.closeOpenViews(mActivity, false, TYPE_DISCOVERY_BOUNCE);
@@ -191,7 +223,7 @@
@Override
protected void exitDrag() {
- if (!mActivity.isInState(EDIT_MODE)) {
+ if (!mIsInPreDrag && !mActivity.isInState(EDIT_MODE)) {
mActivity.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
}
}
@@ -218,4 +250,13 @@
dropCoordinates);
return mActivity.getWorkspace();
}
+
+ /**
+ * Intercepts touch events from a drag source view.
+ */
+ @Override
+ public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+ mIsInMouseRightClick = TouchUtil.isMouseRightClickDownOrMove(ev);
+ return super.onControllerInterceptTouchEvent(ev);
+ }
}
diff --git a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
index 36dad89..fedc118 100644
--- a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
+++ b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
@@ -53,18 +53,22 @@
} else if (index >= MAX_NUM_ITEMS_IN_PREVIEW) {
// Items beyond those displayed in the preview are animated to the center
mTmpPoint[0] = mTmpPoint[1] = mAvailableSpace / 2 - (mIconSize * totalScale) / 2;
- } else if (index == 0) {
- // top left
- getGridPosition(0, 0, mTmpPoint);
- } else if (index == 1) {
- // top right
- getGridPosition(0, 1, mTmpPoint);
- } else if (index == 2) {
- // bottom left
- getGridPosition(1, 0, mTmpPoint);
- } else if (index == 3) {
- // bottom right
- getGridPosition(1, 1, mTmpPoint);
+ } else if (Flags.enableLauncherIconShapes()) {
+ if (index == 0) {
+ // top left
+ getGridPosition(0, 0, mTmpPoint);
+ } else if (index == 1) {
+ // top right
+ getGridPosition(0, 1, mTmpPoint);
+ } else if (index == 2) {
+ // bottom left
+ getGridPosition(1, 0, mTmpPoint);
+ } else if (index == 3) {
+ // bottom right
+ getGridPosition(1, 1, mTmpPoint);
+ }
+ } else {
+ getPosition(index, curNumItems, mTmpPoint);
}
transX = mTmpPoint[0];
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 0ce7249..967af05 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -19,9 +19,6 @@
import static android.text.TextUtils.isEmpty;
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
-import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
-import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR;
-import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
import static com.android.launcher3.LauncherState.EDIT_MODE;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
@@ -29,6 +26,7 @@
import static com.android.launcher3.folder.FolderGridOrganizer.createFolderGridOrganizer;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_LABEL_UPDATED;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED;
+import static com.android.launcher3.model.data.FolderInfo.willAcceptItemType;
import static com.android.launcher3.testing.shared.TestProtocol.FOLDER_OPENED_MESSAGE;
import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
@@ -95,13 +93,12 @@
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.logging.StatsLogManager.StatsLogger;
import com.android.launcher3.model.data.FolderInfo;
-import com.android.launcher3.model.data.FolderInfo.FolderListener;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemFactory;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.pageindicators.PageIndicatorDots;
import com.android.launcher3.util.Executors;
-import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator;
+import com.android.launcher3.util.LauncherBindableItemsContainer;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.BaseDragLayer;
@@ -111,6 +108,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
@@ -122,8 +120,9 @@
* Represents a set of icons chosen by the user or generated by the system.
*/
public class Folder extends AbstractFloatingView implements ClipPathView, DragSource,
- View.OnLongClickListener, DropTarget, FolderListener, TextView.OnEditorActionListener,
- View.OnFocusChangeListener, DragListener, ExtendedEditText.OnBackKeyListener {
+ View.OnLongClickListener, DropTarget, TextView.OnEditorActionListener,
+ View.OnFocusChangeListener, DragListener, ExtendedEditText.OnBackKeyListener,
+ LauncherBindableItemsContainer {
private static final String TAG = "Launcher.Folder";
private static final boolean DEBUG = false;
@@ -178,15 +177,6 @@
return o instanceof ItemInfo info && willAcceptItemType(info.itemType);
}
- /**
- * Checks if {@code itemType} is a type that can be placed in folders.
- */
- public static boolean willAcceptItemType(int itemType) {
- return itemType == ITEM_TYPE_APPLICATION
- || itemType == ITEM_TYPE_DEEP_SHORTCUT
- || itemType == ITEM_TYPE_APP_PAIR;
- }
-
private Alarm mReorderAlarm = new Alarm(Looper.getMainLooper());
private Alarm mOnExitAlarm = new Alarm(Looper.getMainLooper());
private Alarm mOnScrollHintAlarm = new Alarm(Looper.getMainLooper());
@@ -242,7 +232,10 @@
private boolean mIsExternalDrag;
private boolean mIsDragInProgress = false;
private boolean mDeleteFolderOnDropCompleted = false;
+
private boolean mSuppressFolderDeletion = false;
+ private boolean mSuppressContentUpdate = false;
+
private boolean mItemAddedBackToSelfViaIcon = false;
private boolean mIsEditingName = false;
@@ -384,9 +377,8 @@
// We do not want to get events for the item being removed, as they will get handled
// when the drop completes
- try (SuppressInfoChanges s = new SuppressInfoChanges()) {
- mInfo.remove(dragObject.dragInfo, true);
- }
+ executeWithContentUpdateSuppressed(() -> removeFolderContent(true, dragObject.dragInfo));
+
mIsDragInProgress = true;
mItemAddedBackToSelfViaIcon = false;
}
@@ -531,16 +523,7 @@
lp.customPosition = true;
setLayoutParams(lp);
}
- mItemsInvalidated = true;
- mInfo.addListener(this);
-
- if (!isEmpty(mInfo.title)) {
- mFolderName.setText(mInfo.title);
- mFolderName.setHint(null);
- } else {
- mFolderName.setText("");
- mFolderName.setHint(R.string.folder_hint_text);
- }
+ reapplyItemInfo();
// In case any children didn't come across during loading, clean up the folder accordingly
mFolderIcon.post(() -> {
if (getItemCount() <= 1) {
@@ -549,6 +532,17 @@
});
}
+ public void reapplyItemInfo() {
+ mItemsInvalidated = true;
+
+ if (!isEmpty(mInfo.title)) {
+ mFolderName.setText(mInfo.title);
+ mFolderName.setHint(null);
+ } else {
+ mFolderName.setText("");
+ mFolderName.setHint(R.string.folder_hint_text);
+ }
+ }
/**
* Show suggested folder title in FolderEditText if the first suggestion is non-empty, push
@@ -679,7 +673,6 @@
if (!shouldAnimateOpen(items)) {
return;
}
-
Folder openFolder = getOpen(mActivityContext);
closeOpenFolder(openFolder);
@@ -953,9 +946,7 @@
@Override
public boolean acceptDrop(DragObject d) {
- final ItemInfo item = d.dragInfo;
- final int itemType = item.itemType;
- return Folder.willAcceptItemType(itemType);
+ return willAcceptItemType(d.dragInfo.itemType);
}
public void onDragEnter(DragObject d) {
@@ -1114,13 +1105,14 @@
View icon = (mCurrentDragView != null && mCurrentDragView.getTag() == info)
? mCurrentDragView : mContent.createNewView(info);
ArrayList<View> views = getIconsInReadingOrder();
- info.rank = Utilities.boundToRange(info.rank, 0, views.size());
- views.add(info.rank, icon);
- mContent.arrangeChildren(views);
- mItemsInvalidated = true;
+ if (!views.contains(icon)) {
+ info.rank = Utilities.boundToRange(info.rank, 0, views.size());
+ views.add(info.rank, icon);
+ mContent.arrangeChildren(views);
+ mItemsInvalidated = true;
- try (SuppressInfoChanges s = new SuppressInfoChanges()) {
- mFolderIcon.onDrop(d, true /* itemReturnedOnFailedDrop */);
+ executeWithContentUpdateSuppressed(
+ () -> mFolderIcon.onDrop(d, true /* itemReturnedOnFailedDrop */));
}
}
@@ -1414,9 +1406,7 @@
rearrangeChildren();
// Temporarily suppress the listener, as we did all the work already here.
- try (SuppressInfoChanges s = new SuppressInfoChanges()) {
- mInfo.add(si, mEmptyCellRank, false);
- }
+ executeWithContentUpdateSuppressed(() -> addFolderContent(si, mEmptyCellRank, false));
// We only need to update the locations if it doesn't get handled in
// #onDropCompleted.
@@ -1462,37 +1452,66 @@
}
}
- @Override
- public void onAdd(ItemInfo item, int rank) {
- FolderGridOrganizer verifier = createFolderGridOrganizer(
- mActivityContext.getDeviceProfile()).setFolderInfo(mInfo);
- verifier.updateRankAndPos(item, rank);
- mLauncherDelegate.getModelWriter().addOrMoveItemInDatabase(item, mInfo.id, 0, item.cellX,
- item.cellY);
- updateItemLocationsInDatabaseBatch(false);
-
- if (mContent.areViewsBound()) {
- mContent.createAndAddViewForRank(item, rank);
- }
- mItemsInvalidated = true;
+ /** Add an app or shortcut */
+ public void addFolderContent(ItemInfo item) {
+ addFolderContent(item, mInfo.getContents().size(), true);
}
- @Override
- public void onRemove(List<ItemInfo> items) {
- mItemsInvalidated = true;
- items.stream().map(this::getViewForInfo).forEach(mContent::removeItem);
- if (mState == STATE_ANIMATING) {
- mRearrangeOnClose = true;
- } else {
- rearrangeChildren();
+ /** Add an app or shortcut for a specified rank */
+ public void addFolderContent(ItemInfo item, int rank, boolean animate) {
+ if (!willAcceptItemType(item.itemType)) {
+ throw new RuntimeException("tried to add an illegal type into a folder");
}
- if (getItemCount() <= 1) {
- if (mIsOpen) {
- close(true);
- } else {
- replaceFolderWithFinalItem();
+
+ rank = Utilities.boundToRange(rank, 0, mInfo.getContents().size());
+ mInfo.getContents().add(rank, item);
+
+ if (!mSuppressContentUpdate) {
+ FolderGridOrganizer verifier = createFolderGridOrganizer(
+ mActivityContext.getDeviceProfile()).setFolderInfo(mInfo);
+ verifier.updateRankAndPos(item, rank);
+ mLauncherDelegate.getModelWriter().addOrMoveItemInDatabase(item, mInfo.id, 0,
+ item.cellX,
+ item.cellY);
+ updateItemLocationsInDatabaseBatch(false);
+
+ if (mContent.areViewsBound()) {
+ mContent.createAndAddViewForRank(item, rank);
}
+ mItemsInvalidated = true;
+ updateTextViewFocus();
}
+
+ mLauncherDelegate.getModelWriter().notifyItemModified(mInfo);
+ mFolderIcon.onItemsChanged(animate);
+ }
+
+ /** Remove all matching app or shortcut. Does not change the DB. */
+ public void removeFolderContent(boolean animate, ItemInfo... items) {
+ List<ItemInfo> itemArray = Arrays.asList(items);
+ if (mInfo.getContents().removeAll(itemArray)) {
+ mLauncherDelegate.getModelWriter().notifyItemModified(mInfo);
+ }
+
+ if (!mSuppressContentUpdate) {
+ mItemsInvalidated = true;
+ itemArray.forEach(item -> mContent.removeItem(getViewForInfo(item)));
+ if (mState == STATE_ANIMATING) {
+ mRearrangeOnClose = true;
+ } else {
+ rearrangeChildren();
+ }
+ if (getItemCount() <= 1) {
+ if (mIsOpen) {
+ close(true);
+ } else {
+ replaceFolderWithFinalItem();
+ }
+ }
+ updateTextViewFocus();
+ }
+
+ mFolderIcon.onItemsChanged(animate);
}
@VisibleForTesting
@@ -1500,21 +1519,13 @@
return mContent.iterateOverItems((info, view) -> info == item);
}
- @Override
- public void onItemsChanged(boolean animate) {
- updateTextViewFocus();
- }
-
- @Override
- public void onTitleChanged(CharSequence title) {
- mFolderName.setText(title);
- }
-
/**
* Utility methods to iterate over items of the view
*/
- public void iterateOverItems(ItemOperator op) {
- mContent.iterateOverItems(op);
+ @Override
+ @Nullable
+ public View mapOverItems(@NonNull ItemOperator op) {
+ return mContent.iterateOverItems(op);
}
/**
@@ -1661,18 +1672,14 @@
}
};
- /**
- * Temporary resource held while we don't want to handle info changes
- */
- private class SuppressInfoChanges implements AutoCloseable {
-
- SuppressInfoChanges() {
- mInfo.removeListener(Folder.this);
- }
-
- @Override
- public void close() {
- mInfo.addListener(Folder.this);
+ /** Executes the task while suppressing the content update for the folder */
+ private void executeWithContentUpdateSuppressed(Runnable task) {
+ if (mSuppressContentUpdate) {
+ task.run();
+ } else {
+ mSuppressContentUpdate = true;
+ task.run();
+ mSuppressContentUpdate = false;
updateTextViewFocus();
}
}
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 0ed8787..1cd9bcc 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -23,6 +23,7 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_AUTO_LABELED;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_AUTO_LABELING_SKIPPED_EMPTY_PRIMARY;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_AUTO_LABELING_SKIPPED_EMPTY_SUGGESTIONS;
+import static com.android.launcher3.model.data.FolderInfo.willAcceptItemType;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -73,7 +74,6 @@
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.data.AppPairInfo;
import com.android.launcher3.model.data.FolderInfo;
-import com.android.launcher3.model.data.FolderInfo.FolderListener;
import com.android.launcher3.model.data.FolderInfo.LabelState;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemFactory;
@@ -92,7 +92,7 @@
/**
* An icon that can appear on in the workspace representing an {@link Folder}.
*/
-public class FolderIcon extends FrameLayout implements FolderListener, FloatingIconViewCompanion,
+public class FolderIcon extends FrameLayout implements FloatingIconViewCompanion,
DraggableView, Reorderable {
private final MultiTranslateDelegate mTranslateDelegate = new MultiTranslateDelegate(this);
@@ -127,7 +127,7 @@
private boolean mForceHideDot;
@ViewDebug.ExportedProperty(category = "launcher", deepExport = true)
- private FolderDotInfo mDotInfo = new FolderDotInfo();
+ private final FolderDotInfo mDotInfo = new FolderDotInfo();
private DotRenderer mDotRenderer;
@ViewDebug.ExportedProperty(category = "launcher", deepExport = true)
private DotRenderer.DrawParams mDotParams;
@@ -178,7 +178,6 @@
folder.bind(folderInfo);
icon.setFolder(folder);
- folderInfo.addListener(icon);
return icon;
}
@@ -217,13 +216,7 @@
icon.mDotRenderer = grid.mDotRendererWorkSpace;
icon.setContentDescription(icon.getAccessiblityTitle(folderInfo.title));
-
- // Keep the notification dot up to date with the sum of all the content's dots.
- FolderDotInfo folderDotInfo = new FolderDotInfo();
- for (ItemInfo si : folderInfo.getContents()) {
- folderDotInfo.addDotInfo(activity.getDotInfoForItem(si));
- }
- icon.setDotInfo(folderDotInfo);
+ icon.updateDotInfo();
icon.setAccessibilityDelegate(activity.getAccessibilityDelegate());
@@ -264,22 +257,13 @@
}
private boolean willAcceptItem(ItemInfo item) {
- final int itemType = item.itemType;
- return (Folder.willAcceptItemType(itemType) && item != mInfo && !mFolder.isOpen());
+ return (willAcceptItemType(item.itemType) && item != mInfo && !mFolder.isOpen());
}
public boolean acceptDrop(ItemInfo dragInfo) {
return !mFolder.isDestroyed() && willAcceptItem(dragInfo);
}
- public void addItem(ItemInfo item) {
- mInfo.add(item, true);
- }
-
- public void removeItem(ItemInfo item, boolean animate) {
- mInfo.remove(item, animate);
- }
-
public void onDragEnter(ItemInfo dragInfo) {
if (mFolder.isDestroyed() || !willAcceptItem(dragInfo)) return;
CellLayoutLayoutParams lp = (CellLayoutLayoutParams) getLayoutParams();
@@ -308,9 +292,8 @@
public void performCreateAnimation(final ItemInfo destInfo, final View destView,
final ItemInfo srcInfo, final DragObject d, Rect dstRect,
float scaleRelativeToDragLayer) {
- final DragView srcView = d.dragView;
prepareCreateAnimation(destView);
- addItem(destInfo);
+ getFolder().addFolderContent(destInfo);
// This will animate the first item from it's position as an icon into its
// position as the first item in the preview
mPreviewItemManager.createFirstItemAnimation(false /* reverse */, null)
@@ -364,7 +347,7 @@
boolean itemAdded = false;
if (itemReturnedOnFailedDrop || index >= MAX_NUM_ITEMS_IN_PREVIEW) {
List<ItemInfo> oldPreviewItems = new ArrayList<>(mCurrentPreviewItems);
- mInfo.add(item, index, false);
+ getFolder().addFolderContent(item, index, false);
mCurrentPreviewItems.clear();
mCurrentPreviewItems.addAll(getPreviewItemsOnPage(0));
@@ -380,12 +363,12 @@
mPreviewItemManager.onDrop(oldPreviewItems, mCurrentPreviewItems, item);
itemAdded = true;
} else {
- removeItem(item, false);
+ getFolder().removeFolderContent(false, item);
}
}
if (!itemAdded) {
- mInfo.add(item, index, true);
+ getFolder().addFolderContent(item, index, true);
}
int[] center = new int[2];
@@ -431,7 +414,7 @@
}, DROP_IN_ANIMATION_DURATION);
});
} else {
- addItem(item);
+ getFolder().addFolderContent(item);
}
}
@@ -500,9 +483,23 @@
);
}
- public void setDotInfo(FolderDotInfo dotInfo) {
- updateDotScale(mDotInfo.hasDot(), dotInfo.hasDot());
- mDotInfo = dotInfo;
+ /** Keep the notification dot up to date with the sum of all the content's dots. */
+ public void updateDotInfo() {
+ boolean hadDot = mDotInfo.hasDot();
+ mDotInfo.reset();
+ for (ItemInfo si : mInfo.getContents()) {
+ mDotInfo.addDotInfo(mActivity.getDotInfoForItem(si));
+ }
+ boolean isDotted = mDotInfo.hasDot();
+ float newDotScale = isDotted ? 1f : 0f;
+ // Animate when a dot is first added or when it is removed.
+ if ((hadDot ^ isDotted) && isShown()) {
+ animateDotScale(newDotScale);
+ } else {
+ cancelDotScaleAnim();
+ mDotScale = newDotScale;
+ invalidate();
+ }
}
public ClippedFolderIconLayoutRule getLayoutRule() {
@@ -523,22 +520,6 @@
}
}
- /**
- * Sets mDotScale to 1 or 0, animating if wasDotted or isDotted is false
- * (the dot is being added or removed).
- */
- private void updateDotScale(boolean wasDotted, boolean isDotted) {
- float newDotScale = isDotted ? 1f : 0f;
- // Animate when a dot is first added or when it is removed.
- if ((wasDotted ^ isDotted) && isShown()) {
- animateDotScale(newDotScale);
- } else {
- cancelDotScaleAnim();
- mDotScale = newDotScale;
- invalidate();
- }
- }
-
private void cancelDotScaleAnim() {
if (mDotScaleAnim != null) {
mDotScaleAnim.cancel();
@@ -682,13 +663,6 @@
return mPreviewItemManager.verifyDrawable(who) || super.verifyDrawable(who);
}
- @Override
- public void onItemsChanged(boolean animate) {
- updatePreviewItems(animate);
- invalidate();
- requestLayout();
- }
-
private void updatePreviewItems(boolean animate) {
mPreviewItemManager.updatePreviewItems(animate);
mCurrentPreviewItems.clear();
@@ -702,31 +676,15 @@
mPreviewItemManager.updatePreviewItems(itemCheck);
}
- @Override
- public void onAdd(ItemInfo item, int rank) {
+ public void onItemsChanged(boolean animate) {
updatePreviewItems(false);
- boolean wasDotted = mDotInfo.hasDot();
- mDotInfo.addDotInfo(mActivity.getDotInfoForItem(item));
- boolean isDotted = mDotInfo.hasDot();
- updateDotScale(wasDotted, isDotted);
+ updateDotInfo();
setContentDescription(getAccessiblityTitle(mInfo.title));
+ updatePreviewItems(animate);
invalidate();
requestLayout();
}
- @Override
- public void onRemove(List<ItemInfo> items) {
- updatePreviewItems(false);
- boolean wasDotted = mDotInfo.hasDot();
- items.stream().map(mActivity::getDotInfoForItem).forEach(mDotInfo::subtractDotInfo);
- boolean isDotted = mDotInfo.hasDot();
- updateDotScale(wasDotted, isDotted);
- setContentDescription(getAccessiblityTitle(mInfo.title));
- invalidate();
- requestLayout();
- }
-
- @Override
public void onTitleChanged(CharSequence title) {
mFolderName.setText(title);
setContentDescription(getAccessiblityTitle(title));
@@ -762,11 +720,6 @@
mLongPressHelper.cancelLongPress();
}
- public void removeListeners() {
- mInfo.removeListener(this);
- mInfo.removeListener(mFolder);
- }
-
private boolean isInHotseat() {
return mInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT;
}
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index bebe1a4..0963421 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -47,6 +47,7 @@
import com.android.launcher3.model.data.AppPairInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.pageindicators.Direction;
import com.android.launcher3.pageindicators.PageIndicatorDots;
import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator;
import com.android.launcher3.util.Thunk;
@@ -129,6 +130,8 @@
public void setFolder(Folder folder) {
mFolder = folder;
mPageIndicator = folder.findViewById(R.id.folder_page_indicator);
+ mPageIndicator.setArrowClickListener(direction -> snapToPageImmediately(
+ (Direction.END == direction) ? mCurrentPage + 1 : mCurrentPage - 1));
initParentViews(folder);
}
diff --git a/src/com/android/launcher3/graphics/GridCustomizationsProxy.java b/src/com/android/launcher3/graphics/GridCustomizationsProxy.java
index 70b9f46..48519ce 100644
--- a/src/com/android/launcher3/graphics/GridCustomizationsProxy.java
+++ b/src/com/android/launcher3/graphics/GridCustomizationsProxy.java
@@ -113,6 +113,7 @@
private static final String KEY_SHAPE_OPTIONS = "/shape_options";
// default_grid is for setting grid and shape to system settings
private static final String KEY_DEFAULT_GRID = "/default_grid";
+ private static final String SET_SHAPE = "/shape";
private static final String METHOD_GET_PREVIEW = "get_preview";
@@ -130,6 +131,7 @@
private static final int MESSAGE_ID_UPDATE_SHAPE = 2586;
private static final int MESSAGE_ID_UPDATE_GRID = 7414;
private static final int MESSAGE_ID_UPDATE_COLOR = 856;
+ private static final int MESSAGE_ID_UPDATE_ICON_THEMED = 311;
// Set of all active previews used to track duplicate memory allocations
private final Set<PreviewLifecycleObserver> mActivePreviews =
@@ -264,6 +266,12 @@
mContext.getContentResolver().notifyChange(uri, null);
return 1;
}
+ case SET_SHAPE:
+ if (Flags.newCustomizationPickerUi()) {
+ mPrefs.put(PREF_ICON_SHAPE,
+ requireNonNullElse(values.getAsString(KEY_SHAPE_KEY), ""));
+ }
+ return 1;
case ICON_THEMED:
case SET_ICON_THEMED: {
mThemeManager.setMonoThemeEnabled(values.getAsBoolean(BOOLEAN_VALUE));
@@ -384,6 +392,12 @@
renderer.previewColor(message.getData());
}
break;
+ case MESSAGE_ID_UPDATE_ICON_THEMED:
+ if (Flags.newCustomizationPickerUi()) {
+ Boolean iconThemed = message.getData().getBoolean(BOOLEAN_VALUE);
+ // TODO Update icon themed in the preview
+ }
+ break;
default:
// Unknown command, destroy lifecycle
Log.d(TAG, "Unknown preview command: " + message.what + ", destroying preview");
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index 3836f7d..e4e4b90 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -38,7 +38,6 @@
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
-import android.content.ContextWrapper;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.PointF;
@@ -64,7 +63,6 @@
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.Hotseat;
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.InvariantDeviceProfile;
@@ -82,19 +80,23 @@
import com.android.launcher3.dagger.LauncherAppModule;
import com.android.launcher3.dagger.LauncherAppSingleton;
import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.model.BaseLauncherBinder.BaseLauncherBinderFactory;
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
+import com.android.launcher3.model.LoaderTask.LoaderTaskFactory;
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;
+import com.android.launcher3.util.BaseContext;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.SandboxContext;
+import com.android.launcher3.util.Themes;
import com.android.launcher3.util.WindowBounds;
import com.android.launcher3.util.window.WindowManagerProxy;
import com.android.launcher3.views.ActivityContext;
@@ -109,7 +111,6 @@
import dagger.BindsInstance;
import dagger.Component;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -127,7 +128,7 @@
* 3) Place appropriate elements like icons and first-page qsb
* 4) Measure and draw the view on a canvas
*/
-public class LauncherPreviewRenderer extends ContextWrapper
+public class LauncherPreviewRenderer extends BaseContext
implements ActivityContext, WorkspaceLayoutManager, LayoutInflater.Factory2 {
/**
@@ -156,7 +157,6 @@
}
}
- private final List<OnDeviceProfileChangeListener> mDpChangeListeners = new ArrayList<>();
private final Handler mUiHandler;
private final Context mContext;
private final InvariantDeviceProfile mIdp;
@@ -184,7 +184,7 @@
WallpaperColors wallpaperColorsOverride,
@Nullable final SparseArray<Size> launcherWidgetSpanInfo) {
- super(context);
+ super(context, Themes.getActivityThemeRes(context));
mUiHandler = new Handler(Looper.getMainLooper());
mContext = context;
mIdp = idp;
@@ -263,6 +263,13 @@
: null;
}
mAppWidgetHost = new LauncherPreviewAppWidgetHost(context);
+
+ onViewCreated();
+ }
+
+ @Override
+ public InsettableFrameLayout getRootView() {
+ return mRootView;
}
/**
@@ -349,11 +356,6 @@
}
@Override
- public List<OnDeviceProfileChangeListener> getOnDeviceProfileChangeListeners() {
- return mDpChangeListeners;
- }
-
- @Override
public Hotseat getHotseat() {
return mHotseat;
}
@@ -605,6 +607,10 @@
@Component(modules = LauncherAppModule.class)
public interface PreviewAppComponent extends LauncherAppComponent {
+ LoaderTaskFactory getLoaderTaskFactory();
+ BaseLauncherBinderFactory getBaseLauncherBinderFactory();
+ BgDataModel getDataModel();
+
/** Builder for NexusLauncherAppComponent. */
@Component.Builder
interface Builder extends LauncherAppComponent.Builder {
diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
index 3641896..5a9b9c2 100644
--- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
+++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
@@ -58,19 +58,21 @@
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.Workspace;
+import com.android.launcher3.dagger.LauncherComponentProvider;
+import com.android.launcher3.graphics.LauncherPreviewRenderer.PreviewAppComponent;
import com.android.launcher3.graphics.LauncherPreviewRenderer.PreviewContext;
-import com.android.launcher3.model.BaseLauncherBinder;
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.LoaderTask;
import com.android.launcher3.model.ModelDbController;
+import com.android.launcher3.model.UserManagerState;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.Themes;
import com.android.launcher3.widget.LocalColorExtractor;
import com.android.systemui.shared.Flags;
-import java.util.ArrayList;
+import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@@ -338,40 +340,32 @@
// Start the migration
PreviewContext previewContext =
new PreviewContext(inflationContext, mGridName, mShapeKey);
+ PreviewAppComponent appComponent =
+ (PreviewAppComponent) LauncherComponentProvider.get(previewContext);
- BgDataModel bgModel = new BgDataModel();
- new LoaderTask(
- LauncherAppState.getInstance(previewContext),
- /* bgAllAppsList= */ null,
- bgModel,
- LauncherAppState.getInstance(previewContext).getModel().getModelDelegate(),
- new BaseLauncherBinder(LauncherAppState.getInstance(previewContext), bgModel,
- /* bgAllAppsList= */ null, new Callbacks[0]),
- LauncherAppState.getInstance(
- previewContext).getModel().getWidgetsFilterDataProvider()) {
+ LoaderTask task = appComponent.getLoaderTaskFactory().newLoaderTask(
+ appComponent.getBaseLauncherBinderFactory().createBinder(new Callbacks[0]),
+ new UserManagerState());
- @Override
- public void run() {
- InvariantDeviceProfile idp = LauncherAppState.getIDP(previewContext);
- DeviceProfile deviceProfile = idp.getDeviceProfile(previewContext);
- String query =
- LauncherSettings.Favorites.SCREEN + " = " + Workspace.FIRST_SCREEN_ID
- + " or " + LauncherSettings.Favorites.CONTAINER + " = "
- + LauncherSettings.Favorites.CONTAINER_HOTSEAT;
- if (deviceProfile.isTwoPanels) {
- query += " or " + LauncherSettings.Favorites.SCREEN + " = "
- + Workspace.SECOND_SCREEN_ID;
- }
- loadWorkspace(new ArrayList<>(), query, null, null);
+ InvariantDeviceProfile idp = appComponent.getIDP();
+ DeviceProfile deviceProfile = idp.getDeviceProfile(previewContext);
+ String query =
+ LauncherSettings.Favorites.SCREEN + " = " + Workspace.FIRST_SCREEN_ID
+ + " or " + LauncherSettings.Favorites.CONTAINER + " = "
+ + LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+ if (deviceProfile.isTwoPanels) {
+ query += " or " + LauncherSettings.Favorites.SCREEN + " = "
+ + Workspace.SECOND_SCREEN_ID;
+ }
- final SparseArray<Size> spanInfo = getLoadedLauncherWidgetInfo();
- MAIN_EXECUTOR.execute(() -> {
- renderView(previewContext, mBgDataModel, mWidgetProvidersMap, spanInfo,
- idp);
- mLifeCycleTracker.add(previewContext::onDestroy);
- });
- }
- }.run();
+ Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap = new HashMap<>();
+ task.loadWorkspaceForPreview(query, widgetProviderInfoMap);
+ final SparseArray<Size> spanInfo = getLoadedLauncherWidgetInfo();
+ MAIN_EXECUTOR.execute(() -> {
+ renderView(previewContext, appComponent.getDataModel(), widgetProviderInfoMap,
+ spanInfo, idp);
+ mLifeCycleTracker.add(previewContext::onDestroy);
+ });
} else {
LauncherAppState.getInstance(inflationContext).getModel().loadAsync(dataModel -> {
if (dataModel != null) {
@@ -422,7 +416,6 @@
view.setTranslationY((mHeight - scale * view.getHeight()) / 2);
}
-
if (!Flags.newCustomizationPickerUi()) {
view.setAlpha(mSkipAnimations ? 1 : 0);
view.animate().alpha(1)
@@ -479,5 +472,4 @@
MAIN_EXECUTOR.execute(mLifecycleTracker::executeAllAndDestroy);
}
}
-
}
diff --git a/src/com/android/launcher3/graphics/ShapeDelegate.kt b/src/com/android/launcher3/graphics/ShapeDelegate.kt
index 9033eac..01bfe30 100644
--- a/src/com/android/launcher3/graphics/ShapeDelegate.kt
+++ b/src/com/android/launcher3/graphics/ShapeDelegate.kt
@@ -18,6 +18,7 @@
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
+import android.animation.ValueAnimator.AnimatorUpdateListener
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Matrix
@@ -41,7 +42,6 @@
import androidx.graphics.shapes.rectangle
import androidx.graphics.shapes.toPath
import androidx.graphics.shapes.transformed
-import com.android.launcher3.anim.RoundedRectRevealOutlineProvider
import com.android.launcher3.icons.GraphicsUtils
import com.android.launcher3.views.ClipPathView
@@ -127,16 +127,20 @@
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)
+ val startRadius = (startRect.width() / 2f) * radiusRatio
+ return ClipAnimBuilder(target) { progress, path ->
+ val radius = (1 - progress) * startRadius + progress * endRadius
+ path.addRoundRect(
+ (1 - progress) * startRect.left + progress * endRect.left,
+ (1 - progress) * startRect.top + progress * endRect.top,
+ (1 - progress) * startRect.right + progress * endRect.right,
+ (1 - progress) * startRect.bottom + progress * endRect.bottom,
+ radius,
+ radius,
+ Path.Direction.CW,
+ )
+ }
+ .toAnim(isReversed)
}
override fun equals(other: Any?) =
@@ -203,7 +207,11 @@
start =
poly.transformed(
Matrix().apply {
- setRectToRect(RectF(0f, 0f, 100f, 100f), RectF(startRect), FILL)
+ setRectToRect(
+ RectF(0f, 0f, DEFAULT_PATH_SIZE, DEFAULT_PATH_SIZE),
+ RectF(startRect),
+ FILL,
+ )
}
),
end =
@@ -216,40 +224,46 @@
),
)
- val va =
- if (isReversed) ValueAnimator.ofFloat(1f, 0f) else ValueAnimator.ofFloat(0f, 1f)
- va.addListener(
- object : AnimatorListenerAdapter() {
- private var oldOutlineProvider: ViewOutlineProvider? = null
-
- override fun onAnimationStart(animation: Animator) {
- target.apply {
- oldOutlineProvider = outlineProvider
- outlineProvider = null
- translationZ = -target.elevation
- }
- }
-
- override fun onAnimationEnd(animation: Animator) {
- target.apply {
- translationZ = 0f
- setClipPath(null)
- outlineProvider = oldOutlineProvider
- }
- }
- }
- )
-
- val path = Path()
- va.addUpdateListener { anim: ValueAnimator ->
- path.reset()
- morph.toPath(anim.animatedValue as Float, path)
- target.setClipPath(path)
- }
- return va
+ return ClipAnimBuilder(target, morph::toPath).toAnim(isReversed)
}
}
+ private class ClipAnimBuilder<T>(val target: T, val pathProvider: (Float, Path) -> Unit) :
+ AnimatorListenerAdapter(), AnimatorUpdateListener where T : View, T : ClipPathView {
+
+ private var oldOutlineProvider: ViewOutlineProvider? = null
+ val path = Path()
+
+ override fun onAnimationStart(animation: Animator) {
+ target.apply {
+ oldOutlineProvider = outlineProvider
+ outlineProvider = null
+ translationZ = -target.elevation
+ }
+ }
+
+ override fun onAnimationEnd(animation: Animator) {
+ target.apply {
+ translationZ = 0f
+ setClipPath(null)
+ outlineProvider = oldOutlineProvider
+ }
+ }
+
+ override fun onAnimationUpdate(anim: ValueAnimator) {
+ path.reset()
+ pathProvider.invoke(anim.animatedValue as Float, path)
+ target.setClipPath(path)
+ }
+
+ fun toAnim(isReversed: Boolean) =
+ (if (isReversed) ValueAnimator.ofFloat(1f, 0f) else ValueAnimator.ofFloat(0f, 1f))
+ .also {
+ it.addListener(this)
+ it.addUpdateListener(this)
+ }
+ }
+
companion object {
const val TAG = "IconShape"
@@ -281,7 +295,10 @@
PathParser.createPathFromPathData(shapeStr).apply {
transform(
Matrix().apply {
- setScale(AREA_CALC_SIZE / 100f, AREA_CALC_SIZE / 100f)
+ setScale(
+ AREA_CALC_SIZE / DEFAULT_PATH_SIZE,
+ AREA_CALC_SIZE / DEFAULT_PATH_SIZE,
+ )
}
)
}
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 2f1af68..4715132 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -166,6 +166,9 @@
@UiEvent(doc = "User tapped or long pressed on settings icon inside launcher settings.")
LAUNCHER_SETTINGS_BUTTON_TAP_OR_LONGPRESS(463),
+ @UiEvent(doc = "User tapped or long pressed on apps icon inside launcher settings.")
+ LAUNCHER_ALL_APPS_TAP_OR_LONGPRESS(2204),
+
@UiEvent(doc = "User tapped or long pressed on widget tray icon inside launcher settings.")
LAUNCHER_WIDGETSTRAY_BUTTON_TAP_OR_LONGPRESS(464),
@@ -433,20 +436,29 @@
@UiEvent(doc = "Notification dismissed by swiping right.")
LAUNCHER_NOTIFICATION_DISMISSED(652),
- @UiEvent(doc = "Current grid size is changed to 6.")
- LAUNCHER_GRID_SIZE_6(930),
+ @UiEvent(doc = "Current grid size is changed to 2x2")
+ LAUNCHER_GRID_SIZE_2_BY_2(2181),
- @UiEvent(doc = "Current grid size is changed to 5.")
- LAUNCHER_GRID_SIZE_5(662),
+ @UiEvent(doc = "Current grid size is changed to 3x3")
+ LAUNCHER_GRID_SIZE_3_BY_3(2182),
- @UiEvent(doc = "Current grid size is changed to 4.")
- LAUNCHER_GRID_SIZE_4(663),
+ @UiEvent(doc = "Current grid size is changed to 4x4")
+ LAUNCHER_GRID_SIZE_4_BY_4(2183),
- @UiEvent(doc = "Current grid size is changed to 3.")
- LAUNCHER_GRID_SIZE_3(664),
+ @UiEvent(doc = "Current grid size is changed to 4x5")
+ LAUNCHER_GRID_SIZE_4_BY_5(2184),
- @UiEvent(doc = "Current grid size is changed to 2.")
- LAUNCHER_GRID_SIZE_2(665),
+ @UiEvent(doc = "Current grid size is changed to 4x6")
+ LAUNCHER_GRID_SIZE_4_BY_6(2185),
+
+ @UiEvent(doc = "Current grid size is changed to 5x5")
+ LAUNCHER_GRID_SIZE_5_BY_5(2186),
+
+ @UiEvent(doc = "Current grid size is changed to 5x6")
+ LAUNCHER_GRID_SIZE_5_BY_6(2187),
+
+ @UiEvent(doc = "Current grid size is changed to 6x5")
+ LAUNCHER_GRID_SIZE_6_BY_5(2188),
@UiEvent(doc = "Launcher entered into AllApps state.")
LAUNCHER_ALLAPPS_ENTRY(692),
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index ddbbdc7..dfba4bb 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -66,13 +66,6 @@
/**
* @param itemList items to add on the workspace
- */
- public AddWorkspaceItemsTask(@NonNull final List<Pair<ItemInfo, Object>> itemList) {
- this(itemList, new WorkspaceItemSpaceFinder());
- }
-
- /**
- * @param itemList items to add on the workspace
* @param itemSpaceFinder inject WorkspaceItemSpaceFinder dependency for testing
*/
public AddWorkspaceItemsTask(@NonNull final List<Pair<ItemInfo, Object>> itemList,
@@ -91,7 +84,7 @@
final ArrayList<ItemInfo> addedItemsFinal = new ArrayList<>();
final IntArray addedWorkspaceScreensFinal = new IntArray();
- final Context context = taskController.getApp().getContext();
+ final Context context = taskController.getContext();
synchronized (dataModel) {
IntArray workspaceScreens = dataModel.collectWorkspaceScreens();
@@ -133,7 +126,7 @@
for (ItemInfo item : filteredItems) {
// Find appropriate space for the item.
- int[] coords = mItemSpaceFinder.findSpaceForItem(taskController.getApp(), dataModel,
+ int[] coords = mItemSpaceFinder.findSpaceForItem(
workspaceScreens, addedWorkspaceScreensFinal, item.spanX, item.spanY);
int screenId = coords[0];
@@ -192,7 +185,7 @@
continue;
}
- IconCache cache = taskController.getApp().getIconCache();
+ IconCache cache = taskController.getIconCache();
WorkspaceItemInfo wii = (WorkspaceItemInfo) itemInfo;
wii.title = "";
wii.bitmap = cache.getDefaultIcon(item.user);
diff --git a/src/com/android/launcher3/model/AllAppsList.java b/src/com/android/launcher3/model/AllAppsList.java
index 98f9afd..2311239 100644
--- a/src/com/android/launcher3/model/AllAppsList.java
+++ b/src/com/android/launcher3/model/AllAppsList.java
@@ -34,6 +34,7 @@
import com.android.launcher3.AppFilter;
import com.android.launcher3.compat.AlphabeticIndexCompat;
+import com.android.launcher3.dagger.LauncherAppSingleton;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.data.AppInfo;
@@ -53,11 +54,13 @@
import java.util.function.Consumer;
import java.util.function.Predicate;
+import javax.inject.Inject;
+
/**
* Stores the list of all applications for the all apps view.
*/
-@SuppressWarnings("NewApi")
+@LauncherAppSingleton
public class AllAppsList {
private static final String TAG = "AllAppsList";
@@ -70,10 +73,10 @@
public final ArrayList<AppInfo> data = new ArrayList<>(DEFAULT_APPLICATIONS_NUMBER);
@NonNull
- private IconCache mIconCache;
+ private final IconCache mIconCache;
@NonNull
- private AppFilter mAppFilter;
+ private final AppFilter mAppFilter;
private boolean mDataChanged = false;
private Consumer<AppInfo> mRemoveListener = NO_OP_CONSUMER;
@@ -92,6 +95,7 @@
/**
* Boring constructor.
*/
+ @Inject
public AllAppsList(IconCache iconCache, AppFilter appFilter) {
mIconCache = iconCache;
mAppFilter = appFilter;
diff --git a/src/com/android/launcher3/model/BaseLauncherBinder.java b/src/com/android/launcher3/model/BaseLauncherBinder.java
index a2ca6b6..c4bbae4 100644
--- a/src/com/android/launcher3/model/BaseLauncherBinder.java
+++ b/src/com/android/launcher3/model/BaseLauncherBinder.java
@@ -26,8 +26,7 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-import static java.util.Collections.emptyList;
-
+import android.content.Context;
import android.os.Trace;
import android.util.Log;
import android.util.Pair;
@@ -36,16 +35,16 @@
import androidx.annotation.NonNull;
import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherModel.CallbackTask;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.celllayout.CellPosMapper;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dagger.ApplicationContext;
import com.android.launcher3.model.BgDataModel.Callbacks;
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.PackageItemInfo;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSet;
@@ -58,6 +57,10 @@
import com.android.launcher3.widget.model.WidgetsListBaseEntriesBuilder;
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -79,7 +82,9 @@
protected final LooperExecutor mUiExecutor;
- protected final LauncherAppState mApp;
+ private final Context mContext;
+ private final InvariantDeviceProfile mIDP;
+ private final LauncherModel mModel;
protected final BgDataModel mBgDataModel;
private final AllAppsList mBgAllAppsList;
@@ -87,10 +92,18 @@
private int mMyBindingId;
- public BaseLauncherBinder(LauncherAppState app, BgDataModel dataModel,
- AllAppsList allAppsList, Callbacks[] callbacksList) {
+ @AssistedInject
+ public BaseLauncherBinder(
+ @ApplicationContext Context context,
+ InvariantDeviceProfile idp,
+ LauncherModel model,
+ BgDataModel dataModel,
+ AllAppsList allAppsList,
+ @Assisted Callbacks[] callbacksList) {
mUiExecutor = MAIN_EXECUTOR;
- mApp = app;
+ mContext = context;
+ mIDP = idp;
+ mModel = model;
mBgDataModel = dataModel;
mBgAllAppsList = allAppsList;
mCallbacksList = callbacksList;
@@ -113,15 +126,14 @@
mBgDataModel.extraItems.forEach(extraItems::add);
if (incrementBindId) {
mBgDataModel.lastBindId++;
- mBgDataModel.lastLoadId = mApp.getModel().getLastLoadId();
+ mBgDataModel.lastLoadId = mModel.getLastLoadId();
}
mMyBindingId = mBgDataModel.lastBindId;
workspaceItemCount = mBgDataModel.itemsIdMap.size();
}
for (Callbacks cb : mCallbacksList) {
- new UnifiedWorkspaceBinder(cb, mUiExecutor, mApp, mBgDataModel, mMyBindingId,
- itemsIdMap, extraItems, orderedScreenIds)
+ new UnifiedWorkspaceBinder(cb, itemsIdMap, extraItems, orderedScreenIds)
.bind(isBindSync, workspaceItemCount);
}
} finally {
@@ -165,17 +177,9 @@
if (!WIDGETS_ENABLED) {
return;
}
- Map<PackageItemInfo, List<WidgetItem>>
- widgetsByPackageItem = mBgDataModel.widgetsModel.getWidgetsByPackageItemForPicker();
- List<WidgetsListBaseEntry> widgets = new WidgetsListBaseEntriesBuilder(mApp.getContext())
- .build(widgetsByPackageItem);
- Predicate<WidgetItem> filter = mBgDataModel.widgetsModel.getDefaultWidgetsFilter();
- List<WidgetsListBaseEntry> defaultWidgets =
- filter != null ? new WidgetsListBaseEntriesBuilder(
- mApp.getContext()).build(widgetsByPackageItem,
- mBgDataModel.widgetsModel.getDefaultWidgetsFilter()) : emptyList();
-
- executeCallbacksTask(c -> c.bindAllWidgets(widgets, defaultWidgets), mUiExecutor);
+ List<WidgetsListBaseEntry> widgets = new WidgetsListBaseEntriesBuilder(mContext)
+ .build(mBgDataModel.widgetsModel.getWidgetsByPackageItemForPicker());
+ executeCallbacksTask(c -> c.bindAllWidgets(widgets), mUiExecutor);
}
/**
@@ -191,10 +195,9 @@
/**
* Sorts the set of items by hotseat, workspace (spatially from top to bottom, left to right)
*/
- protected void sortWorkspaceItemsSpatially(InvariantDeviceProfile profile,
- ArrayList<ItemInfo> workspaceItems) {
- final int screenCols = profile.numColumns;
- final int screenCellCount = profile.numColumns * profile.numRows;
+ protected void sortWorkspaceItemsSpatially(ArrayList<ItemInfo> workspaceItems) {
+ final int screenCols = mIDP.numColumns;
+ final int screenCellCount = mIDP.numColumns * mIDP.numRows;
Collections.sort(workspaceItems, (lhs, rhs) -> {
if (lhs.container == rhs.container) {
// Within containers, order by their spatial position in that container
@@ -250,30 +253,18 @@
private class UnifiedWorkspaceBinder {
- private final Executor mUiExecutor;
private final Callbacks mCallbacks;
- private final LauncherAppState mApp;
- private final BgDataModel mBgDataModel;
-
- private final int mMyBindingId;
private final IntSparseArrayMap<ItemInfo> mItemIdMap;
private final IntArray mOrderedScreenIds;
private final ArrayList<FixedContainerItems> mExtraItems;
- UnifiedWorkspaceBinder(Callbacks callbacks,
- Executor uiExecutor,
- LauncherAppState app,
- BgDataModel bgDataModel,
- int myBindingId,
+ UnifiedWorkspaceBinder(
+ Callbacks callbacks,
IntSparseArrayMap<ItemInfo> itemIdMap,
ArrayList<FixedContainerItems> extraItems,
IntArray orderedScreenIds) {
mCallbacks = callbacks;
- mUiExecutor = uiExecutor;
- mApp = app;
- mBgDataModel = bgDataModel;
- mMyBindingId = myBindingId;
mItemIdMap = itemIdMap;
mExtraItems = extraItems;
mOrderedScreenIds = orderedScreenIds;
@@ -299,9 +290,8 @@
(WIDGET_FILTER.test(item) ? otherAppWidgets : otherWorkspaceItems).add(item);
}
});
- final InvariantDeviceProfile idp = mApp.getInvariantDeviceProfile();
- sortWorkspaceItemsSpatially(idp, currentWorkspaceItems);
- sortWorkspaceItemsSpatially(idp, otherWorkspaceItems);
+ sortWorkspaceItemsSpatially(currentWorkspaceItems);
+ sortWorkspaceItemsSpatially(otherWorkspaceItems);
// Tell the workspace that we're about to start binding items
executeCallbacksTask(c -> {
@@ -362,7 +352,7 @@
executeCallbacksTask(c -> c.bindStringCache(cacheClone), pendingExecutor);
executeCallbacksTask(c -> c.finishBindingItems(currentScreenIds), pendingExecutor);
- pendingExecutor.execute(() -> ItemInstallQueue.INSTANCE.get(mApp.getContext())
+ pendingExecutor.execute(() -> ItemInstallQueue.INSTANCE.get(mContext)
.resumeModelPush(FLAG_LOADER_RUNNING));
}
@@ -377,8 +367,8 @@
return;
}
- ModelWriter writer = mApp.getModel()
- .getWriter(false /* verifyChanges */, CellPosMapper.DEFAULT, null);
+ ModelWriter writer = mModel.getWriter(
+ false /* verifyChanges */, CellPosMapper.DEFAULT, null);
List<Pair<ItemInfo, View>> bindItems = items.stream()
.map(i -> Pair.create(i, inflater.inflateItem(i, writer, null)))
.collect(Collectors.toList());
@@ -408,4 +398,9 @@
});
}
}
+
+ @AssistedFactory
+ public interface BaseLauncherBinderFactory {
+ BaseLauncherBinder createBinder(Callbacks[] callbacks);
+ }
}
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index eab28b7..ea9f36c 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -46,6 +46,7 @@
import com.android.launcher3.BuildConfig;
import com.android.launcher3.Workspace;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dagger.LauncherAppSingleton;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.CollectionInfo;
@@ -79,9 +80,15 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import javax.inject.Inject;
+
/**
* All the data stored in-memory and managed by the LauncherModel
+ *
+ * All the static data should be accessed on the background thread, A lock should be acquired on
+ * this object when accessing any data from this model.
*/
+@LauncherAppSingleton
public class BgDataModel {
private static final String TAG = "BgDataModel";
@@ -105,7 +112,7 @@
/**
* Entire list of widgets.
*/
- public final WidgetsModel widgetsModel = new WidgetsModel();
+ public final WidgetsModel widgetsModel;
/**
* Cache for strings used in launcher
@@ -124,6 +131,11 @@
public boolean isFirstPagePinnedItemEnabled = QSB_ON_FIRST_SCREEN
&& !enableSmartspaceRemovalToggle();
+ @Inject
+ public BgDataModel(WidgetsModel widgetsModel) {
+ this.widgetsModel = widgetsModel;
+ }
+
/**
* Clears all the data
*/
@@ -431,9 +443,8 @@
/**
* Binds the app widgets to the providers that share widgets with the UI.
*/
- default void bindAllWidgets(@NonNull List<WidgetsListBaseEntry> widgets,
- @NonNull List<WidgetsListBaseEntry> defaultWidgets) {
- }
+ default void bindAllWidgets(@NonNull List<WidgetsListBaseEntry> widgets) { }
+
default void bindSmartspaceWidget() { }
/** Called when workspace has been bound. */
diff --git a/src/com/android/launcher3/model/CacheDataUpdatedTask.java b/src/com/android/launcher3/model/CacheDataUpdatedTask.java
index 48934e2..f740b49 100644
--- a/src/com/android/launcher3/model/CacheDataUpdatedTask.java
+++ b/src/com/android/launcher3/model/CacheDataUpdatedTask.java
@@ -59,7 +59,7 @@
@Override
public void execute(@NonNull ModelTaskController taskController, @NonNull BgDataModel dataModel,
@NonNull AllAppsList apps) {
- IconCache iconCache = taskController.getApp().getIconCache();
+ IconCache iconCache = taskController.getIconCache();
ArrayList<ItemInfo> updatedItems = new ArrayList<>();
synchronized (dataModel) {
diff --git a/src/com/android/launcher3/model/DeviceGridState.java b/src/com/android/launcher3/model/DeviceGridState.java
index d06f541..96ce4c8 100644
--- a/src/com/android/launcher3/model/DeviceGridState.java
+++ b/src/com/android/launcher3/model/DeviceGridState.java
@@ -21,11 +21,6 @@
import static com.android.launcher3.LauncherPrefs.DEVICE_TYPE;
import static com.android.launcher3.LauncherPrefs.HOTSEAT_COUNT;
import static com.android.launcher3.LauncherPrefs.WORKSPACE_SIZE;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_2;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_3;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_4;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_5;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_6;
import android.content.Context;
import android.text.TextUtils;
@@ -114,17 +109,23 @@
*/
public LauncherEvent getWorkspaceSizeEvent() {
if (!TextUtils.isEmpty(mGridSizeString)) {
- switch (getColumns()) {
- case 6:
- return LAUNCHER_GRID_SIZE_6;
- case 5:
- return LAUNCHER_GRID_SIZE_5;
- case 4:
- return LAUNCHER_GRID_SIZE_4;
- case 3:
- return LAUNCHER_GRID_SIZE_3;
- case 2:
- return LAUNCHER_GRID_SIZE_2;
+ switch (mGridSizeString) {
+ case "2,2":
+ return LauncherEvent.LAUNCHER_GRID_SIZE_2_BY_2;
+ case "3,3":
+ return LauncherEvent.LAUNCHER_GRID_SIZE_3_BY_3;
+ case "4,4":
+ return LauncherEvent.LAUNCHER_GRID_SIZE_4_BY_4;
+ case "4,5":
+ return LauncherEvent.LAUNCHER_GRID_SIZE_4_BY_5;
+ case "4,6":
+ return LauncherEvent.LAUNCHER_GRID_SIZE_4_BY_6;
+ case "5,5":
+ return LauncherEvent.LAUNCHER_GRID_SIZE_5_BY_5;
+ case "5,6":
+ return LauncherEvent.LAUNCHER_GRID_SIZE_5_BY_6;
+ case "6,5":
+ return LauncherEvent.LAUNCHER_GRID_SIZE_6_BY_5;
}
}
return null;
diff --git a/src/com/android/launcher3/model/FirstScreenBroadcast.java b/src/com/android/launcher3/model/FirstScreenBroadcast.java
index f443f81..829e0fe 100644
--- a/src/com/android/launcher3/model/FirstScreenBroadcast.java
+++ b/src/com/android/launcher3/model/FirstScreenBroadcast.java
@@ -45,9 +45,9 @@
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@@ -76,9 +76,9 @@
private static final String VERIFICATION_TOKEN_EXTRA = "verificationToken";
- private final HashMap<PackageUserKey, SessionInfo> mSessionInfoForPackage;
+ private final Map<PackageUserKey, SessionInfo> mSessionInfoForPackage;
- public FirstScreenBroadcast(HashMap<PackageUserKey, SessionInfo> sessionInfoForPackage) {
+ public FirstScreenBroadcast(Map<PackageUserKey, SessionInfo> sessionInfoForPackage) {
mSessionInfoForPackage = sessionInfoForPackage;
}
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index bd8c36b..77d0d76 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -45,13 +45,14 @@
import com.android.launcher3.Flags;
import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.backuprestore.LauncherRestoreEventLogger;
import com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RestoreError;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dagger.ApplicationContext;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.data.AppInfo;
@@ -70,6 +71,10 @@
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.UserIconInfo;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+
import java.net.URISyntaxException;
import java.security.InvalidParameterException;
@@ -82,8 +87,8 @@
private final LongSparseArray<UserHandle> allUsers;
- private final LauncherAppState mApp;
private final Context mContext;
+ private final LauncherModel mModel;
private final PackageManagerHelper mPmHelper;
private final IconCache mIconCache;
private final InvariantDeviceProfile mIDP;
@@ -130,17 +135,24 @@
public int itemType;
public int restoreFlag;
- public LoaderCursor(Cursor cursor, LauncherAppState app, UserManagerState userManagerState,
- PackageManagerHelper pmHelper,
- @Nullable LauncherRestoreEventLogger restoreEventLogger) {
+ @AssistedInject
+ public LoaderCursor(
+ @Assisted Cursor cursor,
+ @Assisted UserManagerState userManagerState,
+ @Assisted @Nullable LauncherRestoreEventLogger restoreEventLogger,
+ @ApplicationContext Context context,
+ IconCache iconCache,
+ InvariantDeviceProfile idp,
+ LauncherModel model,
+ PackageManagerHelper pmHelper) {
super(cursor);
-
- mApp = app;
- allUsers = userManagerState.allUsers;
- mContext = app.getContext();
- mIconCache = app.getIconCache();
+ mContext = context;
+ mIconCache = iconCache;
+ mIDP = idp;
+ mModel = model;
mPmHelper = pmHelper;
- mIDP = app.getInvariantDeviceProfile();
+
+ allUsers = userManagerState.allUsers;
mRestoreEventLogger = restoreEventLogger;
// Init column indices
@@ -432,7 +444,7 @@
*/
public ContentWriter updater() {
return new ContentWriter(mContext, new ContentWriter.CommitParams(
- mApp.getModel().getModelDbController(),
+ mModel.getModelDbController(),
BaseColumns._ID + "= ?", new String[]{Integer.toString(id)}));
}
@@ -454,7 +466,7 @@
public boolean commitDeleted() {
if (mItemsToRemove.size() > 0) {
// Remove dead items
- mApp.getModel().getModelDbController().delete(TABLE_NAME,
+ mModel.getModelDbController().delete(TABLE_NAME,
Utilities.createDbSelectionQuery(Favorites._ID, mItemsToRemove), null);
return true;
}
@@ -480,7 +492,7 @@
// Update restored items that no longer require special handling
ContentValues values = new ContentValues();
values.put(Favorites.RESTORED, 0);
- mApp.getModel().getModelDbController().update(TABLE_NAME, values,
+ mModel.getModelDbController().update(TABLE_NAME, values,
Utilities.createDbSelectionQuery(Favorites._ID, mRestoredRows), null);
}
if (mRestoreEventLogger != null) {
@@ -645,4 +657,12 @@
return false;
}
}
+
+ @AssistedFactory
+ public interface LoaderCursorFactory {
+
+ LoaderCursor createLoaderCursor(Cursor cursor,
+ UserManagerState userManagerState,
+ @Nullable LauncherRestoreEventLogger restoreEventLogger);
+ }
}
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index d44b289..73af6a2 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -20,7 +20,6 @@
import static com.android.launcher3.Flags.enableLauncherBrMetricsFixed;
import static com.android.launcher3.Flags.enableSmartspaceAsAWidget;
import static com.android.launcher3.Flags.enableSmartspaceRemovalToggle;
-import static com.android.launcher3.Flags.enableTieredWidgetsByDefaultInPicker;
import static com.android.launcher3.LauncherPrefs.IS_FIRST_LOAD_AFTER_RESTORE;
import static com.android.launcher3.LauncherPrefs.SHOULD_SHOW_SMARTSPACE;
import static com.android.launcher3.LauncherSettings.Favorites.DESKTOP_ICON_FLAG;
@@ -54,7 +53,6 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
-import android.util.ArrayMap;
import android.util.Log;
import android.util.LongSparseArray;
@@ -64,13 +62,14 @@
import androidx.annotation.WorkerThread;
import com.android.launcher3.Flags;
-import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.Utilities;
import com.android.launcher3.backuprestore.LauncherRestoreEventLogger;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dagger.ApplicationContext;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderGridOrganizer;
import com.android.launcher3.folder.FolderNameInfos;
@@ -83,6 +82,7 @@
import com.android.launcher3.icons.cache.IconCacheUpdateHandler;
import com.android.launcher3.icons.cache.LauncherActivityCachingLogic;
import com.android.launcher3.logging.FileLog;
+import com.android.launcher3.model.LoaderCursor.LoaderCursorFactory;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.AppPairInfo;
import com.android.launcher3.model.data.FolderInfo;
@@ -107,6 +107,10 @@
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.widget.WidgetInflater;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -118,6 +122,8 @@
import java.util.Set;
import java.util.concurrent.CancellationException;
+import javax.inject.Named;
+
/**
* Runnable for the thread that loads the contents of the launcher:
* - workspace icons
@@ -132,10 +138,14 @@
private static final boolean DEBUG = true;
- @NonNull
- protected final LauncherAppState mApp;
+ private final Context mContext;
+ private final LauncherModel mModel;
+ private final InvariantDeviceProfile mIDP;
+ private final boolean mIsSafeModeEnabled;
private final AllAppsList mBgAllAppsList;
protected final BgDataModel mBgDataModel;
+ private final LoaderCursorFactory mLoaderCursorFactory;
+
private final ModelDelegate mModelDelegate;
private boolean mIsRestoreFromBackup;
@@ -148,13 +158,11 @@
private final UserManager mUserManager;
private final UserCache mUserCache;
private final PackageManagerHelper mPmHelper;
- private final WidgetsFilterDataProvider mWidgetsFilterDataProvider;
private final InstallSessionHelper mSessionHelper;
private final IconCache mIconCache;
private final UserManagerState mUserManagerState;
- protected final Map<ComponentKey, AppWidgetProviderInfo> mWidgetProvidersMap = new ArrayMap<>();
private Map<ShortcutKey, ShortcutInfo> mShortcutKeyToPinnedShortcuts;
private HashMap<PackageUserKey, SessionInfo> mInstallingPkgsCached;
@@ -166,32 +174,38 @@
private boolean mItemsDeleted = false;
private String mDbName;
- public LoaderTask(@NonNull LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel bgModel,
- ModelDelegate modelDelegate, @NonNull BaseLauncherBinder launcherBinder,
- @NonNull WidgetsFilterDataProvider widgetsFilterDataProvider) {
- this(app, bgAllAppsList, bgModel, modelDelegate, launcherBinder, widgetsFilterDataProvider,
- new UserManagerState());
- }
-
- @VisibleForTesting
- LoaderTask(@NonNull LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel bgModel,
- ModelDelegate modelDelegate, @NonNull BaseLauncherBinder launcherBinder,
- WidgetsFilterDataProvider widgetsFilterDataProvider,
- UserManagerState userManagerState) {
- mApp = app;
+ @AssistedInject
+ LoaderTask(
+ @ApplicationContext Context context,
+ InvariantDeviceProfile idp,
+ LauncherModel model,
+ UserCache userCache,
+ PackageManagerHelper pmHelper,
+ InstallSessionHelper sessionHelper,
+ IconCache iconCache,
+ AllAppsList bgAllAppsList,
+ BgDataModel bgModel,
+ LoaderCursorFactory loaderCursorFactory,
+ @Named("SAFE_MODE") boolean isSafeModeEnabled,
+ @Assisted @NonNull BaseLauncherBinder launcherBinder,
+ @Assisted UserManagerState userManagerState) {
+ mContext = context;
+ mIDP = idp;
+ mModel = model;
+ mIsSafeModeEnabled = isSafeModeEnabled;
mBgAllAppsList = bgAllAppsList;
mBgDataModel = bgModel;
- mModelDelegate = modelDelegate;
+ mModelDelegate = model.getModelDelegate();
mLauncherBinder = launcherBinder;
- mLauncherApps = mApp.getContext().getSystemService(LauncherApps.class);
- mUserManager = mApp.getContext().getSystemService(UserManager.class);
- mUserCache = UserCache.INSTANCE.get(mApp.getContext());
- mPmHelper = PackageManagerHelper.INSTANCE.get(mApp.getContext());
- mSessionHelper = InstallSessionHelper.INSTANCE.get(mApp.getContext());
- mIconCache = mApp.getIconCache();
+ mLoaderCursorFactory = loaderCursorFactory;
+ mLauncherApps = mContext.getSystemService(LauncherApps.class);
+ mUserManager = mContext.getSystemService(UserManager.class);
+ mUserCache = userCache;
+ mPmHelper = pmHelper;
+ mSessionHelper = sessionHelper;
+ mIconCache = iconCache;
mUserManagerState = userManagerState;
mInstallingPkgsCached = null;
- mWidgetsFilterDataProvider = widgetsFilterDataProvider;
}
protected synchronized void waitForIdle() {
@@ -221,7 +235,7 @@
.filter(currentScreenContentFilter(firstScreens))
.toList();
final int disableArchivingLauncherBroadcast = Settings.Secure.getInt(
- mApp.getContext().getContentResolver(),
+ mContext.getContentResolver(),
"disable_launcher_broadcast_installed_apps",
/* default */ 0);
boolean shouldAttachArchivingExtras = mIsRestoreFromBackup
@@ -236,10 +250,10 @@
mBgDataModel.itemsIdMap.stream().filter(WIDGET_FILTER).toList()
);
logASplit("Sending first screen broadcast with additional archiving Extras");
- FirstScreenBroadcastHelper.sendBroadcastsForModels(mApp.getContext(), broadcastModels);
+ FirstScreenBroadcastHelper.sendBroadcastsForModels(mContext, broadcastModels);
} else {
logASplit("Sending first screen broadcast");
- mFirstScreenBroadcast.sendBroadcasts(mApp.getContext(), firstScreenItems);
+ mFirstScreenBroadcast.sendBroadcasts(mContext, firstScreenItems);
}
}
@@ -255,16 +269,14 @@
MODEL_EXECUTOR.elevatePriority(CALLER_LOADER_TASK);
LoaderMemoryLogger memoryLogger = new LoaderMemoryLogger();
mIsRestoreFromBackup =
- LauncherPrefs.get(mApp.getContext()).get(IS_FIRST_LOAD_AFTER_RESTORE);
+ LauncherPrefs.get(mContext).get(IS_FIRST_LOAD_AFTER_RESTORE);
LauncherRestoreEventLogger restoreEventLogger = null;
if (enableLauncherBrMetricsFixed()) {
- restoreEventLogger = LauncherRestoreEventLogger.Companion
- .newInstance(mApp.getContext());
+ restoreEventLogger = LauncherRestoreEventLogger.Companion.newInstance(mContext);
}
- try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
-
+ try (LauncherModel.LoaderTransaction transaction = mModel.beginLoader(this)) {
List<CacheableShortcutInfo> allShortcuts = new ArrayList<>();
- loadWorkspace(allShortcuts, "", memoryLogger, restoreEventLogger);
+ loadWorkspace(allShortcuts, "", new HashMap<>(), memoryLogger, restoreEventLogger);
// Sanitize data re-syncs widgets/shortcuts based on the workspace loaded from db.
// sanitizeData should not be invoked if the workspace is loaded from a db different
@@ -273,7 +285,7 @@
// TODO(b/384731096): Write Unit Test to make sure sanitizeWidgetsShortcutsAndPackages
// actually re-pins shortcuts that are in model but not in ShortcutManager, if possible
// after a simulated restore.
- if (Objects.equals(mApp.getInvariantDeviceProfile().dbFile, mDbName)) {
+ if (Objects.equals(mIDP.dbFile, mDbName)) {
verifyNotStopped();
sanitizeFolders(mItemsDeleted);
sanitizeAppPairs();
@@ -313,13 +325,13 @@
setIgnorePackages(updateHandler);
updateHandler.updateIcons(allActivityList,
LauncherActivityCachingLogic.INSTANCE,
- mApp.getModel()::onPackageIconsUpdated);
+ mModel::onPackageIconsUpdated);
logASplit("update AllApps icon cache finished");
verifyNotStopped();
logASplit("saving all shortcuts in icon cache");
updateHandler.updateIcons(allShortcuts, CacheableShortcutCachingLogic.INSTANCE,
- mApp.getModel()::onPackageIconsUpdated);
+ mModel::onPackageIconsUpdated);
// Take a break
waitForIdle();
@@ -348,21 +360,14 @@
// fourth step
WidgetsModel widgetsModel = mBgDataModel.widgetsModel;
- if (enableTieredWidgetsByDefaultInPicker()) {
- // Begin periodic refresh of filters
- mWidgetsFilterDataProvider.initPeriodicDataRefresh(
- mApp.getModel()::onWidgetFiltersLoaded);
- // And, update model with currently cached data.
- widgetsModel.updateWidgetFilters(mWidgetsFilterDataProvider);
- }
- List<CachedObject> allWidgetsList = widgetsModel.update(mApp, /*packageUser=*/null);
+ List<CachedObject> allWidgetsList = widgetsModel.update(/*packageUser=*/null);
logASplit("load widgets finished");
verifyNotStopped();
mLauncherBinder.bindWidgets();
logASplit("bindWidgets finished");
verifyNotStopped();
- LauncherPrefs prefs = LauncherPrefs.get(mApp.getContext());
+ LauncherPrefs prefs = LauncherPrefs.get(mContext);
if (enableSmartspaceAsAWidget() && prefs.get(SHOULD_SHOW_SMARTSPACE)) {
mLauncherBinder.bindSmartspaceWidget();
@@ -379,7 +384,7 @@
logASplit("saving all widgets in icon cache");
updateHandler.updateIcons(allWidgetsList,
CachedObjectCachingLogic.INSTANCE,
- mApp.getModel()::onWidgetLabelsUpdated);
+ mModel::onWidgetLabelsUpdated);
// fifth step
loadFolderNames();
@@ -393,7 +398,7 @@
memoryLogger.clearLogs();
if (mIsRestoreFromBackup) {
mIsRestoreFromBackup = false;
- LauncherPrefs.get(mApp.getContext()).putSync(IS_FIRST_LOAD_AFTER_RESTORE.to(false));
+ LauncherPrefs.get(mContext).putSync(IS_FIRST_LOAD_AFTER_RESTORE.to(false));
if (restoreEventLogger != null) {
restoreEventLogger.reportLauncherRestoreResults();
}
@@ -415,35 +420,42 @@
this.notify();
}
- protected void loadWorkspace(
+ public void loadWorkspaceForPreview(String selection,
+ Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap) {
+ loadWorkspace(new ArrayList<>(), selection, widgetProviderInfoMap, null, null);
+ }
+
+ private void loadWorkspace(
List<CacheableShortcutInfo> allDeepShortcuts,
String selection,
+ Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap,
@Nullable LoaderMemoryLogger memoryLogger,
@Nullable LauncherRestoreEventLogger restoreEventLogger
) {
Trace.beginSection("LoadWorkspace");
try {
- loadWorkspaceImpl(allDeepShortcuts, selection, memoryLogger, restoreEventLogger);
+ loadWorkspaceImpl(allDeepShortcuts, selection, widgetProviderInfoMap,
+ memoryLogger, restoreEventLogger);
} finally {
Trace.endSection();
}
logASplit("loadWorkspace finished");
mBgDataModel.isFirstPagePinnedItemEnabled = FeatureFlags.QSB_ON_FIRST_SCREEN
- && (!enableSmartspaceRemovalToggle() || LauncherPrefs.getPrefs(
- mApp.getContext()).getBoolean(SMARTSPACE_ON_HOME_SCREEN, true));
+ && (!enableSmartspaceRemovalToggle()
+ || LauncherPrefs.getPrefs(mContext).getBoolean(SMARTSPACE_ON_HOME_SCREEN, true));
}
private void loadWorkspaceImpl(
List<CacheableShortcutInfo> allDeepShortcuts,
String selection,
+ Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap,
@Nullable LoaderMemoryLogger memoryLogger,
@Nullable LauncherRestoreEventLogger restoreEventLogger) {
- final Context context = mApp.getContext();
final boolean isSdCardReady = Utilities.isBootCompleted();
- final WidgetInflater widgetInflater = new WidgetInflater(context);
+ final WidgetInflater widgetInflater = new WidgetInflater(mContext, mIsSafeModeEnabled);
- ModelDbController dbController = mApp.getModel().getModelDbController();
+ ModelDbController dbController = mModel.getModelDbController();
if (Flags.gridMigrationRefactor()) {
try {
dbController.attemptMigrateDb(restoreEventLogger, mModelDelegate);
@@ -465,28 +477,29 @@
if (Flags.enableSupportForArchiving()) {
mInstallingPkgsCached = installingPkgs;
}
- installingPkgs.forEach(mApp.getIconCache()::updateSessionCache);
+ installingPkgs.forEach(mIconCache::updateSessionCache);
FileLog.d(TAG, "loadWorkspace: Packages with active install/update sessions: "
+ installingPkgs.keySet().stream().map(info -> info.mPackageName).toList());
mFirstScreenBroadcast = new FirstScreenBroadcast(installingPkgs);
mShortcutKeyToPinnedShortcuts = new HashMap<>();
- final LoaderCursor c = new LoaderCursor(
+ final LoaderCursor c = mLoaderCursorFactory.createLoaderCursor(
dbController.query(TABLE_NAME, null, selection, null, null),
- mApp, mUserManagerState, mPmHelper,
+ mUserManagerState,
mIsRestoreFromBackup ? restoreEventLogger : null);
final Bundle extras = c.getExtras();
mDbName = extras == null ? null : extras.getString(ModelDbController.EXTRA_DB_NAME);
try {
final LongSparseArray<Boolean> unlockedUsers = new LongSparseArray<>();
- queryPinnedShortcutsForUnlockedUsers(context, unlockedUsers);
+ queryPinnedShortcutsForUnlockedUsers(mContext, unlockedUsers);
mWorkspaceIconRequestInfos = new ArrayList<>();
WorkspaceItemProcessor itemProcessor = new WorkspaceItemProcessor(c, memoryLogger,
mUserCache, mUserManagerState, mLauncherApps, mPendingPackages,
- mShortcutKeyToPinnedShortcuts, mApp, mBgDataModel,
- mWidgetProvidersMap, installingPkgs, isSdCardReady,
+ mShortcutKeyToPinnedShortcuts, mContext, mIDP, mIconCache,
+ mIsSafeModeEnabled, mBgDataModel,
+ widgetProviderInfoMap, installingPkgs, isSdCardReady,
widgetInflater, mPmHelper, mWorkspaceIconRequestInfos, unlockedUsers,
allDeepShortcuts);
@@ -590,7 +603,7 @@
@WorkerThread
private void processFolderItems() {
// Sort the folder items, update ranks, and make sure all preview items are high res.
- List<FolderGridOrganizer> verifiers = mApp.getInvariantDeviceProfile().supportedProfiles
+ List<FolderGridOrganizer> verifiers = mIDP.supportedProfiles
.stream().map(FolderGridOrganizer::createFolderGridOrganizer).toList();
for (ItemInfo itemInfo : mBgDataModel.itemsIdMap) {
if (!(itemInfo instanceof FolderInfo folder)) {
@@ -630,7 +643,7 @@
if (mIconCache.isDefaultIcon(wai.bitmap, wai.user)) {
logASplit("tryLoadWorkspaceIconsInBulk: default icon found for "
+ wai.getTargetComponent() + ", will attempt to load from iconBlob");
- iconRequestInfo.loadIconFromDbBlob(mApp.getContext());
+ iconRequestInfo.loadIconFromDbBlob(mContext);
}
}
} finally {
@@ -662,7 +675,7 @@
private void sanitizeFolders(boolean itemsDeleted) {
if (itemsDeleted) {
// Remove any empty folder
- IntArray deletedFolderIds = mApp.getModel().getModelDbController().deleteEmptyFolders();
+ IntArray deletedFolderIds = mModel.getModelDbController().deleteEmptyFolders();
synchronized (mBgDataModel) {
for (int folderId : deletedFolderIds) {
mBgDataModel.itemsIdMap.remove(folderId);
@@ -673,8 +686,8 @@
/** Cleans up app pairs if they don't have the right number of member apps (2). */
private void sanitizeAppPairs() {
- IntArray deletedAppPairIds = mApp.getModel().getModelDbController().deleteBadAppPairs();
- IntArray deletedAppIds = mApp.getModel().getModelDbController().deleteUnparentedApps();
+ IntArray deletedAppPairIds = mModel.getModelDbController().deleteBadAppPairs();
+ IntArray deletedAppIds = mModel.getModelDbController().deleteUnparentedApps();
IntArray deleted = new IntArray();
deleted.addAll(deletedAppPairIds);
@@ -688,17 +701,15 @@
}
private void sanitizeWidgetsShortcutsAndPackages() {
- Context context = mApp.getContext();
-
// Remove any ghost widgets
- mApp.getModel().getModelDbController().removeGhostWidgets();
+ mModel.getModelDbController().removeGhostWidgets();
// Update pinned state of model shortcuts
- mBgDataModel.updateShortcutPinnedState(context);
+ mBgDataModel.updateShortcutPinnedState(mContext);
if (!Utilities.isBootCompleted() && !mPendingPackages.isEmpty()) {
- context.registerReceiver(
- new SdCardAvailableReceiver(mApp, mPendingPackages),
+ mContext.registerReceiver(
+ new SdCardAvailableReceiver(mContext, mModel, mPendingPackages),
new IntentFilter(Intent.ACTION_BOOT_COMPLETED),
null,
MODEL_EXECUTOR.getHandler());
@@ -735,7 +746,7 @@
for (int i = 0; i < apps.size(); i++) {
LauncherActivityInfo app = apps.get(i);
AppInfo appInfo = new AppInfo(app, mUserCache.getUserInfo(user),
- ApiWrapper.INSTANCE.get(mApp.getContext()), mPmHelper, quietMode);
+ ApiWrapper.INSTANCE.get(mContext), mPmHelper, quietMode);
if (Flags.enableSupportForArchiving() && app.getApplicationInfo().isArchived) {
// For archived apps, include progress info in case there is a pending
// install session post restart of device.
@@ -764,7 +775,7 @@
for (PackageInstaller.SessionInfo info :
mSessionHelper.getAllVerifiedSessions()) {
AppInfo promiseAppInfo = mBgAllAppsList.addPromiseApp(
- mApp.getContext(),
+ mContext,
PackageInstallInfo.fromInstallingState(info),
false);
@@ -789,7 +800,7 @@
+ appInfo.getTargetComponent()
+ ", will attempt to load from iconBlob: "
+ Arrays.toString(iconRequestInfo.iconBlob));
- iconRequestInfo.loadIconFromDbBlob(mApp.getContext());
+ iconRequestInfo.loadIconFromDbBlob(mContext);
}
}
}
@@ -807,9 +818,9 @@
mUserManagerState.isAnyProfileQuietModeEnabled());
}
mBgAllAppsList.setFlags(FLAG_HAS_SHORTCUT_PERMISSION,
- hasShortcutsPermission(mApp.getContext()));
+ hasShortcutsPermission(mContext));
mBgAllAppsList.setFlags(FLAG_QUIET_MODE_CHANGE_PERMISSION,
- mApp.getContext().checkSelfPermission("android.permission.MODIFY_QUIET_MODE")
+ mContext.checkSelfPermission("android.permission.MODIFY_QUIET_MODE")
== PackageManager.PERMISSION_GRANTED);
mBgAllAppsList.getAndResetChangeFlag();
@@ -840,7 +851,7 @@
workspaceIconRequest.get().iconBlob,
false /* useLowResIcon= */
);
- if (!iconRequestInfo.loadIconFromDbBlob(mApp.getContext())) {
+ if (!iconRequestInfo.loadIconFromDbBlob(mContext)) {
Log.d(TAG, "AppInfo Icon failed to load from blob, using cache.");
mIconCache.getTitleAndIcon(
appInfo,
@@ -866,7 +877,7 @@
if (mBgAllAppsList.hasShortcutHostPermission()) {
for (UserHandle user : mUserCache.getUserProfiles()) {
if (mUserManager.isUserUnlocked(user)) {
- List<ShortcutInfo> shortcuts = new ShortcutRequest(mApp.getContext(), user)
+ List<ShortcutInfo> shortcuts = new ShortcutRequest(mContext, user)
.query(ShortcutRequest.ALL);
allShortcuts.addAll(shortcuts);
mBgDataModel.updateDeepShortcutCounts(null, user, shortcuts);
@@ -877,7 +888,7 @@
}
private void loadFolderNames() {
- FolderNameProvider provider = FolderNameProvider.newInstance(mApp.getContext(),
+ FolderNameProvider provider = FolderNameProvider.newInstance(mContext,
mBgAllAppsList.data, FolderNameProvider.getCollectionForSuggestions(mBgDataModel));
synchronized (mBgDataModel) {
@@ -887,7 +898,7 @@
.forEach(info -> {
FolderInfo fi = (FolderInfo) info;
FolderNameInfos suggestionInfos = new FolderNameInfos();
- provider.getSuggestedFolderName(mApp.getContext(), fi.getAppContents(),
+ provider.getSuggestedFolderName(mContext, fi.getAppContents(),
suggestionInfos);
fi.suggestedFolderNames = suggestionInfos;
});
@@ -904,4 +915,10 @@
Log.d(TAG, label);
}
}
+
+ @AssistedFactory
+ public interface LoaderTaskFactory {
+
+ LoaderTask newLoaderTask(BaseLauncherBinder binder, UserManagerState userState);
+ }
}
diff --git a/src/com/android/launcher3/model/ModelLauncherCallbacks.kt b/src/com/android/launcher3/model/ModelLauncherCallbacks.kt
index 7ba2dad..8b6c369 100644
--- a/src/com/android/launcher3/model/ModelLauncherCallbacks.kt
+++ b/src/com/android/launcher3/model/ModelLauncherCallbacks.kt
@@ -114,7 +114,7 @@
override fun onUpdateSessionDisplay(key: PackageUserKey, info: SessionInfo) {
/** Updates the icons and label of all pending icons for the provided package name. */
taskExecutor.accept { controller, _, _ ->
- controller.app.iconCache.updateSessionCache(key, info)
+ controller.iconCache.updateSessionCache(key, info)
}
taskExecutor.accept(
CacheDataUpdatedTask(
@@ -128,7 +128,7 @@
override fun onInstallSessionCreated(sessionInfo: PackageInstallInfo) {
if (FeatureFlags.PROMISE_APPS_IN_ALL_APPS.get()) {
taskExecutor.accept { taskController, _, apps ->
- apps.addPromiseApp(taskController.app.context, sessionInfo)
+ apps.addPromiseApp(taskController.context, sessionInfo)
taskController.bindApplicationsIfNeeded()
}
}
diff --git a/src/com/android/launcher3/model/ModelTaskController.kt b/src/com/android/launcher3/model/ModelTaskController.kt
index 6e3e35e..f17ca32 100644
--- a/src/com/android/launcher3/model/ModelTaskController.kt
+++ b/src/com/android/launcher3/model/ModelTaskController.kt
@@ -16,27 +16,34 @@
package com.android.launcher3.model
-import com.android.launcher3.LauncherAppState
+import android.content.Context
import com.android.launcher3.LauncherModel
import com.android.launcher3.LauncherModel.CallbackTask
import com.android.launcher3.celllayout.CellPosMapper
+import com.android.launcher3.dagger.ApplicationContext
+import com.android.launcher3.icons.IconCache
import com.android.launcher3.model.BgDataModel.FixedContainerItems
import com.android.launcher3.model.data.ItemInfo
+import com.android.launcher3.util.Executors.MAIN_EXECUTOR
import com.android.launcher3.util.PackageUserKey
import com.android.launcher3.widget.model.WidgetsListBaseEntriesBuilder
import java.util.Objects
-import java.util.concurrent.Executor
import java.util.function.Predicate
+import javax.inject.Inject
/** Class with utility methods and properties for running a LauncherModel Task */
-class ModelTaskController(
- val app: LauncherAppState,
+class ModelTaskController
+@Inject
+constructor(
+ @ApplicationContext val context: Context,
+ val iconCache: IconCache,
val dataModel: BgDataModel,
val allAppsList: AllAppsList,
- private val model: LauncherModel,
- private val uiExecutor: Executor,
+ val model: LauncherModel,
) {
+ private val uiExecutor = MAIN_EXECUTOR
+
/** Schedules a {@param task} to be executed on the current callbacks. */
fun scheduleCallbackTask(task: CallbackTask) {
for (cb in model.callbacks) {
@@ -77,19 +84,10 @@
}
fun bindUpdatedWidgets(dataModel: BgDataModel) {
- val widgetsByPackageItem = dataModel.widgetsModel.widgetsByPackageItemForPicker
- val allWidgets = WidgetsListBaseEntriesBuilder(app.context).build(widgetsByPackageItem)
-
- val defaultWidgetsFilter = dataModel.widgetsModel.defaultWidgetsFilter
- val defaultWidgets =
- if (defaultWidgetsFilter != null) {
- WidgetsListBaseEntriesBuilder(app.context)
- .build(widgetsByPackageItem, defaultWidgetsFilter)
- } else {
- emptyList()
- }
-
- scheduleCallbackTask { it.bindAllWidgets(allWidgets, defaultWidgets) }
+ val allWidgets =
+ WidgetsListBaseEntriesBuilder(context)
+ .build(dataModel.widgetsModel.widgetsByPackageItemForPicker)
+ scheduleCallbackTask { it.bindAllWidgets(allWidgets) }
}
fun deleteAndBindComponentsRemoved(matcher: Predicate<ItemInfo?>, reason: String?) {
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
index 0332775..dc103a6 100644
--- a/src/com/android/launcher3/model/ModelWriter.java
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -229,7 +229,7 @@
}).executeOnModelThread();
}
- private void notifyItemModified(ItemInfo item) {
+ public void notifyItemModified(ItemInfo item) {
notifyOtherCallbacks(c -> c.bindItemsModified(Collections.singletonList(item)));
}
diff --git a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
index a216042..a3561ed 100644
--- a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
+++ b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
@@ -52,11 +52,11 @@
try {
// For instant apps we do not get package-add. Use setting events to update
// any pinned icons.
- Context context = taskController.getApp().getContext();
+ Context context = taskController.getContext();
ApplicationInfo ai = context
.getPackageManager().getApplicationInfo(mInstallInfo.packageName, 0);
if (InstantAppResolver.newInstance(context).isInstantApp(ai)) {
- taskController.getApp().getModel().newModelCallbacks()
+ taskController.getModel().newModelCallbacks()
.onPackageAdded(ai.packageName, mInstallInfo.user);
}
} catch (PackageManager.NameNotFoundException e) {
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 3cdb250..04f3faa 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -38,7 +38,6 @@
import androidx.annotation.NonNull;
import com.android.launcher3.Flags;
-import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel.ModelUpdateTask;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.config.FeatureFlags;
@@ -106,9 +105,8 @@
@Override
public void execute(@NonNull ModelTaskController taskController, @NonNull BgDataModel dataModel,
@NonNull AllAppsList appsList) {
- final LauncherAppState app = taskController.getApp();
- final Context context = app.getContext();
- final IconCache iconCache = app.getIconCache();
+ final Context context = taskController.getContext();
+ final IconCache iconCache = taskController.getIconCache();
final String[] packages = mPackages;
final int packageCount = packages.length;
@@ -433,7 +431,7 @@
// Load widgets for the new package. Changes due to app updates are handled through
// AppWidgetHost events, this is just to initialize the long-press options.
for (int i = 0; i < packageCount; i++) {
- dataModel.widgetsModel.update(app, new PackageUserKey(packages[i], mUser));
+ dataModel.widgetsModel.update(new PackageUserKey(packages[i], mUser));
}
taskController.bindUpdatedWidgets(dataModel);
}
diff --git a/src/com/android/launcher3/model/SdCardAvailableReceiver.java b/src/com/android/launcher3/model/SdCardAvailableReceiver.java
index 9e3f0e1..9ae8092 100644
--- a/src/com/android/launcher3/model/SdCardAvailableReceiver.java
+++ b/src/com/android/launcher3/model/SdCardAvailableReceiver.java
@@ -22,7 +22,6 @@
import android.content.pm.LauncherApps;
import android.os.UserHandle;
-import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.util.ApplicationInfoWrapper;
import com.android.launcher3.util.PackageUserKey;
@@ -43,9 +42,10 @@
private final Context mContext;
private final Set<PackageUserKey> mPackages;
- public SdCardAvailableReceiver(LauncherAppState app, Set<PackageUserKey> packages) {
- mModel = app.getModel();
- mContext = app.getContext();
+ public SdCardAvailableReceiver(
+ Context context, LauncherModel model, Set<PackageUserKey> packages) {
+ mContext = context;
+ mModel = model;
mPackages = packages;
}
diff --git a/src/com/android/launcher3/model/SessionFailureTask.kt b/src/com/android/launcher3/model/SessionFailureTask.kt
index 8baf568..6ed5178 100644
--- a/src/com/android/launcher3/model/SessionFailureTask.kt
+++ b/src/com/android/launcher3/model/SessionFailureTask.kt
@@ -33,9 +33,9 @@
dataModel: BgDataModel,
apps: AllAppsList,
) {
- val iconCache = taskController.app.iconCache
+ val iconCache = taskController.iconCache
val isAppArchived =
- ApplicationInfoWrapper(taskController.app.context, packageName, user).isArchived()
+ ApplicationInfoWrapper(taskController.context, packageName, user).isArchived()
synchronized(dataModel) {
if (isAppArchived) {
val updatedItems = mutableListOf<WorkspaceItemInfo>()
diff --git a/src/com/android/launcher3/model/ShortcutsChangedTask.kt b/src/com/android/launcher3/model/ShortcutsChangedTask.kt
index 56e9e43..d6759e2 100644
--- a/src/com/android/launcher3/model/ShortcutsChangedTask.kt
+++ b/src/com/android/launcher3/model/ShortcutsChangedTask.kt
@@ -40,8 +40,7 @@
dataModel: BgDataModel,
apps: AllAppsList,
) {
- val app = taskController.app
- val context = app.context
+ val context = taskController.context
// Find WorkspaceItemInfo's that have changed on the workspace.
val matchingWorkspaceItems = ArrayList<WorkspaceItemInfo>()
@@ -88,7 +87,7 @@
.filter { itemInfo: WorkspaceItemInfo -> shortcutId == itemInfo.deepShortcutId }
.forEach { workspaceItemInfo: WorkspaceItemInfo ->
workspaceItemInfo.updateFromDeepShortcutInfo(fullDetails, context)
- app.iconCache.getShortcutIcon(
+ taskController.iconCache.getShortcutIcon(
workspaceItemInfo,
CacheableShortcutInfo(fullDetails, infoWrapper),
)
diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java
index 3dc5ff3..4d28ccb 100644
--- a/src/com/android/launcher3/model/UserLockStateChangedTask.java
+++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java
@@ -23,7 +23,6 @@
import androidx.annotation.NonNull;
-import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel.ModelUpdateTask;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -55,8 +54,7 @@
@Override
public void execute(@NonNull ModelTaskController taskController, @NonNull BgDataModel dataModel,
@NonNull AllAppsList apps) {
- LauncherAppState app = taskController.getApp();
- Context context = app.getContext();
+ Context context = taskController.getContext();
HashMap<ShortcutKey, ShortcutInfo> pinnedShortcuts = new HashMap<>();
if (mIsUserUnlocked) {
@@ -92,7 +90,7 @@
}
si.runtimeStatusFlags &= ~FLAG_DISABLED_LOCKED_USER;
si.updateFromDeepShortcutInfo(shortcut, context);
- app.getIconCache().getShortcutIcon(si, shortcut);
+ taskController.getIconCache().getShortcutIcon(si, shortcut);
} else {
si.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER;
}
diff --git a/src/com/android/launcher3/model/WidgetsFilterDataProvider.kt b/src/com/android/launcher3/model/WidgetsFilterDataProvider.kt
index 0571de3..90d6fb2 100644
--- a/src/com/android/launcher3/model/WidgetsFilterDataProvider.kt
+++ b/src/com/android/launcher3/model/WidgetsFilterDataProvider.kt
@@ -16,55 +16,38 @@
package com.android.launcher3.model
-import android.content.Context
-import androidx.annotation.WorkerThread
-import com.android.launcher3.R
-import com.android.launcher3.util.ResourceBasedOverride
+import com.android.launcher3.dagger.LauncherAppSingleton
import java.util.function.Predicate
+import javax.inject.Inject
/** Helper for the widgets model to load the filters that can be applied to available widgets. */
-open class WidgetsFilterDataProvider(val context: Context) : ResourceBasedOverride {
+@LauncherAppSingleton
+open class WidgetsFilterDataProvider @Inject constructor() {
+
+ /** Filter that should be applied to the widget predictions */
+ open val predictedWidgetsFilter: Predicate<WidgetItem>? = null
+
/**
- * Start regular periodic refresh of widget filtering data starting now (if not started
- * already).
+ * Filter that should be applied to the widgets list to see which widgets can be shown by
+ * default.
*/
- @WorkerThread
- open fun initPeriodicDataRefresh(callback: WidgetsFilterLoadedCallback? = null) {
- // no-op
+ open val defaultWidgetsFilter: Predicate<WidgetItem>? = null
+
+ protected val listeners = mutableListOf<WidgetsFilterLoadedCallback>()
+
+ /** Adds a callback for listening to filter changes */
+ fun addFilterChangeCallback(callback: WidgetsFilterLoadedCallback) {
+ listeners.add(callback)
}
- /**
- * Returns a filter that should be applied to the widget predictions.
- *
- * @return null if no filter needs to be applied
- */
- @WorkerThread open fun getPredictedWidgetsFilter(): Predicate<WidgetItem>? = null
-
- /**
- * Returns a filter that should be applied to the widgets list to see which widgets can be shown
- * by default.
- *
- * @return null if no separate "default" list is supported
- */
- @WorkerThread open fun getDefaultWidgetsFilter(): Predicate<WidgetItem>? = null
-
- /** Called when filter data provider is no longer needed. */
- open fun destroy() {}
-
- companion object {
- /** Returns a new instance of the [WidgetsFilterDataProvider] based on resource override. */
- fun newInstance(context: Context?): WidgetsFilterDataProvider {
- return ResourceBasedOverride.Overrides.getObject(
- WidgetsFilterDataProvider::class.java,
- context,
- R.string.widgets_filter_data_provider_class,
- )
- }
+ /** Removes a previously added callback */
+ fun removeFilterChangeCallback(callback: WidgetsFilterLoadedCallback) {
+ listeners.remove(callback)
}
-}
-/** Interface for the model callback to be invoked when filters are loaded. */
-interface WidgetsFilterLoadedCallback {
- /** Method called back when widget filters are loaded */
- fun onWidgetsFilterLoaded()
+ /** Interface for the model callback to be invoked when filters are loaded. */
+ interface WidgetsFilterLoadedCallback {
+ /** Method called back when widget filters are loaded */
+ fun onWidgetsFilterLoaded()
+ }
}
diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java
index ab960d8..f2144c0 100644
--- a/src/com/android/launcher3/model/WidgetsModel.java
+++ b/src/com/android/launcher3/model/WidgetsModel.java
@@ -19,8 +19,6 @@
import android.util.Log;
import android.util.Pair;
-import androidx.annotation.AnyThread;
-import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.collection.ArrayMap;
@@ -29,6 +27,7 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dagger.ApplicationContext;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.cache.CachedObject;
import com.android.launcher3.model.data.PackageItemInfo;
@@ -56,6 +55,8 @@
import java.util.function.Predicate;
import java.util.stream.Collectors;
+import javax.inject.Inject;
+
/**
* Widgets data model that is used by the adapters of the widget views and controllers.
*
@@ -68,10 +69,31 @@
/* Map of widgets and shortcuts that are tracked per package. */
private final Map<PackageItemInfo, List<WidgetItem>> mWidgetsByPackageItem = new HashMap<>();
- @Nullable private Predicate<WidgetItem> mDefaultWidgetsFilter = null;
- @Nullable private Predicate<WidgetItem> mPredictedWidgetsFilter = null;
@Nullable private WidgetValidityCheckForPicker mWidgetValidityCheckForPicker = null;
+ private final Context mContext;
+ private final InvariantDeviceProfile mIdp;
+ private final IconCache mIconCache;
+ private final AppFilter mAppFilter;
+
+ @Inject
+ public WidgetsModel(
+ @ApplicationContext Context context,
+ InvariantDeviceProfile idp,
+ IconCache iconCache,
+ AppFilter appFilter) {
+ mContext = context;
+ mIdp = idp;
+ mIconCache = iconCache;
+ mAppFilter = appFilter;
+ }
+
+ public WidgetsModel(Context context) {
+ this(context,
+ LauncherAppState.getIDP(context),
+ LauncherAppState.getInstance(context).getIconCache(), new AppFilter(context));
+ }
+
/**
* Returns all widgets keyed by their component key.
*/
@@ -129,70 +151,36 @@
}
/**
- * Returns widget filter that can be applied to {@link WidgetItem}s to check if they can be
- * shown in the default widgets list.
- * <p>Returns null if filtering isn't available</p>
- */
- @AnyThread
- public @Nullable Predicate<WidgetItem> getDefaultWidgetsFilter() {
- return mDefaultWidgetsFilter;
- }
-
- /**
- * Returns widget filter that can be applied to {@link WidgetItem}s to check if they can be
- * part of widget predictions.
- * <p>Returns null if filter isn't available</p>
- */
- @AnyThread
- public @Nullable Predicate<WidgetItem> getPredictedWidgetsFilter() {
- return mPredictedWidgetsFilter;
- }
-
- /**
- * Updates model with latest filter data in cache.
- */
- public void updateWidgetFilters(@NonNull WidgetsFilterDataProvider widgetsFilterDataProvider) {
- if (!WIDGETS_ENABLED) {
- return;
- }
- mDefaultWidgetsFilter = widgetsFilterDataProvider.getDefaultWidgetsFilter();
- mPredictedWidgetsFilter = widgetsFilterDataProvider.getPredictedWidgetsFilter();
- }
-
- /**
* @param packageUser If null, all widgets and shortcuts are updated and returned, otherwise
* only widgets and shortcuts associated with the package/user are.
*/
- public List<CachedObject> update(
- LauncherAppState app, @Nullable PackageUserKey packageUser) {
+ public List<CachedObject> update(@Nullable PackageUserKey packageUser) {
if (!WIDGETS_ENABLED) {
return new ArrayList<>();
}
Preconditions.assertWorkerThread();
- Context context = app.getContext();
final ArrayList<WidgetItem> widgetsAndShortcuts = new ArrayList<>();
List<CachedObject> updatedItems = new ArrayList<>();
try {
- InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
// Widgets
- WidgetManagerHelper widgetManager = new WidgetManagerHelper(context);
+ WidgetManagerHelper widgetManager = new WidgetManagerHelper(mContext);
for (AppWidgetProviderInfo widgetInfo : widgetManager.getAllProviders(packageUser)) {
LauncherAppWidgetProviderInfo launcherWidgetInfo =
- LauncherAppWidgetProviderInfo.fromProviderInfo(context, widgetInfo);
+ LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo);
widgetsAndShortcuts.add(new WidgetItem(
- launcherWidgetInfo, idp, app.getIconCache(), app.getContext()));
+ launcherWidgetInfo, mIdp, mIconCache, mContext));
updatedItems.add(launcherWidgetInfo);
}
// Shortcuts
for (ShortcutConfigActivityInfo info :
- queryList(context, packageUser)) {
- widgetsAndShortcuts.add(new WidgetItem(info, app.getIconCache()));
+ queryList(mContext, packageUser)) {
+ widgetsAndShortcuts.add(new WidgetItem(info, mIconCache));
updatedItems.add(info);
}
- setWidgetsAndShortcuts(widgetsAndShortcuts, app, packageUser);
+ setWidgetsAndShortcuts(widgetsAndShortcuts, packageUser);
} catch (Exception e) {
if (!FeatureFlags.IS_STUDIO_BUILD && Utilities.isBinderSizeError(e)) {
// the returned value may be incomplete and will not be refreshed until the next
@@ -207,14 +195,14 @@
return updatedItems;
}
- private synchronized void setWidgetsAndShortcuts(ArrayList<WidgetItem> rawWidgetsShortcuts,
- LauncherAppState app, @Nullable PackageUserKey packageUser) {
+ private synchronized void setWidgetsAndShortcuts(
+ ArrayList<WidgetItem> rawWidgetsShortcuts, @Nullable PackageUserKey packageUser) {
if (DEBUG) {
Log.d(TAG, "addWidgetsAndShortcuts, widgetsShortcuts#=" + rawWidgetsShortcuts.size());
}
// Refresh the validity checker with latest app state.
- mWidgetValidityCheckForPicker = new WidgetValidityCheckForPicker(app);
+ mWidgetValidityCheckForPicker = new WidgetValidityCheckForPicker(mIdp, mAppFilter);
// Temporary cache for {@link PackageItemInfos} to avoid having to go through
// {@link mPackageItemInfos} to locate the key to be used for {@link #mWidgetsList}
@@ -231,19 +219,17 @@
// add and update.
mWidgetsByPackageItem.putAll(rawWidgetsShortcuts.stream()
.filter(new WidgetFlagCheck())
- .flatMap(widgetItem -> getPackageUserKeys(app.getContext(), widgetItem).stream()
+ .flatMap(widgetItem -> getPackageUserKeys(mContext, widgetItem).stream()
.map(key -> new Pair<>(packageItemInfoCache.getOrCreate(key), widgetItem)))
.collect(groupingBy(pair -> pair.first, mapping(pair -> pair.second, toList()))));
// Update each package entry
- IconCache iconCache = app.getIconCache();
for (PackageItemInfo p : packageItemInfoCache.values()) {
- iconCache.getTitleAndIconForApp(p, DEFAULT_LOOKUP_FLAG.withUseLowRes());
+ mIconCache.getTitleAndIconForApp(p, DEFAULT_LOOKUP_FLAG.withUseLowRes());
}
}
- public void onPackageIconsUpdated(Set<String> packageNames, UserHandle user,
- LauncherAppState app) {
+ public void onPackageIconsUpdated(Set<String> packageNames, UserHandle user) {
if (!WIDGETS_ENABLED) {
return;
}
@@ -255,11 +241,10 @@
WidgetItem item = items.get(i);
if (item.user.equals(user)) {
if (item.activityInfo != null) {
- items.set(i, new WidgetItem(item.activityInfo, app.getIconCache()));
+ items.set(i, new WidgetItem(item.activityInfo, mIconCache));
} else {
- items.set(i, new WidgetItem(item.widgetInfo,
- app.getInvariantDeviceProfile(), app.getIconCache(),
- app.getContext()));
+ items.set(i, new WidgetItem(
+ item.widgetInfo, mIdp, mIconCache, mContext));
}
}
}
@@ -312,9 +297,9 @@
private final InvariantDeviceProfile mIdp;
private final AppFilter mAppFilter;
- WidgetValidityCheckForPicker(LauncherAppState app) {
- mIdp = app.getInvariantDeviceProfile();
- mAppFilter = new AppFilter(app.getContext());
+ WidgetValidityCheckForPicker(InvariantDeviceProfile idp, AppFilter appFilter) {
+ mIdp = idp;
+ mAppFilter = appFilter;
}
@Override
diff --git a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
index 99f2837..7b8f218 100644
--- a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
+++ b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
@@ -18,6 +18,7 @@
import android.annotation.SuppressLint
import android.appwidget.AppWidgetProviderInfo
import android.content.ComponentName
+import android.content.Context
import android.content.Intent
import android.content.pm.LauncherApps
import android.content.pm.LauncherApps.ShortcutQuery
@@ -29,10 +30,10 @@
import android.util.LongSparseArray
import com.android.launcher3.Flags
import com.android.launcher3.InvariantDeviceProfile
-import com.android.launcher3.LauncherAppState
import com.android.launcher3.LauncherSettings.Favorites
import com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RestoreError
import com.android.launcher3.icons.CacheableShortcutInfo
+import com.android.launcher3.icons.IconCache
import com.android.launcher3.icons.cache.CacheLookupFlag.Companion.DEFAULT_LOOKUP_FLAG
import com.android.launcher3.logging.FileLog
import com.android.launcher3.model.data.AppInfo
@@ -70,7 +71,10 @@
private val launcherApps: LauncherApps,
private val pendingPackages: MutableSet<PackageUserKey>,
private val shortcutKeyToPinnedShortcuts: Map<ShortcutKey, ShortcutInfo>,
- private val app: LauncherAppState,
+ private val context: Context,
+ private val idp: InvariantDeviceProfile,
+ private val iconCache: IconCache,
+ private val isSafeMode: Boolean,
private val bgDataModel: BgDataModel,
private val widgetProvidersMap: MutableMap<ComponentKey, AppWidgetProviderInfo?>,
private val installingPkgs: HashMap<PackageUserKey, PackageInstaller.SessionInfo>,
@@ -82,9 +86,7 @@
private val allDeepShortcuts: MutableList<CacheableShortcutInfo>,
) {
- private val isSafeMode = app.isSafeModeEnabled
private val tempPackageKey = PackageUserKey(null, null)
- private val iconCache = app.iconCache
/**
* This is the entry point for processing 1 workspace item. This method is like the midfielder
@@ -159,7 +161,7 @@
)
return
}
- val appInfoWrapper = ApplicationInfoWrapper(app.context, targetPkg, c.user)
+ val appInfoWrapper = ApplicationInfoWrapper(context, targetPkg, c.user)
var validTarget = launcherApps.isPackageEnabled(targetPkg, c.user)
// If it's a deep shortcut, we'll use pinned shortcuts to restore it
@@ -306,7 +308,7 @@
)
return
}
- info = WorkspaceItemInfo(pinnedShortcut, app.context)
+ info = WorkspaceItemInfo(pinnedShortcut, context)
// If the pinned deep shortcut is no longer published,
// use the last saved icon instead of the default.
val csi = CacheableShortcutInfo(pinnedShortcut, appInfoWrapper)
@@ -369,7 +371,7 @@
info,
activityInfo,
userCache.getUserInfo(c.user),
- ApiWrapper.INSTANCE[app.context],
+ ApiWrapper.INSTANCE[context],
pmHelper,
)
}
@@ -529,7 +531,7 @@
(si == null) &&
(lapi == null) &&
!(Flags.enableSupportForArchiving() &&
- ApplicationInfoWrapper(app.context, component.packageName, c.user)
+ ApplicationInfoWrapper(context, component.packageName, c.user)
.isArchived())
) {
// Restore never started
@@ -553,7 +555,7 @@
if (si == null) 0 else (si.getProgress() * 100).toInt()
appWidgetInfo.pendingItemInfo =
WidgetsModel.newPendingItemInfo(
- app.context,
+ context,
appWidgetInfo.providerName,
appWidgetInfo.user,
)
@@ -563,7 +565,7 @@
WidgetSizes.updateWidgetSizeRangesAsync(
appWidgetInfo.appWidgetId,
lapi,
- app.context,
+ context,
appWidgetInfo.spanX,
appWidgetInfo.spanY,
)
@@ -586,7 +588,7 @@
" appWidgetId: ${c.appWidgetId}," +
" component=${component}",
)
- logWidgetInfo(app.invariantDeviceProfile, lapi)
+ logWidgetInfo(idp, lapi)
}
}
c.checkAndAddItem(appWidgetInfo, bgDataModel, memoryLogger)
diff --git a/src/com/android/launcher3/model/WorkspaceItemSpaceFinder.java b/src/com/android/launcher3/model/WorkspaceItemSpaceFinder.java
index 1a6d178..17f1615 100644
--- a/src/com/android/launcher3/model/WorkspaceItemSpaceFinder.java
+++ b/src/com/android/launcher3/model/WorkspaceItemSpaceFinder.java
@@ -21,7 +21,7 @@
import android.util.LongSparseArray;
import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.ItemInfo;
@@ -31,23 +31,37 @@
import java.util.ArrayList;
+import javax.inject.Inject;
+
/**
* Utility class to help find space for new workspace items
*/
public class WorkspaceItemSpaceFinder {
+ private BgDataModel mDataModel;
+ private InvariantDeviceProfile mIDP;
+ private LauncherModel mModel;
+
+ @Inject
+ WorkspaceItemSpaceFinder(
+ BgDataModel dataModel, InvariantDeviceProfile idp, LauncherModel model) {
+ mDataModel = dataModel;
+ mIDP = idp;
+ mModel = model;
+ }
+
/**
* Find a position on the screen for the given size or adds a new screen.
*
* @return screenId and the coordinates for the item in an int array of size 3.
*/
- public int[] findSpaceForItem(LauncherAppState app, BgDataModel dataModel,
+ public int[] findSpaceForItem(
IntArray workspaceScreens, IntArray addedWorkspaceScreensFinal, int spanX, int spanY) {
LongSparseArray<ArrayList<ItemInfo>> screenItems = new LongSparseArray<>();
// Use sBgItemsIdMap as all the items are already loaded.
- synchronized (dataModel) {
- for (ItemInfo info : dataModel.itemsIdMap) {
+ synchronized (mDataModel) {
+ for (ItemInfo info : mDataModel.itemsIdMap) {
if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
ArrayList<ItemInfo> items = screenItems.get(info.screenId);
if (items == null) {
@@ -75,7 +89,7 @@
for (int screen = 0; screen < screenCount; screen++) {
screenId = workspaceScreens.get(screen);
if (!screensToExclude.contains(screenId) && findNextAvailableIconSpaceInScreen(
- app, screenItems.get(screenId), coordinates, spanX, spanY)) {
+ screenItems.get(screenId), coordinates, spanX, spanY)) {
// We found a space for it
found = true;
break;
@@ -84,7 +98,7 @@
if (!found) {
// Still no position found. Add a new screen to the end.
- screenId = app.getModel().getModelDbController().getNewScreenId();
+ screenId = mModel.getModelDbController().getNewScreenId();
// Save the screen id for binding in the workspace
workspaceScreens.add(screenId);
@@ -92,7 +106,7 @@
// If we still can't find an empty space, then God help us all!!!
if (!findNextAvailableIconSpaceInScreen(
- app, screenItems.get(screenId), coordinates, spanX, spanY)) {
+ screenItems.get(screenId), coordinates, spanX, spanY)) {
throw new RuntimeException("Can't find space to add the item");
}
}
@@ -100,11 +114,8 @@
}
private boolean findNextAvailableIconSpaceInScreen(
- LauncherAppState app, ArrayList<ItemInfo> occupiedPos,
- int[] xy, int spanX, int spanY) {
- InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
-
- GridOccupancy occupied = new GridOccupancy(profile.numColumns, profile.numRows);
+ ArrayList<ItemInfo> occupiedPos, int[] xy, int spanX, int spanY) {
+ GridOccupancy occupied = new GridOccupancy(mIDP.numColumns, mIDP.numRows);
if (occupiedPos != null) {
for (ItemInfo r : occupiedPos) {
occupied.markCells(r, true);
diff --git a/src/com/android/launcher3/model/data/FolderInfo.java b/src/com/android/launcher3/model/data/FolderInfo.java
index 9656ac1..4c792a7 100644
--- a/src/com/android/launcher3/model/data/FolderInfo.java
+++ b/src/com/android/launcher3/model/data/FolderInfo.java
@@ -20,6 +20,9 @@
import static androidx.core.util.Preconditions.checkNotNull;
+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.logger.LauncherAtom.Attribute.EMPTY_LABEL;
import static com.android.launcher3.logger.LauncherAtom.Attribute.MANUAL_LABEL;
import static com.android.launcher3.logger.LauncherAtom.Attribute.SUGGESTED_LABEL;
@@ -30,8 +33,6 @@
import androidx.annotation.Nullable;
import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderNameInfos;
import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.logger.LauncherAtom.Attribute;
@@ -42,8 +43,6 @@
import com.android.launcher3.util.ContentWriter;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
import java.util.OptionalInt;
import java.util.stream.IntStream;
@@ -52,18 +51,6 @@
*/
public class FolderInfo extends CollectionInfo {
- public static final int NO_FLAGS = 0x00000000;
-
- /**
- * The folder is locked in sorted mode
- */
- public static final int FLAG_ITEMS_SORTED = 0x00000001;
-
- /**
- * It is a work folder
- */
- public static final int FLAG_WORK_FOLDER = 0x00000002;
-
/**
* The multi-page animation has run for this folder
*/
@@ -95,8 +82,6 @@
}
}
- public static final String EXTRA_FOLDER_SUGGESTIONS = "suggest";
-
public int options;
public FolderNameInfos suggestedFolderNames;
@@ -106,61 +91,16 @@
*/
private final ArrayList<ItemInfo> contents = new ArrayList<>();
- private ArrayList<FolderListener> mListeners = new ArrayList<>();
-
public FolderInfo() {
itemType = LauncherSettings.Favorites.ITEM_TYPE_FOLDER;
}
- /** Adds a app or shortcut to the contents ArrayList without animation. */
@Override
public void add(@NonNull ItemInfo item) {
- add(item, false /* animate */);
- }
-
- /**
- * Add an app or shortcut
- *
- * @param item
- */
- public void add(ItemInfo item, boolean animate) {
- add(item, getContents().size(), animate);
- }
-
- /**
- * Add an app or shortcut for a specified rank.
- */
- public void add(ItemInfo item, int rank, boolean animate) {
- if (!Folder.willAccept(item)) {
+ if (!willAcceptItemType(item.itemType)) {
throw new RuntimeException("tried to add an illegal type into a folder");
}
-
- rank = Utilities.boundToRange(rank, 0, getContents().size());
- getContents().add(rank, item);
- for (int i = 0; i < mListeners.size(); i++) {
- mListeners.get(i).onAdd(item, rank);
- }
- itemsChanged(animate);
- }
-
- /**
- * Remove an app or shortcut. Does not change the DB.
- *
- * @param item
- */
- public void remove(ItemInfo item, boolean animate) {
- removeAll(Collections.singletonList(item), animate);
- }
-
- /**
- * Remove all matching app or shortcut. Does not change the DB.
- */
- public void removeAll(List<ItemInfo> items, boolean animate) {
- contents.removeAll(items);
- for (int i = 0; i < mListeners.size(); i++) {
- mListeners.get(i).onRemove(items);
- }
- itemsChanged(animate);
+ getContents().add(item);
}
/**
@@ -197,28 +137,6 @@
writer.put(LauncherSettings.Favorites.OPTIONS, options);
}
- public void addListener(FolderListener listener) {
- mListeners.add(listener);
- }
-
- public void removeListener(FolderListener listener) {
- mListeners.remove(listener);
- }
-
- public void itemsChanged(boolean animate) {
- for (int i = 0; i < mListeners.size(); i++) {
- mListeners.get(i).onItemsChanged(animate);
- }
- }
-
- public interface FolderListener {
- void onAdd(ItemInfo item, int rank);
- void onRemove(List<ItemInfo> item);
- void onItemsChanged(boolean animate);
- void onTitleChanged(CharSequence title);
-
- }
-
public boolean hasOption(int optionFlag) {
return (options & optionFlag) != 0;
}
@@ -261,7 +179,6 @@
.build();
}
- @Override
public void setTitle(@Nullable CharSequence title, ModelWriter modelWriter) {
// Updating label from null to empty is considered as false touch.
// Retaining null title(ie., UNLABELED state) allows auto-labeling when new items added.
@@ -289,10 +206,6 @@
if (modelWriter != null) {
modelWriter.updateItemInDatabase(this);
}
-
- for (int i = 0; i < mListeners.size(); i++) {
- mListeners.get(i).onTitleChanged(title);
- }
}
/**
@@ -401,4 +314,13 @@
}
return LauncherAtom.ToState.TO_STATE_UNSPECIFIED;
}
+
+ /**
+ * Checks if {@code itemType} is a type that can be placed in folders.
+ */
+ public static boolean willAcceptItemType(int itemType) {
+ return itemType == ITEM_TYPE_APPLICATION
+ || itemType == ITEM_TYPE_DEEP_SHORTCUT
+ || itemType == ITEM_TYPE_APP_PAIR;
+ }
}
diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java
index 588e759..ad7696c 100644
--- a/src/com/android/launcher3/model/data/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -61,7 +61,6 @@
import com.android.launcher3.logger.LauncherAtom.TaskSwitcherContainer;
import com.android.launcher3.logger.LauncherAtom.WallpapersContainer;
import com.android.launcher3.logger.LauncherAtomExtensions.ExtendedContainers;
-import com.android.launcher3.model.ModelWriter;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.ContentWriter;
@@ -536,14 +535,6 @@
}
/**
- * Sets the title of the item and writes to DB model if needed.
- */
- public void setTitle(@Nullable final CharSequence title,
- @Nullable final ModelWriter modelWriter) {
- this.title = title;
- }
-
- /**
* Returns a string ID that is stable for a user session, but may not be persisted
*/
@Nullable
diff --git a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
index b60b8cc..f5e5e16 100644
--- a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
+++ b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
@@ -26,6 +26,7 @@
import androidx.annotation.Nullable;
import com.android.launcher3.Flags;
+import com.android.launcher3.Utilities;
import com.android.launcher3.graphics.ThemeManager;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.BitmapInfo.DrawableCreationFlags;
@@ -325,10 +326,12 @@
* Returns a FastBitmapDrawable with the icon and context theme applied
*/
public FastBitmapDrawable newIcon(Context context, @DrawableCreationFlags int creationFlags) {
- if (!ThemeManager.INSTANCE.get(context).isIconThemeEnabled()) {
+ ThemeManager themeManager = ThemeManager.INSTANCE.get(context);
+ if (!themeManager.isIconThemeEnabled()) {
creationFlags &= ~FLAG_THEMED;
}
- FastBitmapDrawable drawable = bitmap.newIcon(context, creationFlags);
+ FastBitmapDrawable drawable = bitmap.newIcon(
+ context, creationFlags, Utilities.getIconShapeOrNull(context));
drawable.setIsDisabled(isDisabled());
return drawable;
}
diff --git a/src/com/android/launcher3/model/data/TaskItemInfo.kt b/src/com/android/launcher3/model/data/TaskItemInfo.kt
index fc1cd4d..8b72835 100644
--- a/src/com/android/launcher3/model/data/TaskItemInfo.kt
+++ b/src/com/android/launcher3/model/data/TaskItemInfo.kt
@@ -17,8 +17,7 @@
package com.android.launcher3.model.data
/**
- * Temporary class holding a Task ID to allow us to reference a Task when clicking a hotseat item.
- *
- * TODO(b/315344726): Remove this class when we have proper Taskbar support for multi-instance apps
+ * A Task info class holding a Task ID to allow us to reference a Task when clicking a hotseat item.
+ * This is also used to help identify the shortcuts shown in the long-press menu.
*/
class TaskItemInfo(val taskId: Int, itemInfo: WorkspaceItemInfo) : WorkspaceItemInfo(itemInfo)
diff --git a/src/com/android/launcher3/pageindicators/PageIndicator.java b/src/com/android/launcher3/pageindicators/PageIndicator.java
index 0640bf3..a6f76c4 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicator.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicator.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.pageindicators;
+import java.util.function.Consumer;
+
/**
* Base class for a page indicator.
*/
@@ -27,6 +29,14 @@
void setMarkersCount(int numMarkers);
/**
+ * This is only going to be used by the FolderPagedView's PageIndicator. A refactor is planned
+ * to separate the two purposes of this class, but in the meantime, this indicator will serve to
+ * let the folder snap to the page of its click, and also tell the PageIndicator not to draw
+ * arrows if the click listener is null (at least until after this is refactored).
+ */
+ void setArrowClickListener(Consumer<Direction> listener);
+
+ /**
* Sets a flag indicating whether to pause scroll.
* <p>Should be set to {@code true} while the screen is binding or new data is being applied,
* and to {@code false} once done. This prevents animation conflicts due to scrolling during
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorArrowClickListener.kt b/src/com/android/launcher3/pageindicators/PageIndicatorArrowClickListener.kt
new file mode 100644
index 0000000..970d210
--- /dev/null
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorArrowClickListener.kt
@@ -0,0 +1,26 @@
+/*
+ * 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.pageindicators
+
+interface PageIndicatorArrowClickListener {
+ fun onArrowClick(direction: Direction)
+}
+
+enum class Direction {
+ END,
+ START,
+}
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
index 37f5189..384f876 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
@@ -32,11 +32,13 @@
import android.graphics.Paint.Style;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.graphics.drawable.VectorDrawable;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.IntProperty;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewOutlineProvider;
@@ -51,9 +53,14 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.util.Themes;
+import java.util.function.Consumer;
+
/**
* {@link PageIndicator} which shows dots per page. The active page is shown with the current
* accent color.
+ * <p>
+ * TODO(b/402258632): Split PageIndicatorDots into 2 different classes: FolderPageIndicator &
+ * WorkspacePageIndicator. A lot of the functionality in this class is only used by one UI purpose.
*/
public class PageIndicatorDots extends View implements Insettable, PageIndicator {
@@ -68,6 +75,12 @@
private static final int ENTER_ANIMATION_STAGGERED_DELAY = 150;
private static final int ENTER_ANIMATION_DURATION = 400;
+ private static final int LARGE_HEIGHT_MULTIPLIER = 12;
+ private static final int SMALL_HEIGHT_MULTIPLIER = 4;
+ private static final int LARGE_WIDTH_MULTIPLIER = 5;
+ private static final int SMALL_WIDTH_MULTIPLIER = 3;
+ private static final float ARROW_TOUCH_BOX_FACTOR = 5f;
+
private static final int PAGE_INDICATOR_ALPHA = 255;
private static final int DOT_ALPHA = 128;
private static final float DOT_ALPHA_FRACTION = 0.5f;
@@ -75,12 +88,14 @@
private static final int VISIBLE_ALPHA = 255;
private static final int INVISIBLE_ALPHA = 0;
private Paint mPaginationPaint;
+ private Consumer<Direction> mOnArrowClickListener;
// This value approximately overshoots to 1.5 times the original size.
private static final float ENTER_ANIMATION_OVERSHOOT_TENSION = 4.9f;
// This is used to optimize the onDraw method by not constructing a new RectF each draw.
private static final RectF sTempRect = new RectF();
+ private static final RectF sLastActiveRect = new RectF();
private static final FloatProperty<PageIndicatorDots> CURRENT_POSITION =
new FloatProperty<PageIndicatorDots>("current_position") {
@@ -99,23 +114,27 @@
private static final IntProperty<PageIndicatorDots> PAGINATION_ALPHA =
new IntProperty<PageIndicatorDots>("pagination_alpha") {
- @Override
- public Integer get(PageIndicatorDots obj) {
- return obj.mPaginationPaint.getAlpha();
- }
+ @Override
+ public Integer get(PageIndicatorDots obj) {
+ return obj.mPaginationPaint.getAlpha();
+ }
- @Override
- public void setValue(PageIndicatorDots obj, int alpha) {
- obj.mPaginationPaint.setAlpha(alpha);
- obj.invalidate();
- }
- };
+ @Override
+ public void setValue(PageIndicatorDots obj, int alpha) {
+ obj.mPaginationPaint.setAlpha(alpha);
+ obj.invalidate();
+ }
+ };
private final Handler mDelayedPaginationFadeHandler = new Handler(Looper.getMainLooper());
private final float mDotRadius;
private final float mGapWidth;
private final float mCircleGap;
private final boolean mIsRtl;
+ private final VectorDrawable mArrowRight;
+ private final VectorDrawable mArrowLeft;
+ private final Rect mArrowRightBounds = new Rect();
+ private final Rect mArrowLeftBounds = new Rect();
private int mNumPages;
private int mActivePage;
@@ -167,6 +186,8 @@
: DOT_GAP_FACTOR * mDotRadius;
setOutlineProvider(new MyOutlineProver());
mIsRtl = Utilities.isRtl(getResources());
+ mArrowRight = (VectorDrawable) getResources().getDrawable(R.drawable.ic_chevron_end);
+ mArrowLeft = (VectorDrawable) getResources().getDrawable(R.drawable.ic_chevron_start);
}
@Override
@@ -405,6 +426,11 @@
}
@Override
+ public void setArrowClickListener(Consumer<Direction> listener) {
+ mOnArrowClickListener = listener;
+ }
+
+ @Override
public void setPauseScroll(boolean pause, boolean isTwoPanels) {
mIsTwoPanels = isTwoPanels;
@@ -419,11 +445,16 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO(b/394355070): Verify Folder Entry Animation works correctly with visual updates
- // Add extra spacing of mDotRadius on all sides so than entry animation could be run.
+ // Add extra spacing of mDotRadius on all sides so than entry animation could be run
+ // and so the hitboxes of arrows can be clicked easier.
int width = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY ?
- MeasureSpec.getSize(widthMeasureSpec) : (int) ((mNumPages * 3 + 2) * mDotRadius);
+ MeasureSpec.getSize(widthMeasureSpec)
+ : (int) ((mNumPages * ((enableLauncherVisualRefresh())
+ ? LARGE_WIDTH_MULTIPLIER : SMALL_WIDTH_MULTIPLIER) + 2) * mDotRadius);
int height = MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY
- ? MeasureSpec.getSize(heightMeasureSpec) : (int) (4 * mDotRadius);
+ ? MeasureSpec.getSize(heightMeasureSpec)
+ : (int) (((enableLauncherVisualRefresh())
+ ? LARGE_HEIGHT_MULTIPLIER : SMALL_HEIGHT_MULTIPLIER) * mDotRadius);
setMeasuredDimension(width, height);
}
@@ -443,18 +474,51 @@
float y = getHeight() / 2;
if (mEntryAnimationRadiusFactors != null) {
- // During entry animation, only draw the circles
- // TODO(b/394355070): Verify Folder Entry Animation works correctly - visual updates
+ if (enableLauncherVisualRefresh()) {
+ x -= mDotRadius;
+ if (mIsRtl) {
+ x = getWidth() - x;
+ circleGap = -circleGap;
+ }
+ sTempRect.top = y - mDotRadius;
+ sTempRect.bottom = y + mDotRadius;
- if (mIsRtl) {
- x = getWidth() - x;
- circleGap = -circleGap;
- }
- for (int i = 0; i < mEntryAnimationRadiusFactors.length; i++) {
- mPaginationPaint.setAlpha(i == mActivePage ? PAGE_INDICATOR_ALPHA : DOT_ALPHA);
- canvas.drawCircle(x, y, mDotRadius * mEntryAnimationRadiusFactors[i],
- mPaginationPaint);
- x += circleGap;
+ for (int i = 0; i < mEntryAnimationRadiusFactors.length; i++) {
+ if (i == mActivePage) {
+ if (mIsRtl) {
+ sTempRect.left = x - (mDotRadius * 3);
+ sTempRect.right = x + mDotRadius;
+ x += circleGap - (mDotRadius * 2);
+ } else {
+ sTempRect.left = x - mDotRadius;
+ sTempRect.right = x + (mDotRadius * 3);
+ x += circleGap + (mDotRadius * 2);
+ }
+ scale(sTempRect, mEntryAnimationRadiusFactors[i]);
+ float scaledRadius = mDotRadius * mEntryAnimationRadiusFactors[i];
+ mPaginationPaint.setAlpha(PAGE_INDICATOR_ALPHA);
+ canvas.drawRoundRect(sTempRect, scaledRadius, scaledRadius,
+ mPaginationPaint);
+ } else {
+ mPaginationPaint.setAlpha(DOT_ALPHA);
+ canvas.drawCircle(x, y, mDotRadius * mEntryAnimationRadiusFactors[i],
+ mPaginationPaint);
+ x += circleGap;
+ }
+ }
+ } else {
+ // During entry animation, only draw the circles
+
+ if (mIsRtl) {
+ x = getWidth() - x;
+ circleGap = -circleGap;
+ }
+ for (int i = 0; i < mEntryAnimationRadiusFactors.length; i++) {
+ mPaginationPaint.setAlpha(i == mActivePage ? PAGE_INDICATOR_ALPHA : DOT_ALPHA);
+ canvas.drawCircle(x, y, mDotRadius * mEntryAnimationRadiusFactors[i],
+ mPaginationPaint);
+ x += circleGap;
+ }
}
} else {
// Save the current alpha value, so we can reset to it again after drawing the dots
@@ -468,12 +532,31 @@
sTempRect.bottom = y + mDotRadius;
sTempRect.left = x - diameter;
- float posDif = Math.abs(mLastPosition - mCurrentPosition);
+ float currentPosition = mCurrentPosition;
+ float lastPosition = mLastPosition;
+
+ if (mIsRtl) {
+ currentPosition = mNumPages - currentPosition - 1;
+ lastPosition = mNumPages - lastPosition - 1;
+ }
+ float posDif = Math.abs(lastPosition - currentPosition);
float boundedPosition = (posDif > 1)
- ? Math.round(mCurrentPosition)
- : mCurrentPosition;
+ ? Math.round(currentPosition)
+ : currentPosition;
float bounceProgress = (posDif > 1) ? posDif - 1 : 0;
- float bounceAdjustment = Math.abs(mCurrentPosition - boundedPosition) * diameter;
+ float bounceAdjustment = Math.abs(currentPosition - boundedPosition) * diameter;
+
+ if (mOnArrowClickListener != null && boundedPosition >= 1) {
+ // Here we draw the Left Arrow
+ mArrowLeft.setAlpha(alpha);
+ int size = (int) (mGapWidth * 4);
+ mArrowLeftBounds.left = (int) (sTempRect.left - mGapWidth - size);
+ mArrowLeftBounds.top = (int) (y - size / 2);
+ mArrowLeftBounds.right = (int) (sTempRect.left - mGapWidth);
+ mArrowLeftBounds.bottom = (int) (y + size / 2);
+ mArrowLeft.setBounds(mArrowLeftBounds);
+ mArrowLeft.draw(canvas);
+ }
// Here we draw the dots, one at a time from the left-most dot to the right-most dot
// 1.0 => 000000 000000111111 000000
@@ -495,10 +578,10 @@
// While the animation is shifting the active pagination dots size from
// the previously active one, to the newly active dot, there is no bounce
// adjustment. The bounce happens in the "Overshoot" phase of the animation.
- // mLastPosition is used to determine when the currentPosition is just
+ // lastPosition is used to determine when the currentPosition is just
// leaving the page, or if it is in the overshoot phase.
if (boundedPosition == i && bounceProgress != 0) {
- if (mLastPosition < mCurrentPosition) {
+ if (lastPosition < currentPosition) {
sTempRect.left -= bounceAdjustment;
} else {
sTempRect.right += bounceAdjustment;
@@ -507,19 +590,34 @@
} else {
sTempRect.right = sTempRect.left + diameter;
- if (mLastPosition == i && bounceProgress != 0) {
- if (mLastPosition > mCurrentPosition) {
+ if (lastPosition == i && bounceProgress != 0) {
+ if (lastPosition > currentPosition) {
sTempRect.left += bounceAdjustment;
} else {
sTempRect.right -= bounceAdjustment;
}
}
}
+ if (Math.round(mCurrentPosition) == i) {
+ sLastActiveRect.set(sTempRect);
+ }
canvas.drawRoundRect(sTempRect, mDotRadius, mDotRadius, mPaginationPaint);
// TODO(b/394355070) Verify RTL experience works correctly with visual updates
sTempRect.left = sTempRect.right + mGapWidth;
}
+
+ if (mOnArrowClickListener != null && boundedPosition <= mNumPages - 2) {
+ // Here we draw the Right Arrow
+ mArrowRight.setAlpha(alpha);
+ int size = (int) (mGapWidth * 4);
+ mArrowRightBounds.left = (int) sTempRect.left;
+ mArrowRightBounds.top = (int) (y - size / 2);
+ mArrowRightBounds.right = (int) (int) (sTempRect.left + size);
+ mArrowRightBounds.bottom = (int) (y + size / 2);
+ mArrowRight.setBounds(mArrowRightBounds);
+ mArrowRight.draw(canvas);
+ }
} else {
// Here we draw the dots
mPaginationPaint.setAlpha((int) (alpha * DOT_ALPHA_FRACTION));
@@ -538,6 +636,38 @@
}
}
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ if (mOnArrowClickListener == null) {
+ // No - Op. Don't care about touch events
+ } else if ((mIsRtl && withinExpandedBounds(mArrowRightBounds, ev))
+ || (!mIsRtl && withinExpandedBounds(mArrowLeftBounds, ev))) {
+ mOnArrowClickListener.accept(Direction.START);
+ } else if ((mIsRtl && withinExpandedBounds(mArrowLeftBounds, ev))
+ || (!mIsRtl && withinExpandedBounds(mArrowRightBounds, ev))) {
+ mOnArrowClickListener.accept(Direction.END);
+ }
+ return super.onTouchEvent(ev);
+ }
+
+ // For larger Touch box
+ private boolean withinExpandedBounds(Rect rect, MotionEvent ev) {
+ RectF scaledRect = new RectF(rect);
+ scale(scaledRect, ARROW_TOUCH_BOX_FACTOR);
+ return scaledRect.contains(ev.getX(), ev.getY());
+ }
+
+ private static void scale(RectF rect, float factor) {
+ float horizontalAdjustment = rect.width() * (factor - 1) / 2;
+ float verticalAdjustment = rect.height() * (factor - 1) / 2;
+
+ rect.top -= verticalAdjustment;
+ rect.bottom += verticalAdjustment;
+
+ rect.left -= horizontalAdjustment;
+ rect.right += horizontalAdjustment;
+ }
+
private RectF getActiveRect() {
float startCircle = (int) mCurrentPosition;
float delta = mCurrentPosition - startCircle;
@@ -590,8 +720,8 @@
@Override
public void getOutline(View view, Outline outline) {
if (mEntryAnimationRadiusFactors == null) {
- // TODO(b/394355070): Verify Outline works correctly with visual updates
- RectF activeRect = getActiveRect();
+ RectF activeRect = enableLauncherVisualRefresh()
+ ? sLastActiveRect : getActiveRect();
outline.setRoundRect(
(int) activeRect.left,
(int) activeRect.top,
diff --git a/src/com/android/launcher3/pm/UserCache.java b/src/com/android/launcher3/pm/UserCache.java
index 20c0ecc..98a3882 100644
--- a/src/com/android/launcher3/pm/UserCache.java
+++ b/src/com/android/launcher3/pm/UserCache.java
@@ -219,6 +219,6 @@
public static UserBadgeDrawable getBadgeDrawable(Context context, UserHandle userHandle) {
return (UserBadgeDrawable) BitmapInfo.LOW_RES_INFO.withFlags(UserCache.getInstance(context)
.getUserInfo(userHandle).applyBitmapInfoFlags(FlagOp.NO_OP))
- .getBadgeDrawable(context, false /* isThemed */);
+ .getBadgeDrawable(context, false /* isThemed */, null);
}
}
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index e52ca6d..39f68bf 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -16,9 +16,12 @@
package com.android.launcher3.popup;
+import static android.multiuser.Flags.enableMovingContentIntoPrivateSpace;
+
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SHORTCUTS;
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.Utilities.squaredTouchSlop;
+import static com.android.launcher3.allapps.AlphabeticalAppsList.PRIVATE_SPACE_PACKAGE;
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_NOT_PINNABLE;
import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
@@ -65,6 +68,7 @@
import com.android.launcher3.views.BaseDragLayer;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@@ -207,7 +211,10 @@
container = (PopupContainerWithArrow) launcher.getLayoutInflater().inflate(
R.layout.popup_container, launcher.getDragLayer(), false);
container.configureForLauncher(launcher, item);
- container.populateAndShowRows(icon, deepShortcutCount, systemShortcuts);
+ boolean shouldHideSystemShortcuts = enableMovingContentIntoPrivateSpace()
+ && Objects.equals(item.getTargetPackage(), PRIVATE_SPACE_PACKAGE);
+ container.populateAndShowRows(icon, deepShortcutCount,
+ shouldHideSystemShortcuts ? Collections.emptyList() : systemShortcuts);
launcher.refreshAndBindWidgetsForPackageUser(PackageUserKey.fromItemInfo(item));
container.requestFocus();
return container;
@@ -234,6 +241,20 @@
*/
public void populateAndShowRows(final BubbleTextView originalIcon,
int deepShortcutCount, List<SystemShortcut> systemShortcuts) {
+ populateAndShowRows(originalIcon, (ItemInfo) originalIcon.getTag(), deepShortcutCount,
+ systemShortcuts);
+ }
+
+ /**
+ * Populate and show shortcuts for the Launcher U app shortcut design.
+ * Will inflate the container and shortcut View instances for the popup container.
+ * @param originalIcon App icon that the popup is shown for
+ * @param itemInfo The info that is used to load app shortcuts
+ * @param deepShortcutCount Number of DeepShortcutView instances to add to container
+ * @param systemShortcuts List of SystemShortcuts to add to container
+ */
+ public void populateAndShowRows(final BubbleTextView originalIcon, ItemInfo itemInfo,
+ int deepShortcutCount, List<SystemShortcut> systemShortcuts) {
mOriginalIcon = originalIcon;
mContainerWidth = getResources().getDimensionPixelSize(R.dimen.bg_popup_item_width);
@@ -246,7 +267,7 @@
R.layout.system_shortcut);
}
show();
- loadAppShortcuts((ItemInfo) originalIcon.getTag());
+ loadAppShortcuts(itemInfo);
}
/**
diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java
index 318b3ce..9511032 100644
--- a/src/com/android/launcher3/popup/PopupDataProvider.java
+++ b/src/com/android/launcher3/popup/PopupDataProvider.java
@@ -26,7 +26,6 @@
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.allapps.ActivityAllAppsContainerView;
import com.android.launcher3.dot.DotInfo;
-import com.android.launcher3.dot.FolderDotInfo;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.model.data.FolderInfo;
@@ -76,11 +75,7 @@
((BubbleTextView) v).applyDotState(info, true /* animate */);
} else if (v instanceof FolderIcon icon
&& info instanceof FolderInfo fi && fi.anyMatch(matcher)) {
- FolderDotInfo folderDotInfo = new FolderDotInfo();
- for (ItemInfo si : fi.getContents()) {
- folderDotInfo.addDotInfo(getDotInfoForItem(si));
- }
- icon.setDotInfo(folderDotInfo);
+ icon.updateDotInfo();
}
// process all the shortcuts
@@ -90,7 +85,7 @@
mContext.getContent().mapOverItems(op);
Folder folder = Folder.getOpen(mContext);
if (folder != null) {
- folder.iterateOverItems(op);
+ folder.mapOverItems(op);
}
ActivityAllAppsContainerView<?> appsView = mContext.getAppsView();
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index 7e08c6e..b7efdec 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -210,30 +210,6 @@
}
}
- public static final Factory<ActivityContext> PIN_UNPIN_ITEM =
- (context, itemInfo, originalView) -> {
- // Predicted items use {@code HotseatPredictionController.PinPrediction} shortcut
- // to pin.
- if (itemInfo.isPredictedItem()) {
- return null;
- }
- return new PinUnpinItem<>(context, itemInfo, originalView);
- };
-
- private static class PinUnpinItem<T extends ActivityContext> extends SystemShortcut<T> {
- PinUnpinItem(T target, ItemInfo itemInfo, @NonNull View originalView) {
- // TODO(b/375648361): Check the pin state of the item to determine if the pin or the
- // unpin option should be used.
- super(R.drawable.ic_pin, R.string.pin_to_taskbar, target,
- itemInfo, originalView);
- }
-
- @Override
- public void onClick(View view) {
- // TODO(b/375648361): Pin/Unpin the item here.
- }
- }
-
public static final Factory<ActivityContext> PRIVATE_PROFILE_INSTALL =
(context, itemInfo, originalView) -> {
if (originalView == null) {
diff --git a/src/com/android/launcher3/provider/LauncherDbUtils.kt b/src/com/android/launcher3/provider/LauncherDbUtils.kt
index c92328d..3641371 100644
--- a/src/com/android/launcher3/provider/LauncherDbUtils.kt
+++ b/src/com/android/launcher3/provider/LauncherDbUtils.kt
@@ -26,19 +26,17 @@
import android.os.Process
import android.os.UserManager
import android.text.TextUtils
-import com.android.launcher3.LauncherAppState
import com.android.launcher3.LauncherSettings
import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP
import com.android.launcher3.Utilities
+import com.android.launcher3.dagger.LauncherComponentProvider.appComponent
import com.android.launcher3.icons.IconCache
-import com.android.launcher3.model.LoaderCursor
import com.android.launcher3.model.UserManagerState
import com.android.launcher3.pm.PinRequestHelper
import com.android.launcher3.pm.UserCache
import com.android.launcher3.shortcuts.ShortcutKey
import com.android.launcher3.util.IntArray
import com.android.launcher3.util.IntSet
-import com.android.launcher3.util.PackageManagerHelper
/** A set of utility methods for Launcher DB used for DB updates and migration. */
object LauncherDbUtils {
@@ -155,12 +153,11 @@
null,
null,
)
- val pmHelper = PackageManagerHelper.INSTANCE[context]
val ums = UserManagerState()
ums.run {
init(UserCache.INSTANCE[context], context.getSystemService(UserManager::class.java))
}
- val lc = LoaderCursor(c, LauncherAppState.getInstance(context), ums, pmHelper, null)
+ val lc = context.appComponent.loaderCursorFactory.createLoaderCursor(c, ums, null)
val deletedShortcuts = IntSet()
while (lc.moveToNext()) {
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
index fd8b0e7..c4fed71 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
@@ -94,27 +94,6 @@
mModel = LauncherAppState.getInstance(this).getModel();
mDragController = new SecondaryDragController(this);
mSecondaryDisplayPredictions = SecondaryDisplayPredictions.newInstance(this);
- if (getWindow().getDecorView().isAttachedToWindow()) {
- initUi();
- }
- }
-
- @Override
- public void onAttachedToWindow() {
- super.onAttachedToWindow();
- initUi();
- }
-
- @Override
- public void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- this.getDragController().removeDragListener(this);
- }
-
- private void initUi() {
- if (mDragLayer != null) {
- return;
- }
mDeviceProfile = InvariantDeviceProfile.INSTANCE.get(this)
.createDeviceProfileForSecondaryDisplay(this);
diff --git a/src/com/android/launcher3/statemanager/BaseState.java b/src/com/android/launcher3/statemanager/BaseState.java
index b7dd2bf..ea54159 100644
--- a/src/com/android/launcher3/statemanager/BaseState.java
+++ b/src/com/android/launcher3/statemanager/BaseState.java
@@ -69,6 +69,13 @@
}
/**
+ * For this state, whether we should show desktop exploded view in Overview.
+ */
+ default boolean showExplodedDesktopView() {
+ return false;
+ }
+
+ /**
* For this state, whether fullscreen and desktop quickswitch carousel are detached.
*/
default boolean detachDesktopCarousel() {
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index 943a913..e5105cd 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -214,6 +214,10 @@
ENABLE_TASKBAR_NAVBAR_UNIFICATION);
return response;
+ case TestProtocol.REQUEST_TASKBAR_SHOWN_ON_HOME:
+ response.putBoolean(TEST_INFO_RESPONSE_FIELD,
+ DisplayController.showLockedTaskbarOnHome(mContext));
+ return response;
case TestProtocol.REQUEST_NUM_ALL_APPS_COLUMNS:
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD,
mDeviceProfile.numShownAllAppsColumns);
diff --git a/src/com/android/launcher3/touch/AllAppsSwipeController.java b/src/com/android/launcher3/touch/AllAppsSwipeController.java
index 2cc4909..f86fd6a 100644
--- a/src/com/android/launcher3/touch/AllAppsSwipeController.java
+++ b/src/com/android/launcher3/touch/AllAppsSwipeController.java
@@ -22,6 +22,7 @@
import static com.android.app.animation.Interpolators.FINAL_FRAME;
import static com.android.app.animation.Interpolators.INSTANT;
import static com.android.app.animation.Interpolators.LINEAR;
+import static com.android.app.animation.Interpolators.clampToProgress;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
@@ -39,6 +40,7 @@
import com.android.app.animation.Interpolators;
import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.Flags;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.states.StateAnimationConfig;
@@ -53,10 +55,10 @@
private static final float ALL_APPS_SCRIM_VISIBLE_THRESHOLD = 0.1f;
private static final float ALL_APPS_STAGGERED_FADE_THRESHOLD = 0.5f;
- public static final Interpolator ALL_APPS_SCRIM_RESPONDER =
+ private static final Interpolator ALL_APPS_SCRIM_RESPONDER =
Interpolators.clampToProgress(
LINEAR, ALL_APPS_SCRIM_VISIBLE_THRESHOLD, ALL_APPS_STAGGERED_FADE_THRESHOLD);
- public static final Interpolator ALL_APPS_CLAMPING_RESPONDER =
+ private static final Interpolator ALL_APPS_CLAMPING_RESPONDER =
Interpolators.clampToProgress(
LINEAR,
1 - ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD,
@@ -207,7 +209,16 @@
}
config.setInterpolator(ANIM_WORKSPACE_SCALE, DECELERATED_EASE);
config.setInterpolator(ANIM_DEPTH, DECELERATED_EASE);
- if (launcher.getDeviceProfile().isPhone) {
+ if (Flags.allAppsBlur()) {
+ if (!config.isUserControlled()) {
+ config.setInterpolator(ANIM_DEPTH, EMPHASIZED_DECELERATE);
+ }
+ config.setInterpolator(ANIM_WORKSPACE_FADE,
+ clampToProgress(LINEAR, 1 - ALL_APPS_SCRIM_VISIBLE_THRESHOLD, 1));
+ config.setInterpolator(ANIM_HOTSEAT_FADE,
+ clampToProgress(LINEAR, 1 - ALL_APPS_SCRIM_VISIBLE_THRESHOLD, 1));
+ } else if (launcher.getDeviceProfile().isPhone) {
+ // On phones without blur, reveal the workspace and hotseat when leaving All Apps.
config.setInterpolator(ANIM_WORKSPACE_FADE, INSTANT);
config.setInterpolator(ANIM_HOTSEAT_FADE, INSTANT);
config.animFlags |= StateAnimationConfig.SKIP_DEPTH_CONTROLLER;
@@ -253,7 +264,14 @@
}
config.setInterpolator(ANIM_WORKSPACE_SCALE, DECELERATED_EASE);
config.setInterpolator(ANIM_DEPTH, DECELERATED_EASE);
- if (launcher.getDeviceProfile().isPhone) {
+ if (Flags.allAppsBlur()) {
+ config.setInterpolator(ANIM_DEPTH, LINEAR);
+ config.setInterpolator(ANIM_WORKSPACE_FADE,
+ clampToProgress(LINEAR, 0, ALL_APPS_SCRIM_VISIBLE_THRESHOLD));
+ config.setInterpolator(ANIM_HOTSEAT_FADE,
+ clampToProgress(LINEAR, 0, ALL_APPS_SCRIM_VISIBLE_THRESHOLD));
+ } else if (launcher.getDeviceProfile().isPhone) {
+ // On phones without blur, hide the workspace and hotseat when entering All Apps.
config.setInterpolator(ANIM_WORKSPACE_FADE, FINAL_FRAME);
config.setInterpolator(ANIM_HOTSEAT_FADE, FINAL_FRAME);
config.animFlags |= StateAnimationConfig.SKIP_DEPTH_CONTROLLER;
diff --git a/src/com/android/launcher3/touch/WorkspaceTouchListener.java b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
index d72e6f9..576f176 100644
--- a/src/com/android/launcher3/touch/WorkspaceTouchListener.java
+++ b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
@@ -131,7 +131,7 @@
}
boolean isInAllAppsBottomSheet = mLauncher.isInState(ALL_APPS)
- && mLauncher.getDeviceProfile().isTablet;
+ && mLauncher.getDeviceProfile().shouldShowAllAppsOnSheet();
final boolean result;
if (mLongPressState == STATE_COMPLETED) {
diff --git a/src/com/android/launcher3/util/BaseContext.kt b/src/com/android/launcher3/util/BaseContext.kt
new file mode 100644
index 0000000..8aa10f3
--- /dev/null
+++ b/src/com/android/launcher3/util/BaseContext.kt
@@ -0,0 +1,134 @@
+/*
+ * 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 android.content.Context
+import android.view.ContextThemeWrapper
+import android.view.View
+import android.view.View.OnAttachStateChangeListener
+import android.view.ViewTreeObserver.OnWindowFocusChangeListener
+import android.view.ViewTreeObserver.OnWindowVisibilityChangeListener
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.Lifecycle.State.CREATED
+import androidx.lifecycle.Lifecycle.State.DESTROYED
+import androidx.lifecycle.Lifecycle.State.RESUMED
+import androidx.lifecycle.Lifecycle.State.STARTED
+import androidx.lifecycle.LifecycleRegistry
+import androidx.lifecycle.setViewTreeLifecycleOwner
+import androidx.savedstate.SavedStateRegistry
+import androidx.savedstate.SavedStateRegistryController
+import androidx.savedstate.setViewTreeSavedStateRegistryOwner
+import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener
+import com.android.launcher3.Utilities
+import com.android.launcher3.views.ActivityContext
+
+/**
+ * A context wrapper with lifecycle tracking based on the window events on the rootView of the
+ * [ActivityContext]
+ */
+abstract class BaseContext
+@JvmOverloads
+constructor(base: Context, themeResId: Int, private val destroyOnDetach: Boolean = true) :
+ ContextThemeWrapper(base, themeResId), ActivityContext {
+
+ private val listeners = mutableListOf<OnDeviceProfileChangeListener>()
+
+ private val savedStateRegistryController = SavedStateRegistryController.create(this)
+ private val lifecycleRegistry = LifecycleRegistry(this)
+ private val cleanupSet = WeakCleanupSet(this)
+
+ override val savedStateRegistry: SavedStateRegistry
+ get() = savedStateRegistryController.savedStateRegistry
+
+ override val lifecycle: Lifecycle
+ get() = lifecycleRegistry
+
+ private val viewCache = ViewCache()
+
+ init {
+ Executors.MAIN_EXECUTOR.execute {
+ savedStateRegistryController.performAttach()
+ savedStateRegistryController.performRestore(null)
+ }
+ }
+
+ override fun getOnDeviceProfileChangeListeners() = listeners
+
+ private val finishActions = RunnableList()
+
+ /** Called when the root view is created for this context */
+ fun onViewCreated() {
+ val view = rootView
+ val attachListener =
+ object : OnAttachStateChangeListener {
+
+ override fun onViewAttachedToWindow(view: View) {
+ view.rootView.setViewTreeLifecycleOwner(this@BaseContext)
+ view.rootView.setViewTreeSavedStateRegistryOwner(this@BaseContext)
+ lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
+
+ val treeObserver = view.viewTreeObserver
+
+ val focusListener = OnWindowFocusChangeListener { updateState() }
+ treeObserver.addOnWindowFocusChangeListener(focusListener)
+ finishActions.add {
+ treeObserver.removeOnWindowFocusChangeListener(focusListener)
+ }
+
+ if (Utilities.ATLEAST_V) {
+ val visibilityListener = OnWindowVisibilityChangeListener { updateState() }
+ treeObserver.addOnWindowVisibilityChangeListener(visibilityListener)
+ finishActions.add {
+ treeObserver.removeOnWindowVisibilityChangeListener(visibilityListener)
+ }
+ }
+ }
+
+ override fun onViewDetachedFromWindow(view: View) {
+ if (destroyOnDetach) onViewDestroyed()
+ }
+ }
+ view.addOnAttachStateChangeListener(attachListener)
+ finishActions.add { view.removeOnAttachStateChangeListener(attachListener) }
+
+ if (view.isAttachedToWindow) attachListener.onViewAttachedToWindow(view)
+ updateState()
+ }
+
+ override fun getViewCache() = viewCache
+
+ override fun getOwnerCleanupSet() = cleanupSet
+
+ private fun updateState() {
+ if (lifecycleRegistry.currentState.isAtLeast(CREATED)) {
+ lifecycleRegistry.currentState =
+ if (rootView.windowVisibility != View.VISIBLE) CREATED
+ else (if (!rootView.hasWindowFocus()) STARTED else RESUMED)
+ }
+ }
+
+ fun onViewDestroyed() {
+ if (
+ !lifecycleRegistry.currentState.isAtLeast(CREATED) &&
+ lifecycleRegistry.currentState != DESTROYED
+ ) {
+ lifecycleRegistry.currentState = CREATED
+ }
+ lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
+ finishActions.executeAllAndDestroy()
+ }
+}
diff --git a/src/com/android/launcher3/util/ItemInflater.kt b/src/com/android/launcher3/util/ItemInflater.kt
index ebf4656..acb3c4e 100644
--- a/src/com/android/launcher3/util/ItemInflater.kt
+++ b/src/com/android/launcher3/util/ItemInflater.kt
@@ -24,6 +24,7 @@
import android.view.View.OnFocusChangeListener
import android.view.ViewGroup
import com.android.launcher3.BubbleTextView
+import com.android.launcher3.LauncherAppState
import com.android.launcher3.LauncherSettings.Favorites
import com.android.launcher3.R
import com.android.launcher3.apppairs.AppPairIcon
@@ -46,10 +47,11 @@
private val widgetHolder: LauncherWidgetHolder,
private val clickListener: OnClickListener,
private val focusListener: OnFocusChangeListener,
- private val defaultParent: ViewGroup
+ private val defaultParent: ViewGroup,
) where T : Context, T : ActivityContext {
- private val widgetInflater = WidgetInflater(context)
+ private val widgetInflater =
+ WidgetInflater(context, LauncherAppState.getInstance(context).isSafeModeEnabled)
@JvmOverloads
fun inflateItem(item: ItemInfo, writer: ModelWriter, nullableParent: ViewGroup? = null): View? {
@@ -75,7 +77,7 @@
R.layout.folder_icon,
context,
parent,
- item as FolderInfo
+ item as FolderInfo,
)
Favorites.ITEM_TYPE_APP_PAIR ->
return AppPairIcon.inflateIcon(
@@ -83,7 +85,7 @@
context,
parent,
item as AppPairInfo,
- BubbleTextView.DISPLAY_WORKSPACE
+ BubbleTextView.DISPLAY_WORKSPACE,
)
Favorites.ITEM_TYPE_APPWIDGET,
Favorites.ITEM_TYPE_CUSTOM_APPWIDGET ->
diff --git a/src/com/android/launcher3/util/LauncherBindableItemsContainer.kt b/src/com/android/launcher3/util/LauncherBindableItemsContainer.kt
index 1661796..8fdedef 100644
--- a/src/com/android/launcher3/util/LauncherBindableItemsContainer.kt
+++ b/src/com/android/launcher3/util/LauncherBindableItemsContainer.kt
@@ -25,8 +25,10 @@
import com.android.launcher3.model.data.FolderInfo
import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.model.data.WorkspaceItemInfo
+import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator
import com.android.launcher3.views.ActivityContext
import com.android.launcher3.widget.PendingAppWidgetHostView
+import java.util.function.Predicate
/** Interface representing a container which can bind Launcher items with some utility methods */
interface LauncherBindableItemsContainer {
@@ -54,11 +56,24 @@
}
mapOverItems(op)
- Folder.getOpen(context)?.iterateOverItems(op)
+ Folder.getOpen(context)?.mapOverItems(op)
}
- /** Map the [op] over the shortcuts and widgets. */
- fun mapOverItems(op: ItemOperator)
+ /** Returns the first view, matching the [op] */
+ @Deprecated("Use mapOverItems instead", ReplaceWith("mapOverItems(op)"))
+ fun getFirstMatch(op: ItemOperator): View? = mapOverItems(op)
+
+ /** Finds the first icon to match one of the given matchers, from highest to lowest priority. */
+ fun getFirstMatch(vararg matchers: Predicate<ItemInfo>): View? =
+ matchers.firstNotNullOfOrNull { mapOverItems { info, _ -> info != null && it.test(info) } }
+
+ fun getViewByItemId(id: Int): View? = mapOverItems { info, _ -> info != null && info.id == id }
+
+ /**
+ * Map the [op] over the shortcuts and widgets. Once we found the first view which matches, we
+ * will stop the iteration and return that view.
+ */
+ fun mapOverItems(op: ItemOperator): View?
fun interface ItemOperator {
diff --git a/src/com/android/launcher3/util/LifecycleHelper.kt b/src/com/android/launcher3/util/LifecycleHelper.kt
new file mode 100644
index 0000000..803adae
--- /dev/null
+++ b/src/com/android/launcher3/util/LifecycleHelper.kt
@@ -0,0 +1,69 @@
+/*
+ * 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 android.app.Activity
+import android.os.Bundle
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleRegistry
+import androidx.lifecycle.setViewTreeLifecycleOwner
+import androidx.savedstate.SavedStateRegistryController
+import androidx.savedstate.SavedStateRegistryOwner
+import androidx.savedstate.setViewTreeSavedStateRegistryOwner
+
+/** Utility class for triggering various lifecycle events based on activity callbacks */
+class LifecycleHelper(
+ private val owner: SavedStateRegistryOwner,
+ private val savedStateRegistryController: SavedStateRegistryController,
+ private val lifecycleRegistry: LifecycleRegistry,
+) : ActivityLifecycleCallbacksAdapter {
+
+ override fun onActivityPreCreated(activity: Activity, savedInstanceState: Bundle?) {
+ savedStateRegistryController.performRestore(savedInstanceState)
+ }
+
+ override fun onActivityPostCreated(activity: Activity, savedInstanceState: Bundle?) {
+ activity.window.decorView.setViewTreeLifecycleOwner(owner)
+ activity.window.decorView.setViewTreeSavedStateRegistryOwner(owner)
+ lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
+ }
+
+ override fun onActivityPostStarted(activity: Activity) {
+ lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START)
+ }
+
+ override fun onActivityPostResumed(activity: Activity) {
+ lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
+ }
+
+ override fun onActivityPrePaused(activity: Activity) {
+ lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE)
+ }
+
+ override fun onActivityPreStopped(activity: Activity) {
+ lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP)
+ }
+
+ override fun onActivityPreDestroyed(activity: Activity) {
+ lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
+ }
+
+ override fun onActivitySaveInstanceState(activity: Activity, bundle: Bundle) {
+ lifecycleRegistry.currentState = Lifecycle.State.CREATED
+ savedStateRegistryController.performSave(bundle)
+ }
+}
diff --git a/src/com/android/launcher3/util/SimpleBroadcastReceiver.java b/src/com/android/launcher3/util/SimpleBroadcastReceiver.java
index 7a40abe..53e0bce 100644
--- a/src/com/android/launcher3/util/SimpleBroadcastReceiver.java
+++ b/src/com/android/launcher3/util/SimpleBroadcastReceiver.java
@@ -123,6 +123,32 @@
}
}
+ /**
+ * Same as {@link #register(Runnable, int, String...)} above but with additional permission
+ * params utilizine the original {@link Context}.
+ */
+ @AnyThread
+ public void register(@Nullable Runnable completionCallback,
+ String broadcastPermission, int flags, String... actions) {
+ if (Looper.myLooper() == mHandler.getLooper()) {
+ registerInternal(mContext, completionCallback, broadcastPermission, flags, actions);
+ } else {
+ mHandler.post(() -> registerInternal(mContext, completionCallback, broadcastPermission,
+ flags, actions));
+ }
+ }
+
+ /** Register broadcast receiver with permission and run completion callback if passed. */
+ @AnyThread
+ private void registerInternal(
+ @NonNull Context context, @Nullable Runnable completionCallback,
+ String broadcastPermission, int flags, String... actions) {
+ context.registerReceiver(this, getFilter(actions), broadcastPermission, null, flags);
+ if (completionCallback != null) {
+ completionCallback.run();
+ }
+ }
+
/** Same as {@link #register(Runnable, String...)} above but with pkg name. */
@AnyThread
public void registerPkgActions(@Nullable String pkg, String... actions) {
diff --git a/src/com/android/launcher3/util/WeakCleanupSet.kt b/src/com/android/launcher3/util/WeakCleanupSet.kt
new file mode 100644
index 0000000..7bf3289
--- /dev/null
+++ b/src/com/android/launcher3/util/WeakCleanupSet.kt
@@ -0,0 +1,56 @@
+/*
+ * 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 androidx.lifecycle.DefaultLifecycleObserver
+import androidx.lifecycle.LifecycleOwner
+import com.android.launcher3.util.Executors.MAIN_EXECUTOR
+import java.util.Collections
+import java.util.WeakHashMap
+
+/**
+ * Utility class which maintains a list of cleanup callbacks using weak-references. These callbacks
+ * are called when the [owner] is destroyed, but can also be cleared when the caller is GCed
+ */
+class WeakCleanupSet(owner: LifecycleOwner) {
+
+ private val callbacks = Collections.newSetFromMap<OnOwnerDestroyedCallback>(WeakHashMap())
+ private var destroyed = false
+
+ init {
+ MAIN_EXECUTOR.execute {
+ owner.lifecycle.addObserver(
+ object : DefaultLifecycleObserver {
+
+ override fun onDestroy(owner: LifecycleOwner) {
+ destroyed = true
+ callbacks.forEach { it.onOwnerDestroyed() }
+ }
+ }
+ )
+ }
+ }
+
+ fun addOnOwnerDestroyedCallback(callback: OnOwnerDestroyedCallback) {
+ if (destroyed) callback.onOwnerDestroyed() else callbacks.add(callback)
+ }
+
+ /** Callback when the owner is destroyed */
+ interface OnOwnerDestroyedCallback {
+ fun onOwnerDestroyed()
+ }
+}
diff --git a/src/com/android/launcher3/util/window/WindowManagerProxy.java b/src/com/android/launcher3/util/window/WindowManagerProxy.java
index 11f0bc2..68e0324 100644
--- a/src/com/android/launcher3/util/window/WindowManagerProxy.java
+++ b/src/com/android/launcher3/util/window/WindowManagerProxy.java
@@ -520,6 +520,33 @@
*/
default void onCanCreateDesksChanged(boolean canCreateDesks) {
}
+
+ /**
+ * Called when a new desk is added.
+ *
+ * @param displayId The ID of the display on which the desk was added.
+ * @param deskId The ID of the newly added desk.
+ */
+ default void onDeskAdded(int displayId, int deskId) {}
+
+ /**
+ * Called when an existing desk is removed.
+ *
+ * @param displayId The ID of the display on which the desk was removed.
+ * @param deskId The ID of the desk that was removed.
+ */
+ default void onDeskRemoved(int displayId, int deskId) {}
+
+ /**
+ * Called when the active desk changes.
+ *
+ * @param displayId The ID of the display on which the desk activation change is happening.
+ * @param newActiveDesk The ID of the new active desk or -1 if no desk is active anymore
+ * (i.e. exit desktop mode).
+ * @param oldActiveDesk The ID of the desk that was previously active, or -1 if no desk was
+ * active before.
+ */
+ default void onActiveDeskChanged(int displayId, int newActiveDesk, int oldActiveDesk) {}
}
}
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index 30af586..cbf5341 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -56,6 +56,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.view.WindowInsetsCompat;
+import androidx.savedstate.SavedStateRegistryOwner;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DeviceProfile;
@@ -86,6 +87,7 @@
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.ViewCache;
+import com.android.launcher3.util.WeakCleanupSet;
import com.android.launcher3.widget.picker.model.WidgetPickerDataProvider;
import java.util.List;
@@ -94,7 +96,7 @@
* An interface to be used along with a context for various activities in Launcher. This allows a
* generic class to depend on Context subclass instead of an Activity.
*/
-public interface ActivityContext {
+public interface ActivityContext extends SavedStateRegistryOwner {
String TAG = "ActivityContext";
@@ -195,7 +197,7 @@
*/
@NonNull
default LauncherBindableItemsContainer getContent() {
- return op -> { };
+ return op -> null;
}
/**
@@ -229,9 +231,7 @@
getOnDeviceProfileChangeListeners().remove(listener);
}
- default ViewCache getViewCache() {
- return new ViewCache();
- }
+ ViewCache getViewCache();
/**
* Controller for supporting item drag-and-drop
@@ -521,6 +521,9 @@
return new CellPosMapper(dp.isVerticalBarLayout(), dp.numShownHotseatIcons);
}
+ /** Set to manage objects that can be cleaned up along with the context */
+ WeakCleanupSet getOwnerCleanupSet();
+
/** Whether bubbles are enabled. */
default boolean isBubbleBarEnabled() {
return false;
diff --git a/src/com/android/launcher3/views/ComposeInitializer.java b/src/com/android/launcher3/views/ComposeInitializer.java
deleted file mode 100644
index 0929885..0000000
--- a/src/com/android/launcher3/views/ComposeInitializer.java
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.views;
-
-import android.os.Build;
-import android.view.View;
-import android.view.ViewParent;
-import android.view.ViewTreeObserver;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RequiresApi;
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleOwner;
-import androidx.lifecycle.LifecycleRegistry;
-import androidx.lifecycle.ViewTreeLifecycleOwner;
-import androidx.savedstate.SavedStateRegistry;
-import androidx.savedstate.SavedStateRegistryController;
-import androidx.savedstate.SavedStateRegistryOwner;
-import androidx.savedstate.ViewTreeSavedStateRegistryOwner;
-
-import com.android.launcher3.Utilities;
-
-/**
- * An initializer to use Compose for classes implementing {@code ActivityContext}. This allows
- * adding ComposeView to ViewTree outside a {@link androidx.activity.ComponentActivity}.
- */
-public final class ComposeInitializer {
- /**
- * Performs the initialization to use Compose in the ViewTree of {@code target}.
- */
- public static void initCompose(ActivityContext target) {
- getContentChild(target).addOnAttachStateChangeListener(
- new View.OnAttachStateChangeListener() {
-
- @Override
- public void onViewAttachedToWindow(View v) {
- ComposeInitializer.onAttachedToWindow(v);
- }
-
- @Override
- public void onViewDetachedFromWindow(View v) {
- ComposeInitializer.onDetachedFromWindow(v);
- }
- });
- }
-
- /**
- * Find the "content child" for {@code target}.
- *
- * @see "WindowRecomposer.android.kt: [View.contentChild]"
- */
- private static View getContentChild(ActivityContext target) {
- View self = target.getDragLayer();
- ViewParent parent = self.getParent();
- while (parent instanceof View parentView) {
- if (parentView.getId() == android.R.id.content) return self;
- self = parentView;
- parent = self.getParent();
- }
- return self;
- }
-
- /**
- * Function to be called on your window root view's [View.onAttachedToWindow] function.
- */
- private static void onAttachedToWindow(View root) {
- if (ViewTreeLifecycleOwner.get(root) != null) {
- throw new IllegalStateException(
- "View " + root + " already has a LifecycleOwner");
- }
-
- ViewParent parent = root.getParent();
- if (parent instanceof View && ((View) parent).getId() != android.R.id.content) {
- throw new IllegalStateException(
- "ComposeInitializer.onContentChildAttachedToWindow(View) must be called on "
- + "the content child. Outside of activities and dialogs, this is "
- + "usually the top-most View of a window.");
- }
-
- // The lifecycle owner, which is STARTED when [root] is visible and RESUMED when [root]
- // is both visible and focused.
- ViewLifecycleOwner lifecycleOwner = new ViewLifecycleOwner(root);
-
- // We must call [ViewLifecycleOwner.onCreate] after creating the
- // [SavedStateRegistryOwner] because `onCreate` might move the lifecycle state to STARTED
- // which will make [SavedStateRegistryController.performRestore] throw.
- lifecycleOwner.onCreate();
-
- // Set the owners on the root. They will be reused by any ComposeView inside the root
- // hierarchy.
- ViewTreeLifecycleOwner.set(root, lifecycleOwner);
- ViewTreeSavedStateRegistryOwner.set(root, lifecycleOwner);
- }
-
- /**
- * Function to be called on your window root view's [View.onDetachedFromWindow] function.
- */
- private static void onDetachedFromWindow(View root) {
- final LifecycleOwner lifecycleOwner = ViewTreeLifecycleOwner.get(root);
- if (lifecycleOwner != null) {
- ((ViewLifecycleOwner) lifecycleOwner).onDestroy();
- }
- ViewTreeLifecycleOwner.set(root, null);
- ViewTreeSavedStateRegistryOwner.set(root, null);
- }
-
- /**
- * A [LifecycleOwner] for a [View] that updates lifecycle state based on window state.
- *
- * Also a trivial implementation of [SavedStateRegistryOwner] that does not do any save or
- * restore. This works for processes similar to the SystemUI process, which is always running
- * and top-level windows using this initialization are created once, when the process is
- * started.
- *
- * The implementation requires the caller to call [onCreate] and [onDestroy] when the view is
- * attached to or detached from a view hierarchy. After [onCreate] and before [onDestroy] is
- * called, the implementation monitors window state in the following way
- * * If the window is not visible, we are in the [Lifecycle.State.CREATED] state
- * * If the window is visible but not focused, we are in the [Lifecycle.State.STARTED] state
- * * If the window is visible and focused, we are in the [Lifecycle.State.RESUMED] state
- *
- * Or in table format:
- * ```
- * ┌───────────────┬───────────────────┬──────────────┬─────────────────┐
- * │ View attached │ Window Visibility │ Window Focus │ Lifecycle State │
- * ├───────────────┼───────────────────┴──────────────┼─────────────────┤
- * │ Not attached │ Any │ N/A │
- * ├───────────────┼───────────────────┬──────────────┼─────────────────┤
- * │ │ Not visible │ Any │ CREATED │
- * │ ├───────────────────┼──────────────┼─────────────────┤
- * │ Attached │ │ No focus │ STARTED │
- * │ │ Visible ├──────────────┼─────────────────┤
- * │ │ │ Has focus │ RESUMED │
- * └───────────────┴───────────────────┴──────────────┴─────────────────┘
- * ```
- */
- private static class ViewLifecycleOwner implements SavedStateRegistryOwner {
- private final ViewTreeObserver.OnWindowFocusChangeListener mWindowFocusListener =
- hasFocus -> updateState();
- private final LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
-
- private final SavedStateRegistryController mSavedStateRegistryController =
- SavedStateRegistryController.create(this);
-
- private final View mView;
- private final Api34Impl mApi34Impl;
-
- ViewLifecycleOwner(View view) {
- mView = view;
- if (Utilities.ATLEAST_U) {
- mApi34Impl = new Api34Impl();
- } else {
- mApi34Impl = null;
- }
-
- mSavedStateRegistryController.performRestore(null);
- }
-
- @NonNull
- @Override
- public Lifecycle getLifecycle() {
- return mLifecycleRegistry;
- }
-
- @NonNull
- @Override
- public SavedStateRegistry getSavedStateRegistry() {
- return mSavedStateRegistryController.getSavedStateRegistry();
- }
-
- void onCreate() {
- mLifecycleRegistry.setCurrentState(Lifecycle.State.CREATED);
- if (Utilities.ATLEAST_U) {
- mApi34Impl.addOnWindowVisibilityChangeListener();
- }
- mView.getViewTreeObserver().addOnWindowFocusChangeListener(
- mWindowFocusListener);
- updateState();
- }
-
- void onDestroy() {
- if (Utilities.ATLEAST_U) {
- mApi34Impl.removeOnWindowVisibilityChangeListener();
- }
- mView.getViewTreeObserver().removeOnWindowFocusChangeListener(
- mWindowFocusListener);
- mLifecycleRegistry.setCurrentState(Lifecycle.State.DESTROYED);
- }
-
- private void updateState() {
- Lifecycle.State state =
- mView.getWindowVisibility() != View.VISIBLE ? Lifecycle.State.CREATED
- : (!mView.hasWindowFocus() ? Lifecycle.State.STARTED
- : Lifecycle.State.RESUMED);
- mLifecycleRegistry.setCurrentState(state);
- }
-
- @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
- private class Api34Impl {
- private final ViewTreeObserver.OnWindowVisibilityChangeListener
- mWindowVisibilityListener =
- visibility -> updateState();
-
- void addOnWindowVisibilityChangeListener() {
- mView.getViewTreeObserver().addOnWindowVisibilityChangeListener(
- mWindowVisibilityListener);
- }
-
- void removeOnWindowVisibilityChangeListener() {
- mView.getViewTreeObserver().removeOnWindowVisibilityChangeListener(
- mWindowVisibilityListener);
- }
- }
- }
-}
diff --git a/src/com/android/launcher3/views/FloatingSurfaceView.java b/src/com/android/launcher3/views/FloatingSurfaceView.java
index 5f8e2c0..a4055b6 100644
--- a/src/com/android/launcher3/views/FloatingSurfaceView.java
+++ b/src/com/android/launcher3/views/FloatingSurfaceView.java
@@ -159,9 +159,8 @@
if (mContract == null) {
return;
}
- View icon = mLauncher.getFirstMatchForAppClose(null /* StableViewInfo */,
- mContract.componentName.getPackageName(), mContract.user,
- false /* supportsAllAppsState */);
+ View icon = mLauncher.getFirstHomeElementForAppClose(null /* StableViewInfo */,
+ mContract.componentName.getPackageName(), mContract.user);
boolean iconChanged = mIcon != icon;
if (iconChanged) {
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index 82cc40d..8c01c59 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -19,6 +19,7 @@
import static com.android.launcher3.LauncherState.EDIT_MODE;
import static com.android.launcher3.config.FeatureFlags.MULTI_SELECT_EDIT_MODE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALL_APPS_TAP_OR_LONGPRESS;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SETTINGS_BUTTON_TAP_OR_LONGPRESS;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_BUTTON_TAP_OR_LONGPRESS;
@@ -219,6 +220,11 @@
OptionsPopupView::enterHomeGardening));
}
options.add(new OptionItem(launcher,
+ R.string.all_apps_home_screen,
+ R.drawable.ic_apps,
+ LAUNCHER_ALL_APPS_TAP_OR_LONGPRESS,
+ OptionsPopupView::enterAllApps));
+ options.add(new OptionItem(launcher,
R.string.settings_button_text,
R.drawable.ic_setting,
LAUNCHER_SETTINGS_BUTTON_TAP_OR_LONGPRESS,
@@ -226,6 +232,20 @@
return options;
}
+ /**
+ * Used by the options to open All Apps, uses an intent as to not tie the implementation of
+ * opening All Apps with OptionsPopup, instead it uses the public API to open All Apps.
+ */
+ public static boolean enterAllApps(View view) {
+ Launcher launcher = Launcher.getLauncher(view.getContext());
+ launcher.startActivity(
+ new Intent(Intent.ACTION_ALL_APPS)
+ .setComponent(launcher.getComponentName())
+ .setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
+ );
+ return true;
+ }
+
private static boolean enterHomeGardening(View view) {
Launcher launcher = Launcher.getLauncher(view.getContext());
launcher.getStateManager().goToState(EDIT_MODE);
diff --git a/src/com/android/launcher3/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java
index ce58de1..ec71f31 100644
--- a/src/com/android/launcher3/views/ScrimView.java
+++ b/src/com/android/launcher3/views/ScrimView.java
@@ -54,8 +54,7 @@
}
@Override
- public void setInsets(Rect insets) {
- }
+ public void setInsets(Rect insets) {}
@Override
public boolean hasOverlappingRendering() {
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index fda5175..af31276 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -16,7 +16,6 @@
package com.android.launcher3.widget;
import static com.android.app.animation.Interpolators.EMPHASIZED;
-import static com.android.launcher3.Flags.enableWidgetTapToAdd;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGET_ADD_BUTTON_TAP;
@@ -150,40 +149,36 @@
return;
}
- if (enableWidgetTapToAdd()) {
- scrollToWidgetCell(wc);
+ scrollToWidgetCell(wc);
- if (mWidgetCellWithAddButton != null) {
- if (mWidgetCellWithAddButton.isShowingAddButton()) {
- // If there is a add button currently showing, hide it.
- mWidgetCellWithAddButton.hideAddButton(/* animate= */ true);
- } else {
- // The last recorded widget cell to show an add button is no longer showing it,
- // likely because the widget cell has been recycled or lost focus. If this is
- // the cell that has been clicked, we will show it below.
- mWidgetCellWithAddButton = null;
- }
- }
-
- if (mWidgetCellWithAddButton != wc) {
- // If click is on a cell not showing an add button, show it now.
- final PendingAddItemInfo info = (PendingAddItemInfo) wc.getTag();
- if (mActivityContext instanceof Launcher) {
- wc.showAddButton((view) -> addWidget(info));
- } else {
- wc.showAddButton((view) -> mActivityContext.getItemOnClickListener()
- .onClick(wc));
- }
- }
-
- mWidgetCellWithAddButton = mWidgetCellWithAddButton != wc ? wc : null;
- if (mWidgetCellWithAddButton != null) {
- mLastSelectedWidgetItem = mWidgetCellWithAddButton.getWidgetItem();
+ if (mWidgetCellWithAddButton != null) {
+ if (mWidgetCellWithAddButton.isShowingAddButton()) {
+ // If there is a add button currently showing, hide it.
+ mWidgetCellWithAddButton.hideAddButton(/* animate= */ true);
} else {
- mLastSelectedWidgetItem = null;
+ // The last recorded widget cell to show an add button is no longer showing it,
+ // likely because the widget cell has been recycled or lost focus. If this is
+ // the cell that has been clicked, we will show it below.
+ mWidgetCellWithAddButton = null;
}
+ }
+
+ if (mWidgetCellWithAddButton != wc) {
+ // If click is on a cell not showing an add button, show it now.
+ final PendingAddItemInfo info = (PendingAddItemInfo) wc.getTag();
+ if (mActivityContext instanceof Launcher) {
+ wc.showAddButton((view) -> addWidget(info));
+ } else {
+ wc.showAddButton((view) -> mActivityContext.getItemOnClickListener()
+ .onClick(wc));
+ }
+ }
+
+ mWidgetCellWithAddButton = mWidgetCellWithAddButton != wc ? wc : null;
+ if (mWidgetCellWithAddButton != null) {
+ mLastSelectedWidgetItem = mWidgetCellWithAddButton.getWidgetItem();
} else {
- mActivityContext.getItemOnClickListener().onClick(wc);
+ mLastSelectedWidgetItem = null;
}
}
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 7a27bf4..130843b 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -18,7 +18,6 @@
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
-import static com.android.launcher3.Flags.enableWidgetTapToAdd;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY;
import static com.android.launcher3.widget.util.WidgetSizes.getWidgetItemSizePx;
@@ -152,24 +151,22 @@
mWidgetTextContainer = findViewById(R.id.widget_text_container);
mWidgetAddButton = findViewById(R.id.widget_add_button);
- if (enableWidgetTapToAdd()) {
- setAccessibilityDelegate(new AccessibilityDelegate() {
- @Override
- public void onInitializeAccessibilityNodeInfo(View host,
- AccessibilityNodeInfo info) {
- super.onInitializeAccessibilityNodeInfo(host, info);
- if (hasOnClickListeners()) {
- String accessibilityLabel = getResources().getString(
- mWidgetAddButton.isShown()
- ? R.string.widget_cell_tap_to_hide_add_button_label
- : R.string.widget_cell_tap_to_show_add_button_label);
- info.addAction(new AccessibilityNodeInfo.AccessibilityAction(ACTION_CLICK,
- accessibilityLabel));
- }
+ setAccessibilityDelegate(new AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host,
+ AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ if (hasOnClickListeners()) {
+ String accessibilityLabel = getResources().getString(
+ mWidgetAddButton.isShown()
+ ? R.string.widget_cell_tap_to_hide_add_button_label
+ : R.string.widget_cell_tap_to_show_add_button_label);
+ info.addAction(new AccessibilityNodeInfo.AccessibilityAction(ACTION_CLICK,
+ accessibilityLabel));
}
- });
- mWidgetAddButton.setVisibility(INVISIBLE);
- }
+ }
+ });
+ mWidgetAddButton.setVisibility(INVISIBLE);
}
public void setRemoteViewsPreview(RemoteViews view) {
@@ -210,9 +207,7 @@
showDescription(true);
showDimensions(true);
- if (enableWidgetTapToAdd()) {
- hideAddButton(/* animate= */ false);
- }
+ hideAddButton(/* animate= */ false);
if (mActiveRequest != null) {
mActiveRequest.cancel();
diff --git a/src/com/android/launcher3/widget/WidgetInflater.kt b/src/com/android/launcher3/widget/WidgetInflater.kt
index d6cadc7..03f680a 100644
--- a/src/com/android/launcher3/widget/WidgetInflater.kt
+++ b/src/com/android/launcher3/widget/WidgetInflater.kt
@@ -19,14 +19,21 @@
import android.content.Context
import com.android.launcher3.BuildConfig
import com.android.launcher3.Launcher
-import com.android.launcher3.LauncherAppState
import com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RestoreError
+import com.android.launcher3.dagger.ApplicationContext
import com.android.launcher3.logging.FileLog
import com.android.launcher3.model.data.LauncherAppWidgetInfo
import com.android.launcher3.qsb.QsbContainerView
+import javax.inject.Inject
+import javax.inject.Named
/** Utility class for handling widget inflation taking into account all the restore state updates */
-class WidgetInflater(private val context: Context) {
+class WidgetInflater
+@Inject
+constructor(
+ @ApplicationContext private val context: Context,
+ @Named("SAFE_MODE") private val isSafeModeEnabled: Boolean,
+) {
private val widgetHelper = WidgetManagerHelper(context)
@@ -41,9 +48,8 @@
)
}
}
- if (LauncherAppState.INSTANCE.get(context).isSafeModeEnabled) {
- return InflationResult(TYPE_PENDING)
- }
+ if (isSafeModeEnabled) return InflationResult(TYPE_PENDING)
+
val appWidgetInfo: LauncherAppWidgetProviderInfo?
var removalReason = ""
@RestoreError var logReason = RestoreError.OTHER_WIDGET_INFLATION_FAIL
diff --git a/src/com/android/launcher3/widget/model/WidgetListSpaceEntry.java b/src/com/android/launcher3/widget/model/WidgetListSpaceEntry.java
index 5b1da5b..a761ecd 100644
--- a/src/com/android/launcher3/widget/model/WidgetListSpaceEntry.java
+++ b/src/com/android/launcher3/widget/model/WidgetListSpaceEntry.java
@@ -33,4 +33,9 @@
Collections.EMPTY_LIST);
mPkgItem.title = "";
}
+
+ @Override
+ public WidgetsListBaseEntry copy() {
+ return new WidgetListSpaceEntry();
+ }
}
diff --git a/src/com/android/launcher3/widget/model/WidgetsListBaseEntry.java b/src/com/android/launcher3/widget/model/WidgetsListBaseEntry.java
index 0003b76..9246e45 100644
--- a/src/com/android/launcher3/widget/model/WidgetsListBaseEntry.java
+++ b/src/com/android/launcher3/widget/model/WidgetsListBaseEntry.java
@@ -43,4 +43,7 @@
this.mWidgets =
items.stream().sorted(new WidgetItemComparator()).collect(Collectors.toList());
}
+
+
+ public abstract WidgetsListBaseEntry copy();
}
diff --git a/src/com/android/launcher3/widget/model/WidgetsListContentEntry.java b/src/com/android/launcher3/widget/model/WidgetsListContentEntry.java
index d709196..cc1739f 100644
--- a/src/com/android/launcher3/widget/model/WidgetsListContentEntry.java
+++ b/src/com/android/launcher3/widget/model/WidgetsListContentEntry.java
@@ -58,6 +58,11 @@
}
@Override
+ public WidgetsListBaseEntry copy() {
+ return new WidgetsListContentEntry(mPkgItem, mTitleSectionName, mWidgets, mMaxSpanSize);
+ }
+
+ @Override
public String toString() {
return "Content:" + mPkgItem.packageName + ":" + mWidgets.size() + " maxSpanSize: "
+ mMaxSpanSize;
diff --git a/src/com/android/launcher3/widget/model/WidgetsListExpandActionEntry.java b/src/com/android/launcher3/widget/model/WidgetsListExpandActionEntry.java
index 8c84030..7519bb7 100644
--- a/src/com/android/launcher3/widget/model/WidgetsListExpandActionEntry.java
+++ b/src/com/android/launcher3/widget/model/WidgetsListExpandActionEntry.java
@@ -35,4 +35,9 @@
/*items=*/ Collections.EMPTY_LIST);
mPkgItem.title = "";
}
+
+ @Override
+ public WidgetsListBaseEntry copy() {
+ return new WidgetsListExpandActionEntry();
+ }
}
diff --git a/src/com/android/launcher3/widget/model/WidgetsListHeaderEntry.java b/src/com/android/launcher3/widget/model/WidgetsListHeaderEntry.java
index 0d775c3..e2ea068 100644
--- a/src/com/android/launcher3/widget/model/WidgetsListHeaderEntry.java
+++ b/src/com/android/launcher3/widget/model/WidgetsListHeaderEntry.java
@@ -83,6 +83,12 @@
mIsWidgetListShown = isWidgetListShown;
}
+ @Override
+ public WidgetsListBaseEntry copy() {
+ return new WidgetsListHeaderEntry(mPkgItem, mTitleSectionName, mWidgets,
+ mVisibleWidgetsCount, mIsSearchEntry, mIsWidgetListShown);
+ }
+
/** Returns {@code true} if the widgets list associated with this header is shown. */
public boolean isWidgetListShown() {
return mIsWidgetListShown;
diff --git a/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java b/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java
index 4ccf16b..ec91622 100644
--- a/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java
+++ b/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java
@@ -16,6 +16,7 @@
package com.android.launcher3.widget.picker;
+import static com.android.launcher3.widget.picker.WidgetRecommendationCategory.DEFAULT_WIDGET_RECOMMENDATION_CATEGORY;
import static com.android.launcher3.widget.util.WidgetsTableUtils.groupWidgetItemsUsingRowPxWithReordering;
import android.content.ComponentName;
@@ -55,6 +56,7 @@
public final class WidgetRecommendationsView extends PagedView<PageIndicatorDots> {
private @Px float mAvailableHeight = Float.MAX_VALUE;
private @Px float mAvailableWidth = 0;
+ private int mLastUiMode = -1;
private static final String INITIALLY_DISPLAYED_WIDGETS_STATE_KEY =
"widgetRecommendationsView:mDisplayedWidgets";
private static final int MAX_CATEGORIES = 3;
@@ -151,41 +153,7 @@
mPageSwitchListeners.add(pageChangeListener);
}
- /**
- * Displays all the provided recommendations in a single table if they fit.
- *
- * @param recommendedWidgets list of widgets to be displayed in recommendation section.
- * @param deviceProfile the current {@link DeviceProfile}
- * @param availableHeight height in px that can be used to display the recommendations;
- * recommendations that don't fit in this height won't be shown
- * @param availableWidth width in px that the recommendations should display in
- * @param cellPadding padding in px that should be applied to each widget in the
- * recommendations
- * @return number of recommendations that could fit in the available space.
- */
- public int setRecommendations(
- List<WidgetItem> recommendedWidgets, DeviceProfile deviceProfile,
- final @Px float availableHeight, final @Px int availableWidth,
- final @Px int cellPadding) {
- this.mAvailableHeight = availableHeight;
- this.mAvailableWidth = availableWidth;
- clear();
-
- Set<ComponentName> displayedWidgets = maybeDisplayInTable(recommendedWidgets,
- deviceProfile,
- availableWidth, cellPadding);
-
- if (mDisplayedWidgets.isEmpty()) {
- // Save the widgets shown for the first time user opened the picker; so that, they can
- // be maintained across orientation changes.
- mDisplayedWidgets = displayedWidgets;
- }
-
- updateTitleAndIndicator(/* requestedPage= */ 0);
- return displayedWidgets.size();
- }
-
- private boolean shouldShowFullPageView(
+ private boolean shouldShowSinglePageView(
Map<WidgetRecommendationCategory, List<WidgetItem>> recommendations) {
if (mShowFullPageViewIfLowDensity) {
boolean hasLessCategories = recommendations.size() < MAX_CATEGORIES;
@@ -213,63 +181,82 @@
* @param cellPadding padding in px that should be applied to each widget in the
* recommendations
* @param requestedPage page number to display initially.
+ * @param forceUpdate whether to re-render even if available space didn't change
* @return number of recommendations that could fit in the available space.
*/
public int setRecommendations(
Map<WidgetRecommendationCategory, List<WidgetItem>> recommendations,
DeviceProfile deviceProfile, final @Px float availableHeight,
- final @Px int availableWidth, final @Px int cellPadding, final int requestedPage) {
- if (shouldShowFullPageView(recommendations)) {
- // Show all widgets in single page with unlimited available height.
- return setRecommendations(
- recommendations.values().stream().flatMap(Collection::stream)
- .collect(Collectors.toList()),
- deviceProfile, /*availableHeight=*/ Float.MAX_VALUE, availableWidth,
- cellPadding);
+ final @Px int availableWidth, final @Px int cellPadding, final int requestedPage,
+ final boolean forceUpdate) {
+ if (forceUpdate || shouldUpdate(availableWidth, availableHeight)) {
+ Context context = getContext();
+ this.mAvailableHeight = availableHeight;
+ this.mAvailableWidth = availableWidth;
+ this.mLastUiMode = context.getResources().getConfiguration().uiMode;
- }
- this.mAvailableHeight = availableHeight;
- this.mAvailableWidth = availableWidth;
- Context context = getContext();
- // For purpose of recommendations section, we don't want paging dots to be halved in two
- // pane display, so, we always provide isTwoPanels = "false".
- mPageIndicator.setPauseScroll(/*pause=*/true, /*isTwoPanels=*/ false);
- clear();
-
- int displayedCategories = 0;
- Set<ComponentName> allDisplayedWidgets = new HashSet<>();
-
- // Render top MAX_CATEGORIES in separate tables. Each table becomes a page.
- for (Map.Entry<WidgetRecommendationCategory, List<WidgetItem>> entry :
- new TreeMap<>(recommendations).entrySet()) {
- // If none of the recommendations for the category could fit in the mAvailableHeight, we
- // don't want to add that category; and we look for the next one.
- Set<ComponentName> displayedWidgetsForCategory = maybeDisplayInTable(entry.getValue(),
- deviceProfile,
- availableWidth, cellPadding);
- if (!displayedWidgetsForCategory.isEmpty()) {
- mCategoryTitles.add(
- context.getResources().getString(entry.getKey().categoryTitleRes));
- displayedCategories++;
- allDisplayedWidgets.addAll(displayedWidgetsForCategory);
+ final Map<WidgetRecommendationCategory, List<WidgetItem>> mappedRecommendations;
+ if (shouldShowSinglePageView(recommendations)) { // map to single category.
+ mappedRecommendations = Map.of(DEFAULT_WIDGET_RECOMMENDATION_CATEGORY,
+ recommendations.values().stream().flatMap(
+ Collection::stream).toList());
+ } else {
+ mappedRecommendations = recommendations;
}
- if (displayedCategories == MAX_CATEGORIES) {
- break;
+ // For purpose of recommendations section, we don't want paging dots to be halved in two
+ // pane display, so, we always provide isTwoPanels = "false".
+ mPageIndicator.setPauseScroll(/*pause=*/true, /*isTwoPanels=*/ false);
+ clear();
+
+ int displayedCategories = 0;
+ Set<ComponentName> allDisplayedWidgets = new HashSet<>();
+
+ // Render top MAX_CATEGORIES in separate tables. Each table becomes a page.
+ for (Map.Entry<WidgetRecommendationCategory, List<WidgetItem>> entry :
+ new TreeMap<>(mappedRecommendations).entrySet()) {
+ // If none of the recommendations for the category could fit in the
+ // mAvailableHeight, we don't want to add that category; and we look for the next
+ // one.
+ Set<ComponentName> displayedWidgetsForCategory = maybeDisplayInTable(
+ entry.getValue(),
+ deviceProfile,
+ availableWidth, cellPadding);
+ if (!displayedWidgetsForCategory.isEmpty()) {
+ mCategoryTitles.add(
+ context.getResources().getString(entry.getKey().categoryTitleRes));
+ displayedCategories++;
+ allDisplayedWidgets.addAll(displayedWidgetsForCategory);
+ }
+
+ if (displayedCategories == MAX_CATEGORIES) {
+ break;
+ }
}
- }
- if (mDisplayedWidgets.isEmpty()) {
- // Save the widgets shown for the first time user opened the picker; so that, they can
- // be maintained across orientation changes.
- mDisplayedWidgets = allDisplayedWidgets;
- }
+ if (mDisplayedWidgets.isEmpty()) {
+ // Save the widgets shown for the first time user opened the picker; so that,
+ // they can
+ // be maintained across orientation changes.
+ mDisplayedWidgets = allDisplayedWidgets;
+ }
- updateTitleAndIndicator(requestedPage);
- // For purpose of recommendations section, we don't want paging dots to be halved in two
- // pane display, so, we always provide isTwoPanels = "false".
- mPageIndicator.setPauseScroll(/*pause=*/false, /*isTwoPanels=*/false);
- return allDisplayedWidgets.size();
+ updateTitleAndIndicator(requestedPage);
+ // For purpose of recommendations section, we don't want paging dots to be halved in two
+ // pane display, so, we always provide isTwoPanels = "false".
+ mPageIndicator.setPauseScroll(/*pause=*/false, /*isTwoPanels=*/false);
+ return allDisplayedWidgets.size();
+ } else {
+ return mDisplayedWidgets.size();
+ }
+ }
+
+ /**
+ * Returns if we should re-render the views.
+ */
+ private boolean shouldUpdate(int availableWidth, float availableHeight) {
+ return this.mAvailableWidth != availableWidth || this.mAvailableHeight != availableHeight
+ || getContext().getResources().getConfiguration().uiMode != this.mLastUiMode;
}
private void clear() {
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 7a218ae..44c0ebd 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -15,9 +15,7 @@
*/
package com.android.launcher3.widget.picker;
-import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
import static com.android.launcher3.Flags.enableTieredWidgetsByDefaultInPicker;
-import static com.android.launcher3.Flags.enableUnfoldedTwoPanePicker;
import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.SEARCH;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_EXPAND_PRESS;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_SEARCHED;
@@ -82,12 +80,10 @@
import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.OnActivePageChangedListener;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
-import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
@@ -632,35 +628,22 @@
if (mIsInSearchMode) {
return;
}
- if (enableCategorizedWidgetSuggestions()) {
- // We avoid applying new recommendations when some are already displayed.
- if (mRecommendedWidgetsMap.isEmpty()) {
- mRecommendedWidgetsMap =
- mActivityContext.getWidgetPickerDataProvider().get().getRecommendations();
- }
- mRecommendedWidgetsCount = mWidgetRecommendationsView.setRecommendations(
- mRecommendedWidgetsMap,
- mDeviceProfile,
- /* availableHeight= */ getMaxAvailableHeightForRecommendations(),
- /* availableWidth= */ mMaxSpanPerRow,
- /* cellPadding= */ mWidgetCellHorizontalPadding,
- /* requestedPage= */ mRecommendationsCurrentPage
- );
- } else {
- if (mRecommendedWidgets.isEmpty()) {
- mRecommendedWidgets = mActivityContext.getWidgetPickerDataProvider().get()
- .getRecommendations()
- .values().stream()
- .flatMap(Collection::stream).collect(Collectors.toList());
- mRecommendedWidgetsCount = mWidgetRecommendationsView.setRecommendations(
- mRecommendedWidgets,
- mDeviceProfile,
- /* availableHeight= */ getMaxAvailableHeightForRecommendations(),
- /* availableWidth= */ mMaxSpanPerRow,
- /* cellPadding= */ mWidgetCellHorizontalPadding
- );
- }
+ boolean forceUpdate = false;
+ // We avoid applying new recommendations when some are already displayed.
+ if (mRecommendedWidgetsMap.isEmpty()) {
+ mRecommendedWidgetsMap =
+ mActivityContext.getWidgetPickerDataProvider().get().getRecommendations();
+ forceUpdate = true;
}
+ mRecommendedWidgetsCount = mWidgetRecommendationsView.setRecommendations(
+ mRecommendedWidgetsMap,
+ mDeviceProfile,
+ /* availableHeight= */ getMaxAvailableHeightForRecommendations(),
+ /* availableWidth= */ mMaxSpanPerRow,
+ /* cellPadding= */ mWidgetCellHorizontalPadding,
+ /* requestedPage= */ mRecommendationsCurrentPage,
+ /* forceUpdate= */ forceUpdate
+ );
mWidgetRecommendationsContainer.setVisibility(
mRecommendedWidgetsCount > 0 ? VISIBLE : GONE);
@@ -792,13 +775,7 @@
}
private static int getWidgetSheetId(BaseActivity activity) {
- boolean isTwoPane = (activity.getDeviceProfile().isTablet
- // Enables two pane picker for tablets in all orientations when the
- // enableCategorizedWidgetSuggestions flag is on.
- && (activity.getDeviceProfile().isLandscape || enableCategorizedWidgetSuggestions())
- && !activity.getDeviceProfile().isTwoPanels)
- // Enables two pane picker for unfolded foldables if the flag is on.
- || (activity.getDeviceProfile().isTwoPanels && enableUnfoldedTwoPanePicker());
+ boolean isTwoPane = activity.getDeviceProfile().isTablet;
return isTwoPane ? R.layout.widgets_two_pane_sheet : R.layout.widgets_full_sheet;
}
@@ -945,16 +922,7 @@
private static boolean shouldRecreateLayout(DeviceProfile oldDp, DeviceProfile newDp) {
// When folding/unfolding the foldables, we need to switch between the regular widget picker
// and the two pane picker, so we rebuild the picker with the correct layout.
- boolean isFoldUnFold =
- oldDp.isTwoPanels != newDp.isTwoPanels && enableUnfoldedTwoPanePicker();
- // In tablets, on orientation change we switch between single and two pane picker unless the
- // categorized suggestions flag was on. With the categorized suggestions feature, we use a
- // two pane picker across all orientations.
- boolean useDifferentLayoutOnOrientationChange =
- (!enableCategorizedWidgetSuggestions() && (newDp.isTablet && !newDp.isTwoPanels
- && oldDp.isLandscape != newDp.isLandscape));
-
- return isFoldUnFold || useDifferentLayoutOnOrientationChange;
+ return oldDp.isTwoPanels != newDp.isTwoPanels;
}
/**
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
index 679b0f5..fc99fcc 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
@@ -112,21 +112,48 @@
// Bind the widget items.
for (int i = 0; i < widgetItemsTable.size(); i++) {
List<WidgetItem> widgetItemsPerRow = widgetItemsTable.get(i);
- for (int j = 0; j < widgetItemsPerRow.size(); j++) {
- WidgetTableRow row = (WidgetTableRow) table.getChildAt(i);
- row.setVisibility(View.VISIBLE);
- WidgetCell widget = (WidgetCell) row.getChildAt(j);
- widget.clear();
- widget.addPreviewReadyListener(row);
- WidgetItem widgetItem = widgetItemsPerRow.get(j);
- widget.setVisibility(View.VISIBLE);
+ WidgetTableRow row = (WidgetTableRow) table.getChildAt(i);
- widget.applyFromCellItem(widgetItem);
- widget.requestLayout();
+ if (areRowItemsUnchanged(row, widgetItemsPerRow)) { // Just show widgets in row as is
+ row.setVisibility(View.VISIBLE);
+ for (int j = 0; j < widgetItemsPerRow.size(); j++) {
+ WidgetCell widget = (WidgetCell) row.getChildAt(j);
+ widget.setVisibility(View.VISIBLE);
+ }
+ } else {
+ for (int j = 0; j < widgetItemsPerRow.size(); j++) {
+ row.setVisibility(View.VISIBLE);
+ WidgetCell widget = (WidgetCell) row.getChildAt(j);
+ widget.clear();
+ WidgetItem widgetItem = widgetItemsPerRow.get(j);
+ widget.addPreviewReadyListener(row);
+ widget.setVisibility(View.VISIBLE);
+
+ widget.applyFromCellItem(widgetItem);
+ widget.requestLayout();
+ }
}
}
}
+ private boolean areRowItemsUnchanged(WidgetTableRow row, List<WidgetItem> widgetItemsPerRow) {
+ // NOTE: on rotation or fold / unfold, we bind different view holders
+ // so, we don't any special handling for that case.
+ if (row.getChildCount() != widgetItemsPerRow.size()) { // Items not equal
+ return false;
+ }
+
+ for (int j = 0; j < widgetItemsPerRow.size(); j++) {
+ WidgetCell widgetCell = (WidgetCell) row.getChildAt(j);
+ WidgetItem widgetItem = widgetItemsPerRow.get(j);
+ if (widgetCell.getWidgetItem() == null
+ || !widgetCell.getWidgetItem().equals(widgetItem)) {
+ return false; // Items at given position in row aren't same.
+ }
+ }
+ return true;
+ }
+
/**
* Adds and hides table rows and columns from {@code table} to ensure there is sufficient room
* to display {@code widgetItemsTable}.
@@ -151,26 +178,31 @@
tableRow.setGravity(Gravity.TOP);
table.addView(tableRow);
}
- // Pass resize delay to let the "move" and "change" animations run before resizing the
- // row.
- tableRow.setupRow(widgetItems.size(),
- /*resizeDelayMs=*/ WIDGET_LIST_ITEM_APPEARANCE_START_DELAY);
- if (tableRow.getChildCount() > widgetItems.size()) {
- for (int j = widgetItems.size(); j < tableRow.getChildCount(); j++) {
- tableRow.getChildAt(j).setVisibility(View.GONE);
- }
- } else {
- for (int j = tableRow.getChildCount(); j < widgetItems.size(); j++) {
- WidgetCell widget = (WidgetCell) mLayoutInflater.inflate(
- R.layout.widget_cell, tableRow, false);
- // set up touch.
- widget.setOnClickListener(mIconClickListener);
- widget.addPreviewReadyListener(tableRow);
- View preview = widget.findViewById(R.id.widget_preview_container);
- preview.setOnClickListener(mIconClickListener);
- preview.setOnLongClickListener(mIconLongClickListener);
- widget.setAnimatePreview(false);
- tableRow.addView(widget);
+
+ // If the row items are unchanged, we don't need to re-setup the row or the items;
+ // we can just show the row as is.
+ if (!areRowItemsUnchanged(tableRow, widgetItems)) {
+ // Pass resize delay to let the "move" and "change" animations run before resizing
+ // the row.
+ tableRow.setupRow(widgetItems.size(),
+ /*resizeDelayMs=*/ WIDGET_LIST_ITEM_APPEARANCE_START_DELAY);
+ if (tableRow.getChildCount() > widgetItems.size()) {
+ for (int j = widgetItems.size(); j < tableRow.getChildCount(); j++) {
+ tableRow.getChildAt(j).setVisibility(View.GONE);
+ }
+ } else {
+ for (int j = tableRow.getChildCount(); j < widgetItems.size(); j++) {
+ WidgetCell widget = (WidgetCell) mLayoutInflater.inflate(
+ R.layout.widget_cell, tableRow, false);
+ // set up touch.
+ widget.setOnClickListener(mIconClickListener);
+ widget.addPreviewReadyListener(tableRow);
+ View preview = widget.findViewById(R.id.widget_preview_container);
+ preview.setOnClickListener(mIconClickListener);
+ preview.setOnLongClickListener(mIconLongClickListener);
+ widget.setAnimatePreview(false);
+ tableRow.addView(widget);
+ }
}
}
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java b/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
index 216f4d4..9ee9150 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
@@ -15,7 +15,6 @@
*/
package com.android.launcher3.widget.picker;
-import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
import static com.android.launcher3.widget.util.WidgetSizes.getWidgetSizePx;
import static com.android.launcher3.widget.util.WidgetsTableUtils.WIDGETS_TABLE_ROW_COUNT_COMPARATOR;
@@ -112,10 +111,8 @@
WidgetCell widgetCell = addItemCell(tableRow);
widgetCell.applyFromCellItem(widgetItem);
widgetCell.showAppIconInWidgetTitle(true);
- if (enableCategorizedWidgetSuggestions()) {
- widgetCell.showDescription(false);
- widgetCell.showDimensions(false);
- }
+ widgetCell.showDescription(false);
+ widgetCell.showDimensions(false);
}
addView(tableRow);
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
index df76400..9ffaf51 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
@@ -15,9 +15,7 @@
*/
package com.android.launcher3.widget.picker;
-import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
import static com.android.launcher3.Flags.enableTieredWidgetsByDefaultInPicker;
-import static com.android.launcher3.Flags.enableUnfoldedTwoPanePicker;
import static com.android.launcher3.UtilitiesKt.CLIP_CHILDREN_FALSE_MODIFIER;
import static com.android.launcher3.UtilitiesKt.CLIP_TO_PADDING_FALSE_MODIFIER;
import static com.android.launcher3.UtilitiesKt.modifyAttributesOnViewTree;
@@ -215,22 +213,9 @@
@Override
protected int getTabletHorizontalMargin(DeviceProfile deviceProfile) {
- if (enableCategorizedWidgetSuggestions()) {
- // two pane picker is full width for fold as well as tablet.
- return getResources().getDimensionPixelSize(
- R.dimen.widget_picker_two_panels_left_right_margin);
- }
- if (deviceProfile.isTwoPanels && enableUnfoldedTwoPanePicker()) {
- // enableUnfoldedTwoPanePicker made two pane picker full-width for fold only.
- return getResources().getDimensionPixelSize(
- R.dimen.widget_picker_two_panels_left_right_margin);
- }
- if (deviceProfile.isLandscape && !deviceProfile.isTwoPanels) {
- // non-fold tablet landscape margins (ag/22163531)
- return getResources().getDimensionPixelSize(
- R.dimen.widget_picker_landscape_tablet_left_right_margin);
- }
- return deviceProfile.allAppsLeftRightMargin;
+ // two pane picker is full width for fold as well as tablet.
+ return getResources().getDimensionPixelSize(
+ R.dimen.widget_picker_two_panels_left_right_margin);
}
@Override
@@ -257,7 +242,7 @@
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
- if (changed && mDeviceProfile.isTwoPanels && enableUnfoldedTwoPanePicker()) {
+ if (changed && mDeviceProfile.isTwoPanels) {
LinearLayout layout = mContent.findViewById(R.id.linear_layout_container);
FrameLayout leftPane = layout.findViewById(R.id.recycler_view_container);
LinearLayout.LayoutParams layoutParams = (LayoutParams) leftPane.getLayoutParams();
@@ -427,7 +412,7 @@
protected int getAvailableWidthForSuggestions(int pickerAvailableWidth) {
int rightPaneWidth = (int) Math.ceil(0.67 * pickerAvailableWidth);
- if (mDeviceProfile.isTwoPanels && enableUnfoldedTwoPanePicker()) {
+ if (mDeviceProfile.isTwoPanels) {
// See onLayout
int leftPaneWidth = (int) (0.33 * pickerAvailableWidth);
@Px int minLeftPaneWidthPx = Utilities.dpToPx(MINIMUM_WIDTH_LEFT_PANE_FOLDABLE_DP);
@@ -552,13 +537,9 @@
}
WidgetsListContentEntry contentEntryToBind;
- if (enableCategorizedWidgetSuggestions()) {
- // Setting max span size enables row to understand how to fit more than one item
- // in a row.
- contentEntryToBind = contentEntry.withMaxSpanSize(mMaxSpanPerRow);
- } else {
- contentEntryToBind = contentEntry;
- }
+ // Setting max span size enables row to understand how to fit more than one item
+ // in a row.
+ contentEntryToBind = contentEntry.withMaxSpanSize(mMaxSpanPerRow);
WidgetsRowViewHolder widgetsRowViewHolder =
mWidgetsListTableViewHolderBinder.newViewHolder(mRightPane);
diff --git a/src/com/android/launcher3/widget/picker/model/WidgetPickerDataProvider.kt b/src/com/android/launcher3/widget/picker/model/WidgetPickerDataProvider.kt
index 46d3e7a..5b97a49 100644
--- a/src/com/android/launcher3/widget/picker/model/WidgetPickerDataProvider.kt
+++ b/src/com/android/launcher3/widget/picker/model/WidgetPickerDataProvider.kt
@@ -16,43 +16,79 @@
package com.android.launcher3.widget.picker.model
+import android.content.Context
+import com.android.launcher3.dagger.LauncherComponentProvider.appComponent
+import com.android.launcher3.model.WidgetItem
+import com.android.launcher3.model.WidgetsFilterDataProvider
+import com.android.launcher3.model.WidgetsFilterDataProvider.WidgetsFilterLoadedCallback
import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.widget.model.WidgetsListBaseEntry
import com.android.launcher3.widget.picker.model.data.WidgetPickerData
import com.android.launcher3.widget.picker.model.data.WidgetPickerDataUtils.withRecommendedWidgets
import com.android.launcher3.widget.picker.model.data.WidgetPickerDataUtils.withWidgets
import java.io.PrintWriter
+import java.util.function.Predicate
/**
* Provides [WidgetPickerData] to various views such as widget picker, app-specific widget picker,
* widgets shortcut.
*/
-class WidgetPickerDataProvider {
+class WidgetPickerDataProvider(private val filterProvider: WidgetsFilterDataProvider) :
+ WidgetsFilterLoadedCallback {
+
+ constructor(context: Context) : this(context.appComponent.widgetsFilterDataProvider)
+
/** All the widgets data provided for the views */
private var mWidgetPickerData: WidgetPickerData = WidgetPickerData()
private var changeListener: WidgetPickerDataChangeListener? = null
+ var hostSpecifiedDefaultWidgetsFilter: Predicate<WidgetItem>? = null
+
+ private var allWidgets: List<WidgetsListBaseEntry> = emptyList()
+
/** Sets a listener to be called back when widget data is updated. */
fun setChangeListener(changeListener: WidgetPickerDataChangeListener?) {
this.changeListener = changeListener
}
+ init {
+ filterProvider.addFilterChangeCallback(this)
+ }
+
/** Returns the current snapshot of [WidgetPickerData]. */
fun get(): WidgetPickerData {
return mWidgetPickerData
}
+ override fun onWidgetsFilterLoaded() {
+ setWidgets(allWidgets)
+ }
+
/**
* Updates the widgets available to the widget picker.
*
* Generally called when the widgets model has new data.
*/
- @JvmOverloads
- fun setWidgets(
- allWidgets: List<WidgetsListBaseEntry>,
- defaultWidgets: List<WidgetsListBaseEntry> = listOf()
- ) {
+ fun setWidgets(allWidgets: List<WidgetsListBaseEntry>) {
+ this.allWidgets = allWidgets
+
+ val currentFilter = filterProvider.defaultWidgetsFilter
+ val finalFilter =
+ when {
+ currentFilter != null && hostSpecifiedDefaultWidgetsFilter != null ->
+ currentFilter.and(hostSpecifiedDefaultWidgetsFilter)
+ hostSpecifiedDefaultWidgetsFilter != null -> hostSpecifiedDefaultWidgetsFilter
+ else -> currentFilter
+ }
+
+ val defaultWidgets =
+ if (finalFilter != null)
+ allWidgets
+ .map { it.copy().apply { mWidgets.removeIf(finalFilter) } }
+ .filter { it.mWidgets.isNotEmpty() }
+ else emptyList()
+
mWidgetPickerData =
mWidgetPickerData.withWidgets(allWidgets = allWidgets, defaultWidgets = defaultWidgets)
changeListener?.onWidgetsBound()
@@ -74,6 +110,10 @@
writer.println("$prefix\twidgetPickerData:$mWidgetPickerData")
}
+ fun destroy() {
+ filterProvider.removeFilterChangeCallback(this)
+ }
+
interface WidgetPickerDataChangeListener {
/** A callback to get notified when widgets are bound. */
fun onWidgetsBound()
diff --git a/src/com/android/launcher3/workprofile/PersonalWorkSlidingTabStrip.java b/src/com/android/launcher3/workprofile/PersonalWorkSlidingTabStrip.java
index e94f3a0..185c3ee 100644
--- a/src/com/android/launcher3/workprofile/PersonalWorkSlidingTabStrip.java
+++ b/src/com/android/launcher3/workprofile/PersonalWorkSlidingTabStrip.java
@@ -26,9 +26,12 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
+import com.android.launcher3.pageindicators.Direction;
import com.android.launcher3.pageindicators.PageIndicator;
import com.android.launcher3.views.ActivityContext;
+import java.util.function.Consumer;
+
/**
* Supports two indicator colors, dedicated for personal and work tabs.
*/
@@ -78,6 +81,11 @@
}
@Override
+ public void setArrowClickListener(Consumer<Direction> listener) {
+ // No-Op. All Apps doesn't need accessibility arrows for single click navigation.
+ }
+
+ @Override
public boolean hasOverlappingRendering() {
return false;
}
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/phonePortrait.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/phonePortrait.txt
index a13d63b..6a0d774 100644
--- a/tests/assets/dumpTests/DeviceProfileDumpTest/phonePortrait.txt
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/phonePortrait.txt
@@ -53,7 +53,7 @@
bottomSheetTopPadding: 146.0px (55.61905dp)
bottomSheetOpenDuration: 267
bottomSheetCloseDuration: 267
- bottomSheetWorkspaceScale: 1.0
+ bottomSheetWorkspaceScale: 0.97
bottomSheetDepth: 0.0
allAppsShiftRange: 2400.0px (914.2857dp)
allAppsOpenDuration: 600
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/phonePortrait3Button.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/phonePortrait3Button.txt
index 3c24885..9e2397c 100644
--- a/tests/assets/dumpTests/DeviceProfileDumpTest/phonePortrait3Button.txt
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/phonePortrait3Button.txt
@@ -53,7 +53,7 @@
bottomSheetTopPadding: 146.0px (55.61905dp)
bottomSheetOpenDuration: 267
bottomSheetCloseDuration: 267
- bottomSheetWorkspaceScale: 1.0
+ bottomSheetWorkspaceScale: 0.97
bottomSheetDepth: 0.0
allAppsShiftRange: 2400.0px (914.2857dp)
allAppsOpenDuration: 600
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar.txt
index 5e06513..f25ab42 100644
--- a/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar.txt
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar.txt
@@ -53,7 +53,7 @@
bottomSheetTopPadding: 114.0px (43.42857dp)
bottomSheetOpenDuration: 267
bottomSheetCloseDuration: 267
- bottomSheetWorkspaceScale: 1.0
+ bottomSheetWorkspaceScale: 0.97
bottomSheetDepth: 0.0
allAppsShiftRange: 1080.0px (411.42856dp)
allAppsOpenDuration: 600
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar3Button.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar3Button.txt
index d107988..053dd62 100644
--- a/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar3Button.txt
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar3Button.txt
@@ -53,7 +53,7 @@
bottomSheetTopPadding: 114.0px (43.42857dp)
bottomSheetOpenDuration: 267
bottomSheetCloseDuration: 267
- bottomSheetWorkspaceScale: 1.0
+ bottomSheetWorkspaceScale: 0.97
bottomSheetDepth: 0.0
allAppsShiftRange: 1080.0px (411.42856dp)
allAppsOpenDuration: 600
diff --git a/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt b/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
index ad6afcf..a30261e 100644
--- a/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
@@ -39,6 +39,7 @@
import com.android.launcher3.util.SandboxContext
import com.android.launcher3.util.WindowBounds
import com.android.launcher3.util.rule.TestStabilityRule
+import com.android.launcher3.util.rule.ZipFilesRule
import com.android.launcher3.util.rule.setFlags
import com.android.launcher3.util.window.CachedDisplayInfo
import com.android.launcher3.util.window.WindowManagerProxy
@@ -51,6 +52,7 @@
import java.io.StringWriter
import kotlin.math.max
import kotlin.math.min
+import org.junit.ClassRule
import org.junit.Rule
import org.mockito.kotlin.any
import org.mockito.kotlin.doReturn
@@ -80,6 +82,13 @@
@Rule @JvmField val limitDevicesRule = LimitDevicesRule()
+ companion object {
+ @ClassRule
+ @JvmField
+ val resultZipRule =
+ ZipFilesRule(InstrumentationRegistry.getInstrumentation().targetContext, "DumpTest")
+ }
+
class DeviceSpec(
val naturalSize: Pair<Int, Int>,
var densityDpi: Int,
@@ -364,13 +373,9 @@
context.assets.open("dumpTests/$fileName").bufferedReader().use(BufferedReader::readText)
private fun writeToDevice(context: Context, fileName: String, content: String) {
- val dir =
- File(context.filesDir, "dumpTests").also {
- if (!it.exists()) {
- it.mkdirs()
- }
- }
- File(dir, fileName).writeText(content)
+ val file = File(context.getDir("dumpTests", Context.MODE_PRIVATE), fileName)
+ file.writeText(content)
+ resultZipRule.write(file)
}
protected fun Float.dpToPx(): Float {
diff --git a/tests/multivalentTests/src/com/android/launcher3/AppWidgetsRestoredReceiverTest.kt b/tests/multivalentTests/src/com/android/launcher3/AppWidgetsRestoredReceiverTest.kt
index 0e06051..6483bd5 100644
--- a/tests/multivalentTests/src/com/android/launcher3/AppWidgetsRestoredReceiverTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/AppWidgetsRestoredReceiverTest.kt
@@ -6,7 +6,7 @@
import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_OLD_IDS
import android.appwidget.AppWidgetManager.EXTRA_HOST_ID
import android.content.Intent
-import android.platform.uiautomator_helpers.DeviceHelpers
+import android.platform.uiautomatorhelpers.DeviceHelpers
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.launcher3.LauncherPrefs.Companion.APP_WIDGET_IDS
diff --git a/tests/multivalentTests/src/com/android/launcher3/celllayout/board/TestWorkspaceBuilder.kt b/tests/multivalentTests/src/com/android/launcher3/celllayout/board/TestWorkspaceBuilder.kt
index 2e556e8..3405cae 100644
--- a/tests/multivalentTests/src/com/android/launcher3/celllayout/board/TestWorkspaceBuilder.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/celllayout/board/TestWorkspaceBuilder.kt
@@ -44,7 +44,7 @@
private fun fillWithWidgets(
widgetRect: WidgetRect,
transaction: FavoriteItemsTransaction,
- screenId: Int
+ screenId: Int,
): FavoriteItemsTransaction {
val initX = widgetRect.cellX
val initY = widgetRect.cellY
@@ -76,7 +76,7 @@
appComponentName =
ComponentName(
InstrumentationRegistry.getInstrumentation().context.packageName,
- TEST_ACTIVITY_PACKAGE_PREFIX + testAppName
+ TEST_ACTIVITY_PACKAGE_PREFIX + testAppName,
)
}
@@ -92,7 +92,7 @@
private fun addCorrespondingWidgetRect(
widgetRect: WidgetRect,
transaction: FavoriteItemsTransaction,
- screenId: Int
+ screenId: Int,
) {
if (widgetRect.type == 'x') {
fillWithWidgets(widgetRect, transaction, screenId)
@@ -105,7 +105,7 @@
fun buildFromBoard(
board: CellLayoutBoard,
transaction: FavoriteItemsTransaction,
- screenId: Int
+ screenId: Int,
): FavoriteItemsTransaction {
board.widgets.forEach { addCorrespondingWidgetRect(it, transaction, screenId) }
board.icons.forEach { transaction.addItem { createIconInCell(it, screenId) } }
@@ -130,7 +130,7 @@
WidgetUtils.createWidgetInfo(
TestViewHelpers.findWidgetProvider(false),
ApplicationProvider.getApplicationContext(),
- true
+ true,
)
.apply {
cellX = widgetRect.cellX
@@ -154,7 +154,7 @@
minSpanY = 1
setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, true, null)
for (i in 0 until folderPoint.numberIconsInside) {
- add(getDefaultWorkspaceItem(paramScreenId), false)
+ add(getDefaultWorkspaceItem(paramScreenId))
}
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/folder/FolderTest.kt b/tests/multivalentTests/src/com/android/launcher3/folder/FolderTest.kt
index bcf5f0d..e825995 100644
--- a/tests/multivalentTests/src/com/android/launcher3/folder/FolderTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/folder/FolderTest.kt
@@ -298,12 +298,13 @@
val dragObject = DragObject(context)
dragObject.dragInfo = Mockito.mock(ItemInfo::class.java)
folder.mContent = Mockito.mock(FolderPagedView::class.java)
+ folder.mFolderIcon = Mockito.mock(FolderIcon::class.java)
dragObject.dragSource = folder
folder.onDragStart(dragObject, DragOptions())
verify(folder.mContent, times(1)).removeItem(folder.currentDragView)
- verify(folder.mInfo, times(1)).remove(dragObject.dragInfo, true)
+ verify(folder, times(1)).removeFolderContent(true, dragObject.dragInfo)
assertTrue(folder.itemsInvalidated)
assertTrue(folder.isDragInProgress)
assertFalse(folder.itemAddedBackToSelfViaIcon)
@@ -827,19 +828,19 @@
@Test
fun `onRemove should call removeItem with the correct views`() {
+ val folderInfo =
+ workspaceBuilder.createFolderInCell(FolderPoint(Point(1, 0), TWO_ICON_FOLDER_TYPE), 0)
+ val items = folderInfo.getContents().toTypedArray()
+ folder.mInfo = folderInfo
+ folder.mFolderIcon = Mockito.mock(FolderIcon::class.java)
folder.mContent = Mockito.mock(FolderPagedView::class.java)
- val items =
- arrayListOf<ItemInfo>(
- Mockito.mock(ItemInfo::class.java),
- Mockito.mock(ItemInfo::class.java),
- )
val view1 = Mockito.mock(View::class.java)
val view2 = Mockito.mock(View::class.java)
doReturn(view1).whenever(folder).getViewForInfo(items[0])
doReturn(view2).whenever(folder).getViewForInfo(items[1])
doReturn(2).whenever(folder).itemCount
- folder.onRemove(items)
+ folder.removeFolderContent(false, *items)
verify(folder.mContent, times(1)).removeItem(view1)
verify(folder.mContent, times(1)).removeItem(view2)
@@ -847,20 +848,20 @@
@Test
fun `onRemove should set mRearrangeOnClose to true and not call rearrangeChildren if animating`() {
+ val folderInfo =
+ workspaceBuilder.createFolderInCell(FolderPoint(Point(1, 0), TWO_ICON_FOLDER_TYPE), 0)
+ val items = folderInfo.getContents().toTypedArray()
+ folder.mInfo = folderInfo
+ folder.mFolderIcon = Mockito.mock(FolderIcon::class.java)
folder.mContent = Mockito.mock(FolderPagedView::class.java)
folder.state = STATE_ANIMATING
- val items =
- arrayListOf<ItemInfo>(
- Mockito.mock(ItemInfo::class.java),
- Mockito.mock(ItemInfo::class.java),
- )
val view1 = Mockito.mock(View::class.java)
val view2 = Mockito.mock(View::class.java)
doReturn(view1).whenever(folder).getViewForInfo(items[0])
doReturn(view2).whenever(folder).getViewForInfo(items[1])
doReturn(2).whenever(folder).itemCount
- folder.onRemove(items)
+ folder.removeFolderContent(true, *items)
assertTrue(folder.rearrangeOnClose)
verify(folder, times(0)).rearrangeChildren()
@@ -868,21 +869,21 @@
@Test
fun `onRemove should set not change mRearrangeOnClose and not call rearrangeChildren if not animating`() {
+ val folderInfo =
+ workspaceBuilder.createFolderInCell(FolderPoint(Point(1, 0), TWO_ICON_FOLDER_TYPE), 0)
+ val items = folderInfo.getContents().toTypedArray()
+ folder.mInfo = folderInfo
+ folder.mFolderIcon = Mockito.mock(FolderIcon::class.java)
folder.mContent = Mockito.mock(FolderPagedView::class.java)
folder.state = STATE_CLOSED
folder.rearrangeOnClose = false
- val items =
- arrayListOf<ItemInfo>(
- Mockito.mock(ItemInfo::class.java),
- Mockito.mock(ItemInfo::class.java),
- )
val view1 = Mockito.mock(View::class.java)
val view2 = Mockito.mock(View::class.java)
doReturn(view1).whenever(folder).getViewForInfo(items[0])
doReturn(view2).whenever(folder).getViewForInfo(items[1])
doReturn(2).whenever(folder).itemCount
- folder.onRemove(items)
+ folder.removeFolderContent(false, *items)
assertFalse(folder.rearrangeOnClose)
verify(folder, times(1)).rearrangeChildren()
@@ -890,12 +891,12 @@
@Test
fun `onRemove should call close if mIsOpen is true and item count is less than or equal to one`() {
+ val folderInfo =
+ workspaceBuilder.createFolderInCell(FolderPoint(Point(1, 0), TWO_ICON_FOLDER_TYPE), 0)
+ val items = folderInfo.getContents().toTypedArray()
+ folder.mInfo = folderInfo
+ folder.mFolderIcon = Mockito.mock(FolderIcon::class.java)
folder.mContent = Mockito.mock(FolderPagedView::class.java)
- val items =
- arrayListOf<ItemInfo>(
- Mockito.mock(ItemInfo::class.java),
- Mockito.mock(ItemInfo::class.java),
- )
val view1 = Mockito.mock(View::class.java)
val view2 = Mockito.mock(View::class.java)
doReturn(view1).whenever(folder).getViewForInfo(items[0])
@@ -904,19 +905,19 @@
folder.setIsOpen(true)
doNothing().`when`(folder).close(true)
- folder.onRemove(items)
+ folder.removeFolderContent(false, *items)
verify(folder, times(1)).close(true)
}
@Test
fun `onRemove should call replaceFolderWithFinalItem if mIsOpen is false and item count is less than or equal to one`() {
+ val folderInfo =
+ workspaceBuilder.createFolderInCell(FolderPoint(Point(1, 0), TWO_ICON_FOLDER_TYPE), 0)
+ val items = folderInfo.getContents().toTypedArray()
+ folder.mInfo = folderInfo
+ folder.mFolderIcon = Mockito.mock(FolderIcon::class.java)
folder.mContent = Mockito.mock(FolderPagedView::class.java)
- val items =
- arrayListOf<ItemInfo>(
- Mockito.mock(ItemInfo::class.java),
- Mockito.mock(ItemInfo::class.java),
- )
val view1 = Mockito.mock(View::class.java)
val view2 = Mockito.mock(View::class.java)
doReturn(view1).whenever(folder).getViewForInfo(items[0])
@@ -924,7 +925,7 @@
doReturn(1).whenever(folder).itemCount
folder.setIsOpen(false)
- folder.onRemove(items)
+ folder.removeFolderContent(false, *items)
verify(folder, times(1)).replaceFolderWithFinalItem()
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/icons/UserBadgeDrawableTest.kt b/tests/multivalentTests/src/com/android/launcher3/icons/UserBadgeDrawableTest.kt
index d611ae8..91ba628 100644
--- a/tests/multivalentTests/src/com/android/launcher3/icons/UserBadgeDrawableTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/icons/UserBadgeDrawableTest.kt
@@ -34,19 +34,20 @@
private val context = InstrumentationRegistry.getInstrumentation().targetContext
private val canvas = mock<Canvas>()
private val systemUnderTest =
- UserBadgeDrawable(context, R.drawable.ic_work_app_badge, R.color.badge_tint_work, false)
+ UserBadgeDrawable(
+ context,
+ R.drawable.ic_work_app_badge,
+ R.color.badge_tint_work,
+ false /* isThemed */,
+ null, /* shape */
+ )
@Test
fun draw_opaque() {
val colorList = mutableListOf<Int>()
- whenever(
- canvas.drawCircle(
- any(),
- any(),
- any(),
- any()
- )
- ).then { colorList.add(it.getArgument<Paint>(3).color) }
+ whenever(canvas.drawCircle(any(), any(), any(), any())).then {
+ colorList.add(it.getArgument<Paint>(3).color)
+ }
systemUnderTest.alpha = 255
systemUnderTest.draw(canvas)
@@ -57,14 +58,9 @@
@Test
fun draw_transparent() {
val colorList = mutableListOf<Int>()
- whenever(
- canvas.drawCircle(
- any(),
- any(),
- any(),
- any()
- )
- ).then { colorList.add(it.getArgument<Paint>(3).color) }
+ whenever(canvas.drawCircle(any(), any(), any(), any())).then {
+ colorList.add(it.getArgument<Paint>(3).color)
+ }
systemUnderTest.alpha = 0
systemUnderTest.draw(canvas)
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt
index 08b8f81..25f4cf1 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt
@@ -33,7 +33,6 @@
import org.mockito.kotlin.any
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
-import org.mockito.kotlin.same
import org.mockito.kotlin.verify
import org.mockito.kotlin.verifyNoMoreInteractions
import org.mockito.kotlin.whenever
@@ -142,10 +141,9 @@
/** Sets up the item space data that will be returned from WorkspaceItemSpaceFinder. */
private fun givenNewItemSpaces(vararg newItemSpaces: NewItemSpace) {
val spaceStack = newItemSpaces.toMutableList()
- whenever(
- mWorkspaceItemSpaceFinder.findSpaceForItem(any(), any(), any(), any(), any(), any())
- )
- .then { spaceStack.removeFirst().toIntArray() }
+ whenever(mWorkspaceItemSpaceFinder.findSpaceForItem(any(), any(), any(), any())).then {
+ spaceStack.removeFirst().toIntArray()
+ }
}
/**
@@ -155,8 +153,6 @@
private fun verifyItemSpaceFinderCall(nonEmptyScreenIds: List<Int>, numberOfExpectedCall: Int) {
verify(mWorkspaceItemSpaceFinder, times(numberOfExpectedCall))
.findSpaceForItem(
- eq(mAppState),
- same(mModelHelper.bgDataModel),
eq(IntArray.wrap(*nonEmptyScreenIds.toIntArray())),
eq(IntArray()),
eq(1),
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java
index ad40818..b848d27 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java
@@ -55,7 +55,6 @@
import static junit.framework.Assert.assertTrue;
import android.content.ComponentName;
-import android.content.Context;
import android.content.Intent;
import android.database.MatrixCursor;
import android.graphics.Bitmap;
@@ -76,6 +75,7 @@
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.LauncherModelHelper;
+import com.android.launcher3.util.LauncherModelHelper.SandboxModelContext;
import com.android.launcher3.util.PackageManagerHelper;
import org.junit.After;
@@ -96,11 +96,10 @@
private LauncherModelHelper mModelHelper;
private LauncherAppState mApp;
- private PackageManagerHelper mPmHelper;
private MatrixCursor mCursor;
private InvariantDeviceProfile mIDP;
- private Context mContext;
+ private SandboxModelContext mContext;
private LoaderCursor mLoaderCursor;
@@ -116,7 +115,6 @@
mContext = mModelHelper.sandboxContext;
mIDP = InvariantDeviceProfile.INSTANCE.get(mContext);
mApp = LauncherAppState.getInstance(mContext);
- mPmHelper = PackageManagerHelper.INSTANCE.get(mContext);
mCursor = new MatrixCursor(new String[] {
ICON, TITLE, _ID, CONTAINER, ITEM_TYPE,
@@ -126,7 +124,8 @@
});
UserManagerState ums = new UserManagerState();
- mLoaderCursor = new LoaderCursor(mCursor, mApp, ums, mPmHelper, null);
+ mLoaderCursor = mContext.getAppComponent().getLoaderCursorFactory()
+ .createLoaderCursor(mCursor, ums, null);
ums.allUsers.put(0, Process.myUserHandle());
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/PackageUpdatedTaskTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/PackageUpdatedTaskTest.kt
index eec6eed..5fd93d1 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/PackageUpdatedTaskTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/model/PackageUpdatedTaskTest.kt
@@ -27,8 +27,9 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.launcher3.AppFilter
import com.android.launcher3.Flags.FLAG_ENABLE_PRIVATE_SPACE
-import com.android.launcher3.LauncherAppState
import com.android.launcher3.LauncherSettings
+import com.android.launcher3.dagger.LauncherAppComponent
+import com.android.launcher3.dagger.LauncherAppSingleton
import com.android.launcher3.icons.IconCache
import com.android.launcher3.model.PackageUpdatedTask.OP_ADD
import com.android.launcher3.model.PackageUpdatedTask.OP_REMOVE
@@ -39,17 +40,20 @@
import com.android.launcher3.model.PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE
import com.android.launcher3.model.data.AppInfo
import com.android.launcher3.model.data.WorkspaceItemInfo
+import com.android.launcher3.util.AllModulesForTest
import com.android.launcher3.util.Executors
import com.android.launcher3.util.LauncherModelHelper
-import com.android.launcher3.util.LauncherModelHelper.SandboxModelContext
import com.android.launcher3.util.TestUtil
import com.google.common.truth.Truth.assertThat
+import dagger.BindsInstance
+import dagger.Component
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
+import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
import org.mockito.kotlin.verify
@@ -61,10 +65,8 @@
@get:Rule val setFlagsRule = SetFlagsRule()
private val mUser = myUserHandle()
- private val mDataModel: BgDataModel = BgDataModel()
private val mLauncherModelHelper = LauncherModelHelper()
- private val mContext: SandboxModelContext = spy(mLauncherModelHelper.sandboxContext)
- private val mAppState: LauncherAppState = spy(LauncherAppState.getInstance(mContext))
+ private val mContext = mLauncherModelHelper.sandboxContext
private val expectedPackage = "Test.Package"
private val expectedComponent = ComponentName(expectedPackage, "TestClass")
@@ -72,22 +74,33 @@
private val expectedWorkspaceItem = spy(WorkspaceItemInfo())
private val mockIconCache: IconCache = mock()
- private val mockTaskController: ModelTaskController = mock<ModelTaskController>()
private val mockAppFilter: AppFilter = mock<AppFilter>()
+ private lateinit var mAllAppsList: AllAppsList
+
private val mockApplicationInfo: ApplicationInfo = mock<ApplicationInfo>()
private val mockActivityInfo: ActivityInfo = mock<ActivityInfo>()
- private lateinit var mAllAppsList: AllAppsList
+ private lateinit var mDataModel: BgDataModel
+ private lateinit var mockTaskController: ModelTaskController
@Before
fun setup() {
mAllAppsList = spy(AllAppsList(mockIconCache, mockAppFilter))
- mLauncherModelHelper.sandboxContext.spyService(LauncherApps::class.java).apply {
+ mContext.initDaggerComponent(
+ DaggerPackageUpdatedTaskTest_TestComponent.builder()
+ .bindAllAppsList(mAllAppsList)
+ .bindAppFilter(mockAppFilter)
+ .bindIconCache(mockIconCache)
+ )
+
+ mContext.spyService(LauncherApps::class.java).apply {
whenever(getActivityList(expectedPackage, mUser))
.thenReturn(listOf(expectedActivityInfo))
}
- whenever(mAppState.iconCache).thenReturn(mockIconCache)
- whenever(mockTaskController.app).thenReturn(mAppState)
+
+ mockTaskController = spy((mContext.appComponent as TestComponent).getTaskController())
+ mDataModel = (mContext.appComponent as TestComponent).getDataModel()
+
whenever(mockAppFilter.shouldShowApp(expectedComponent)).thenReturn(true)
mockApplicationInfo.apply {
uid = 1
@@ -122,7 +135,6 @@
TestUtil.runOnExecutorSync(Executors.MODEL_EXECUTOR) {
taskUnderTest.execute(mockTaskController, mDataModel, mAllAppsList)
}
- mLauncherModelHelper.loadModelSync()
// Then
verify(mockIconCache).updateIconsForPkg(expectedPackage, mUser)
verify(mAllAppsList).addPackage(mContext, expectedPackage, mUser)
@@ -141,7 +153,6 @@
TestUtil.runOnExecutorSync(Executors.MODEL_EXECUTOR) {
taskUnderTest.execute(mockTaskController, mDataModel, mAllAppsList)
}
- mLauncherModelHelper.loadModelSync()
// Then
verify(mockIconCache).updateIconsForPkg(expectedPackage, mUser)
verify(mAllAppsList).updatePackage(mContext, expectedPackage, mUser)
@@ -159,7 +170,6 @@
TestUtil.runOnExecutorSync(Executors.MODEL_EXECUTOR) {
taskUnderTest.execute(mockTaskController, mDataModel, mAllAppsList)
}
- mLauncherModelHelper.loadModelSync()
// Then
verify(mockIconCache).removeIconsForPkg(expectedPackage, mUser)
verify(mAllAppsList).removePackage(expectedPackage, mUser)
@@ -176,7 +186,6 @@
TestUtil.runOnExecutorSync(Executors.MODEL_EXECUTOR) {
taskUnderTest.execute(mockTaskController, mDataModel, mAllAppsList)
}
- mLauncherModelHelper.loadModelSync()
// Then
verify(mAllAppsList).removePackage(expectedPackage, mUser)
verify(mockTaskController).bindUpdatedWorkspaceItems(listOf(expectedWorkspaceItem))
@@ -190,10 +199,11 @@
// When
mDataModel.addItem(mContext, expectedWorkspaceItem, true)
mAllAppsList.add(AppInfo(mContext, expectedActivityInfo, mUser), expectedActivityInfo)
+ mAllAppsList.getAndResetChangeFlag()
+ doAnswer {}.whenever(mockTaskController).bindApplicationsIfNeeded()
TestUtil.runOnExecutorSync(Executors.MODEL_EXECUTOR) {
taskUnderTest.execute(mockTaskController, mDataModel, mAllAppsList)
}
- mLauncherModelHelper.loadModelSync()
// Then
verify(mAllAppsList).updateDisabledFlags(any(), any())
verify(mockTaskController).bindUpdatedWorkspaceItems(listOf(expectedWorkspaceItem))
@@ -206,10 +216,11 @@
val taskUnderTest = PackageUpdatedTask(OP_UNSUSPEND, mUser, expectedPackage)
// When
mDataModel.addItem(mContext, expectedWorkspaceItem, true)
+ mAllAppsList.getAndResetChangeFlag()
+ doAnswer {}.whenever(mockTaskController).bindApplicationsIfNeeded()
TestUtil.runOnExecutorSync(Executors.MODEL_EXECUTOR) {
taskUnderTest.execute(mockTaskController, mDataModel, mAllAppsList)
}
- mLauncherModelHelper.loadModelSync()
// Then
verify(mAllAppsList).updateDisabledFlags(any(), any())
verify(mockTaskController).bindUpdatedWorkspaceItems(emptyList())
@@ -226,10 +237,29 @@
TestUtil.runOnExecutorSync(Executors.MODEL_EXECUTOR) {
taskUnderTest.execute(mockTaskController, mDataModel, mAllAppsList)
}
- mLauncherModelHelper.loadModelSync()
// Then
verify(mAllAppsList).updateDisabledFlags(any(), any())
verify(mockTaskController).bindUpdatedWorkspaceItems(emptyList())
assertThat(mAllAppsList.data).isEmpty()
}
+
+ @LauncherAppSingleton
+ @Component(modules = [AllModulesForTest::class])
+ interface TestComponent : LauncherAppComponent {
+
+ fun getDataModel(): BgDataModel
+
+ fun getTaskController(): ModelTaskController
+
+ @Component.Builder
+ interface Builder : LauncherAppComponent.Builder {
+ @BindsInstance fun bindAppFilter(appFilter: AppFilter): Builder
+
+ @BindsInstance fun bindIconCache(iconCache: IconCache): Builder
+
+ @BindsInstance fun bindAllAppsList(list: AllAppsList): Builder
+
+ override fun build(): TestComponent
+ }
+ }
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/ShortcutsChangedTaskTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/ShortcutsChangedTaskTest.kt
index c6863f4..0ab736b 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/ShortcutsChangedTaskTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/model/ShortcutsChangedTaskTest.kt
@@ -30,7 +30,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.launcher3.Flags
-import com.android.launcher3.LauncherAppState
import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
import com.android.launcher3.icons.BitmapInfo
import com.android.launcher3.icons.CacheableShortcutInfo
@@ -73,7 +72,6 @@
private val user: UserHandle = myUserHandle()
private val mockTaskController: ModelTaskController = mock()
private val mockAllApps: AllAppsList = mock()
- private val mockAppState: LauncherAppState = mock()
private val mockIconCache: IconCache = mock()
private val expectedWai =
@@ -93,9 +91,8 @@
modelHelper.loadModelSync()
context = modelHelper.sandboxContext
launcherApps = context.spyService(LauncherApps::class.java)
- whenever(mockTaskController.app).thenReturn(mockAppState)
- whenever(mockAppState.context).thenReturn(context)
- whenever(mockAppState.iconCache).thenReturn(mockIconCache)
+ whenever(mockTaskController.context).thenReturn(context)
+ whenever(mockTaskController.iconCache).thenReturn(mockIconCache)
whenever(mockIconCache.getShortcutIcon(eq(expectedWai), any<CacheableShortcutInfo>()))
.then { _ -> { expectedWai.bitmap = BitmapInfo.LOW_RES_INFO } }
shortcuts = emptyList()
@@ -132,8 +129,7 @@
// When
shortcutsChangedTask.execute(mockTaskController, modelHelper.bgDataModel, mockAllApps)
// Then
- verify(mockAppState.iconCache)
- .getShortcutIcon(eq(expectedWai), any<CacheableShortcutInfo>())
+ verify(mockIconCache).getShortcutIcon(eq(expectedWai), any<CacheableShortcutInfo>())
verify(mockTaskController).bindUpdatedWorkspaceItems(listOf(expectedWai))
}
@@ -196,8 +192,7 @@
// When
shortcutsChangedTask.execute(mockTaskController, modelHelper.bgDataModel, mockAllApps)
// Then
- verify(mockAppState.iconCache)
- .getShortcutIcon(eq(expectedWai), any<CacheableShortcutInfo>())
+ verify(mockIconCache).getShortcutIcon(eq(expectedWai), any<CacheableShortcutInfo>())
verify(mockTaskController).bindUpdatedWorkspaceItems(listOf(expectedWai))
}
@@ -256,8 +251,7 @@
// When
shortcutsChangedTask.execute(mockTaskController, modelHelper.bgDataModel, mockAllApps)
// Then
- verify(mockAppState.iconCache)
- .getShortcutIcon(eq(expectedWai), any<CacheableShortcutInfo>())
+ verify(mockIconCache).getShortcutIcon(eq(expectedWai), any<CacheableShortcutInfo>())
verify(mockTaskController).bindUpdatedWorkspaceItems(listOf(expectedWai))
}
@@ -287,8 +281,7 @@
// When
shortcutsChangedTask.execute(mockTaskController, modelHelper.bgDataModel, mockAllApps)
// Then
- verify(mockAppState.iconCache)
- .getShortcutIcon(eq(expectedWai), any<CacheableShortcutInfo>())
+ verify(mockIconCache).getShortcutIcon(eq(expectedWai), any<CacheableShortcutInfo>())
verify(mockTaskController).bindUpdatedWorkspaceItems(listOf(expectedWai))
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/WidgetsModelTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/WidgetsModelTest.kt
index d704195..2e6ecc5 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/WidgetsModelTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/model/WidgetsModelTest.kt
@@ -25,9 +25,9 @@
import android.platform.test.rule.LimitDevicesRule
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.launcher3.AppFilter
import com.android.launcher3.DeviceProfile
import com.android.launcher3.InvariantDeviceProfile
-import com.android.launcher3.LauncherAppState
import com.android.launcher3.icons.IconCache
import com.android.launcher3.model.data.PackageItemInfo
import com.android.launcher3.pm.UserCache
@@ -43,7 +43,6 @@
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
-import java.util.function.Predicate
import org.junit.Assert.fail
import org.junit.Before
import org.junit.Rule
@@ -63,9 +62,7 @@
@Rule @JvmField val mockitoRule: MockitoRule = MockitoJUnit.rule()
@Mock private lateinit var appWidgetManager: AppWidgetManager
- @Mock private lateinit var app: LauncherAppState
@Mock private lateinit var iconCacheMock: IconCache
- @Mock private lateinit var widgetsFilterDataProvider: WidgetsFilterDataProvider
private lateinit var context: Context
private lateinit var idp: InvariantDeviceProfile
@@ -95,9 +92,6 @@
whenever(iconCacheMock.getTitleNoCache(any<LauncherAppWidgetProviderInfo>()))
.thenReturn("title")
- whenever(app.iconCache).thenReturn(iconCacheMock)
- whenever(app.context).thenReturn(context)
- whenever(app.invariantDeviceProfile).thenReturn(idp)
val widgetToCategoryEntry: Map.Entry<ComponentName, IntSet> =
WidgetSections.getWidgetsToCategory(context).entries.first()
@@ -130,7 +124,7 @@
val userCache = spy(UserCache.INSTANCE.get(context))
whenever(userCache.userProfiles).thenReturn(listOf(UserHandle.CURRENT))
- underTest = WidgetsModel()
+ underTest = WidgetsModel(context, idp, iconCacheMock, AppFilter(context))
}
@Test
@@ -239,7 +233,7 @@
update = false
// Similarly, model could update its code independently while a client is
// iterating on the list.
- underTest.update(app, /* packageUser= */ null)
+ underTest.update(/* packageUser= */ null)
}
}
@@ -252,31 +246,10 @@
// No exception
}
- @Test
- fun updateWidgetFilters_setsFiltersCorrectly() {
- val testDefaultWidgetFilter = Predicate<WidgetItem> { w -> w.widgetInfo != null }
- whenever(widgetsFilterDataProvider.getDefaultWidgetsFilter())
- .thenReturn(testDefaultWidgetFilter)
- val testPredicatedWidgetFilter = Predicate<WidgetItem> { w -> w.widgetInfo != null }
- whenever(widgetsFilterDataProvider.getPredictedWidgetsFilter())
- .thenReturn(testPredicatedWidgetFilter)
-
- underTest.updateWidgetFilters(widgetsFilterDataProvider)
-
- assertThat(underTest.defaultWidgetsFilter).isEqualTo(testDefaultWidgetFilter)
- assertThat(underTest.predictedWidgetsFilter).isEqualTo(testPredicatedWidgetFilter)
- }
-
- @Test
- fun widgetFilters_nullInitially() {
- assertThat(underTest.defaultWidgetsFilter).isNull()
- assertThat(underTest.predictedWidgetsFilter).isNull()
- }
-
private fun loadWidgets() {
val latch = CountDownLatch(1)
Executors.MODEL_EXECUTOR.execute {
- underTest.update(app, /* packageUser= */ null)
+ underTest.update(/* packageUser= */ null)
latch.countDown()
}
if (!latch.await(LOAD_WIDGETS_TIMEOUT_SECONDS, TimeUnit.SECONDS)) {
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt
index a55d64b..cba7b88 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt
@@ -31,7 +31,7 @@
import android.util.LongSparseArray
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.launcher3.Flags
-import com.android.launcher3.LauncherAppState
+import com.android.launcher3.InvariantDeviceProfile
import com.android.launcher3.LauncherSettings.Favorites
import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP
import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
@@ -41,6 +41,7 @@
import com.android.launcher3.Utilities.EMPTY_PERSON_ARRAY
import com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RestoreError
import com.android.launcher3.icons.CacheableShortcutInfo
+import com.android.launcher3.icons.IconCache
import com.android.launcher3.model.data.FolderInfo
import com.android.launcher3.model.data.IconRequestInfo
import com.android.launcher3.model.data.ItemInfo
@@ -66,12 +67,14 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Answers
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.RETURNS_DEEP_STUBS
import org.mockito.Mockito.mock
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.doAnswer
@@ -88,12 +91,12 @@
@Mock private lateinit var mockIconRequestInfo: IconRequestInfo<WorkspaceItemInfo>
@Mock private lateinit var mockWorkspaceInfo: WorkspaceItemInfo
@Mock private lateinit var mockBgDataModel: BgDataModel
- @Mock private lateinit var mockAppState: LauncherAppState
@Mock private lateinit var mockPmHelper: PackageManagerHelper
- @Mock private lateinit var mockCursor: LoaderCursor
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var mockCursor: LoaderCursor
@Mock private lateinit var mockUserCache: UserCache
@Mock private lateinit var mockUserManagerState: UserManagerState
@Mock private lateinit var mockWidgetInflater: WidgetInflater
+ @Mock private lateinit var mockIconCache: IconCache
lateinit var mModelHelper: LauncherModelHelper
lateinit var mContext: SandboxModelContext
@@ -114,6 +117,7 @@
@Before
fun setup() {
+ MockitoAnnotations.initMocks(this)
mModelHelper = LauncherModelHelper()
mContext = mModelHelper.sandboxContext
mLauncherApps =
@@ -122,9 +126,6 @@
doReturn(true).whenever(this).isActivityEnabled(mComponentName, mUserHandle)
}
mUserHandle = Process.myUserHandle()
- mockIconRequestInfo = mock<IconRequestInfo<WorkspaceItemInfo>>()
- mockWorkspaceInfo = mock<WorkspaceItemInfo>()
- mockBgDataModel = mock<BgDataModel>()
mComponentName = ComponentName("package", "class")
mUnlockedUsersArray = LongSparseArray<Boolean>(1).apply { put(101, true) }
mIntent =
@@ -133,40 +134,26 @@
`package` = "pkg"
putExtra(ShortcutKey.EXTRA_SHORTCUT_ID, "")
}
- mockAppState =
- mock<LauncherAppState>().apply {
- whenever(context).thenReturn(mContext)
- whenever(iconCache).thenReturn(mock())
- whenever(iconCache.getShortcutIcon(any(), any(), any())).then {}
- }
- mockPmHelper =
- mock<PackageManagerHelper>().apply {
- whenever(getAppLaunchIntent(mComponentName.packageName, mUserHandle))
- .thenReturn(mIntent)
- }
- mockCursor =
- mock(LoaderCursor::class.java, RETURNS_DEEP_STUBS).apply {
- user = mUserHandle
- itemType = ITEM_TYPE_APPLICATION
- id = 1
- restoreFlag = 1
- serialNumber = 101
- whenever(parseIntent()).thenReturn(mIntent)
- whenever(markRestored()).doAnswer { restoreFlag = 0 }
- whenever(updater().put(Favorites.INTENT, mIntent.toUri(0)).commit()).thenReturn(1)
- whenever(getAppShortcutInfo(any(), any(), any(), any()))
- .thenReturn(mockWorkspaceInfo)
- whenever(createIconRequestInfo(any(), any())).thenReturn(mockIconRequestInfo)
- }
- mockUserCache =
- mock<UserCache>().apply {
- val userIconInfo =
- mock<UserIconInfo>().apply { whenever(isPrivate).thenReturn(false) }
- whenever(getUserInfo(any())).thenReturn(userIconInfo)
- }
+ whenever(mockIconCache.getShortcutIcon(any(), any(), any())).then {}
+ whenever(mockPmHelper.getAppLaunchIntent(mComponentName.packageName, mUserHandle))
+ .thenReturn(mIntent)
+ mockCursor.apply {
+ user = mUserHandle
+ itemType = ITEM_TYPE_APPLICATION
+ id = 1
+ restoreFlag = 1
+ serialNumber = 101
+ whenever(parseIntent()).thenReturn(mIntent)
+ whenever(markRestored()).doAnswer { restoreFlag = 0 }
+ whenever(updater().put(Favorites.INTENT, mIntent.toUri(0)).commit()).thenReturn(1)
+ whenever(getAppShortcutInfo(any(), any(), any(), any())).thenReturn(mockWorkspaceInfo)
+ whenever(createIconRequestInfo(any(), any())).thenReturn(mockIconRequestInfo)
+ }
+ mockUserCache.apply {
+ val userIconInfo = mock<UserIconInfo>().apply { whenever(isPrivate).thenReturn(false) }
+ whenever(getUserInfo(any())).thenReturn(userIconInfo)
+ }
- mockUserManagerState = mock<UserManagerState>()
- mockWidgetInflater = mock<WidgetInflater>()
mKeyToPinnedShortcutsMap = mutableMapOf()
mInstallingPkgs = hashMapOf()
mAllDeepShortcuts = mutableListOf()
@@ -187,7 +174,6 @@
userManagerState: UserManagerState = mockUserManagerState,
launcherApps: LauncherApps = mLauncherApps,
shortcutKeyToPinnedShortcuts: Map<ShortcutKey, ShortcutInfo> = mKeyToPinnedShortcutsMap,
- app: LauncherAppState = mockAppState,
bgDataModel: BgDataModel = mockBgDataModel,
widgetProvidersMap: MutableMap<ComponentKey, AppWidgetProviderInfo?> = mWidgetProvidersMap,
widgetInflater: WidgetInflater = mockWidgetInflater,
@@ -205,7 +191,7 @@
userCache = userCache,
userManagerState = userManagerState,
launcherApps = launcherApps,
- app = app,
+ context = mContext,
bgDataModel = bgDataModel,
widgetProvidersMap = widgetProvidersMap,
widgetInflater = widgetInflater,
@@ -217,6 +203,9 @@
shortcutKeyToPinnedShortcuts = shortcutKeyToPinnedShortcuts,
installingPkgs = installingPkgs,
allDeepShortcuts = allDeepShortcuts,
+ iconCache = mockIconCache,
+ idp = InvariantDeviceProfile.INSTANCE.get(mContext),
+ isSafeMode = false,
)
@Test
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemSpaceFinderTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemSpaceFinderTest.kt
index dd03eee..a18f93b 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemSpaceFinderTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemSpaceFinderTest.kt
@@ -29,8 +29,6 @@
@RunWith(AndroidJUnit4::class)
class WorkspaceItemSpaceFinderTest : AbstractWorkspaceModelTest() {
- private val mItemSpaceFinder = WorkspaceItemSpaceFinder()
-
@Before
override fun setup() {
super.setup()
@@ -42,15 +40,12 @@
}
private fun findSpace(spanX: Int, spanY: Int): NewItemSpace =
- mItemSpaceFinder
- .findSpaceForItem(
- mAppState,
+ WorkspaceItemSpaceFinder(
mModelHelper.bgDataModel,
- mExistingScreens,
- mNewScreens,
- spanX,
- spanY,
+ mAppState.invariantDeviceProfile,
+ mModelHelper.model,
)
+ .findSpaceForItem(mExistingScreens, mNewScreens, spanX, spanY)
.let { NewItemSpace.fromIntArray(it) }
private fun assertRegionVacant(newItemSpace: NewItemSpace, spanX: Int, spanY: Int) {
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/ActivityContextWrapper.java b/tests/multivalentTests/src/com/android/launcher3/util/ActivityContextWrapper.java
index 4217d22..f4dc88d 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/ActivityContextWrapper.java
+++ b/tests/multivalentTests/src/com/android/launcher3/util/ActivityContextWrapper.java
@@ -18,23 +18,15 @@
import android.R;
import android.content.Context;
import android.content.ContextWrapper;
-import android.view.ContextThemeWrapper;
import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.BaseDragLayer;
-import java.util.ArrayList;
-import java.util.List;
-
/**
* {@link ContextWrapper} with internal Launcher interface for testing
*/
-public class ActivityContextWrapper extends ContextThemeWrapper implements ActivityContext {
-
- private final List<OnDeviceProfileChangeListener> mDpChangeListeners = new ArrayList<>();
+public class ActivityContextWrapper extends BaseContext {
private final DeviceProfile mProfile;
private final MyDragLayer mMyDragLayer;
@@ -47,20 +39,15 @@
super(base, theme);
mProfile = InvariantDeviceProfile.INSTANCE.get(base).getDeviceProfile(base).copy(base);
mMyDragLayer = new MyDragLayer(this);
+ Executors.MAIN_EXECUTOR.execute(this::onViewCreated);
}
-
@Override
public BaseDragLayer getDragLayer() {
return mMyDragLayer;
}
@Override
- public List<OnDeviceProfileChangeListener> getOnDeviceProfileChangeListeners() {
- return mDpChangeListeners;
- }
-
- @Override
public DeviceProfile getDeviceProfile() {
return mProfile;
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/LauncherBindableItemsContainerTest.kt b/tests/multivalentTests/src/com/android/launcher3/util/LauncherBindableItemsContainerTest.kt
index 93be5f5..054c90b 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/LauncherBindableItemsContainerTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/util/LauncherBindableItemsContainerTest.kt
@@ -122,9 +122,10 @@
val items = mutableMapOf<ItemInfo, View>()
- override fun mapOverItems(op: ItemOperator) {
- items.forEach { (item, view) -> if (op.evaluate(item, view)) return@forEach }
- }
+ override fun mapOverItems(op: ItemOperator): View? =
+ items.firstNotNullOfOrNull { (item, view) ->
+ if (op.evaluate(item, view)) view else null
+ }
fun addIcon(info: WorkspaceItemInfo) {
val btv = BubbleTextView(this)
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/SimpleBroadcastReceiverTest.kt b/tests/multivalentTests/src/com/android/launcher3/util/SimpleBroadcastReceiverTest.kt
index 17933f2..97651d3 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/SimpleBroadcastReceiverTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/util/SimpleBroadcastReceiverTest.kt
@@ -127,6 +127,29 @@
}
@Test
+ fun sync_register_withCompletionRunnable_and_permission_and_flag() {
+ underTest =
+ SimpleBroadcastReceiver(context, Handler(Looper.getMainLooper()), intentConsumer)
+
+ underTest.register(completionRunnable, "permission", 1, "test_action_1", "test_action_2")
+ getInstrumentation().waitForIdleSync()
+
+ verify(context)
+ .registerReceiver(
+ same(underTest),
+ intentFilterCaptor.capture(),
+ eq("permission"),
+ eq(null),
+ eq(1),
+ )
+ verify(completionRunnable).run()
+ val intentFilter = intentFilterCaptor.value
+ assertThat(intentFilter.countActions()).isEqualTo(2)
+ assertThat(intentFilter.getAction(0)).isEqualTo("test_action_1")
+ assertThat(intentFilter.getAction(1)).isEqualTo("test_action_2")
+ }
+
+ @Test
fun async_unregister() {
underTest.unregisterReceiverSafely()
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/TestSandboxModelContextWrapper.java b/tests/multivalentTests/src/com/android/launcher3/util/TestSandboxModelContextWrapper.java
index 8be1341..acd17d1 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/TestSandboxModelContextWrapper.java
+++ b/tests/multivalentTests/src/com/android/launcher3/util/TestSandboxModelContextWrapper.java
@@ -28,6 +28,7 @@
import com.android.launcher3.allapps.AllAppsStore;
import com.android.launcher3.allapps.AlphabeticalAppsList;
import com.android.launcher3.model.BgDataModel;
+import com.android.launcher3.model.WidgetsFilterDataProvider;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.popup.PopupDataProvider;
@@ -58,7 +59,7 @@
private final PopupDataProvider mPopupDataProvider = new PopupDataProvider(this);
private final WidgetPickerDataProvider mWidgetPickerDataProvider =
- new WidgetPickerDataProvider();
+ new WidgetPickerDataProvider(new WidgetsFilterDataProvider());
protected final UserCache mUserCache;
public TestSandboxModelContextWrapper(SandboxContext base) {
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/rule/ZipFilesRule.kt b/tests/multivalentTests/src/com/android/launcher3/util/rule/ZipFilesRule.kt
new file mode 100644
index 0000000..d12de76
--- /dev/null
+++ b/tests/multivalentTests/src/com/android/launcher3/util/rule/ZipFilesRule.kt
@@ -0,0 +1,65 @@
+/*
+ * 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.rule
+
+import android.content.Context
+import android.os.FileUtils
+import java.io.File
+import java.io.FileInputStream
+import java.io.FileOutputStream
+import java.util.zip.ZipEntry
+import java.util.zip.ZipOutputStream
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+class ZipFilesRule(val context: Context, val name: String) : TestRule {
+
+ var resultsZip: ZipOutputStream? = null
+
+ override fun apply(base: Statement, description: Description): Statement {
+ return object : Statement() {
+ @Throws(Throwable::class)
+ override fun evaluate() {
+ resultsZip =
+ ZipOutputStream(
+ FileOutputStream(
+ File(context.filesDir, "${description.testClass.simpleName}-$name.zip")
+ )
+ )
+ try {
+ base.evaluate() // This will run the test.
+ } finally {
+ resultsZip?.close()
+ }
+ }
+ }
+ }
+
+ fun write(file: File) {
+ if (resultsZip !is ZipOutputStream) {
+ throw RuntimeException(
+ "Cannot save files before the test rule starts! We need the rule to start to get the name of the test"
+ )
+ }
+ resultsZip!!.let {
+ it.putNextEntry(ZipEntry(file.name))
+ FileUtils.copy(FileInputStream(file), it)
+ it.closeEntry()
+ }
+ }
+}
diff --git a/tests/multivalentTests/src/com/android/launcher3/widget/picker/model/WidgetPickerDataProviderTest.kt b/tests/multivalentTests/src/com/android/launcher3/widget/picker/model/WidgetPickerDataProviderTest.kt
index 1da74cb..c1827bc 100644
--- a/tests/multivalentTests/src/com/android/launcher3/widget/picker/model/WidgetPickerDataProviderTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/widget/picker/model/WidgetPickerDataProviderTest.kt
@@ -40,6 +40,7 @@
import com.android.launcher3.widget.model.WidgetsListHeaderEntry
import com.android.launcher3.widget.picker.model.WidgetPickerDataProvider.WidgetPickerDataChangeListener
import com.google.common.truth.Truth.assertThat
+import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -72,14 +73,14 @@
private lateinit var appWidgetItem: WidgetItem
- private var underTest = WidgetPickerDataProvider()
+ private lateinit var underTest: WidgetPickerDataProvider
@Before
fun setUp() {
userHandle = UserHandle.CURRENT
context = ActivityContextWrapper(ApplicationProvider.getApplicationContext())
testInvariantProfile = LauncherAppState.getIDP(context)
-
+ underTest = WidgetPickerDataProvider(context)
doAnswer { invocation: InvocationOnMock ->
val componentWithLabel = invocation.getArgument<Any>(0) as CachedObject
componentWithLabel.getComponent().shortClassName
@@ -90,6 +91,11 @@
appWidgetItem = createWidgetItem()
}
+ @After
+ fun tearDown() {
+ underTest.destroy()
+ }
+
@Test
fun setWidgets_invokesTheListener_andUpdatedWidgetsAvailable() {
assertThat(underTest.get().allWidgets).isEmpty()
diff --git a/tests/src/com/android/launcher3/backuprestore/BackupAndRestoreDBSelectionTest.kt b/tests/src/com/android/launcher3/backuprestore/BackupAndRestoreDBSelectionTest.kt
index 1e54603..38fad6b 100644
--- a/tests/src/com/android/launcher3/backuprestore/BackupAndRestoreDBSelectionTest.kt
+++ b/tests/src/com/android/launcher3/backuprestore/BackupAndRestoreDBSelectionTest.kt
@@ -32,7 +32,6 @@
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
@@ -70,7 +69,6 @@
}
@Test
- @Ignore("b/385147987")
fun oldDatabasesNotPresentAfterRestore() {
val dbController = ModelDbController(getInstrumentation().targetContext)
if (Flags.gridMigrationRefactor()) {
diff --git a/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java b/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java
index 95d5076..f490bd6 100644
--- a/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java
+++ b/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java
@@ -36,7 +36,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;
import org.junit.Test;
@@ -127,7 +126,6 @@
* Adds three icons to the workspace and removes one of them by dragging to uninstall.
*/
@Test
- @ScreenRecordRule.ScreenRecord // b/399756302
@PlatinumTest(focusArea = "launcher")
public void uninstallWorkspaceIcon() throws IOException {
Point[] gridPositions = TestUtil.getCornersAndCenterPositions(mLauncher);
diff --git a/tests/src/com/android/launcher3/model/LoaderTaskTest.kt b/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
index 582cf3c..7f9b7a0 100644
--- a/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
+++ b/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
@@ -16,7 +16,6 @@
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn
import com.android.launcher3.Flags
-import com.android.launcher3.LauncherAppState
import com.android.launcher3.LauncherModel
import com.android.launcher3.LauncherModel.LoaderTransaction
import com.android.launcher3.LauncherPrefs
@@ -31,6 +30,7 @@
import com.android.launcher3.icons.IconCache
import com.android.launcher3.icons.cache.CachingLogic
import com.android.launcher3.icons.cache.IconCacheUpdateHandler
+import com.android.launcher3.model.LoaderTask.LoaderTaskFactory
import com.android.launcher3.model.data.AppInfo
import com.android.launcher3.model.data.IconRequestInfo
import com.android.launcher3.model.data.WorkspaceItemInfo
@@ -43,12 +43,10 @@
import com.android.launcher3.util.LooperIdleLock
import com.android.launcher3.util.TestUtil
import com.android.launcher3.util.UserIconInfo
-import com.google.common.truth.Truth
import com.google.common.truth.Truth.assertThat
import dagger.BindsInstance
import dagger.Component
import java.util.concurrent.CountDownLatch
-import java.util.function.Predicate
import junit.framework.Assert.assertEquals
import org.junit.After
import org.junit.Before
@@ -58,7 +56,6 @@
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito
-import org.mockito.Mockito.mock
import org.mockito.Mockito.times
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@@ -94,27 +91,28 @@
@Mock private lateinit var bgAllAppsList: AllAppsList
@Mock private lateinit var modelDelegate: ModelDelegate
- @Mock private lateinit var launcherBinder: BaseLauncherBinder
- private lateinit var launcherModel: LauncherModel
- @Mock private lateinit var widgetsFilterDataProvider: WidgetsFilterDataProvider
- @Mock private lateinit var transaction: LoaderTransaction
+ @Mock private lateinit var launcherModel: LauncherModel
@Mock private lateinit var iconCache: IconCache
- @Mock private lateinit var idleLock: LooperIdleLock
- @Mock private lateinit var iconCacheUpdateHandler: IconCacheUpdateHandler
@Mock private lateinit var userCache: UserCache
- @Spy private var userManagerState: UserManagerState? = UserManagerState()
+ @Mock private lateinit var launcherBinder: BaseLauncherBinder
+ @Mock private lateinit var transaction: LoaderTransaction
+ @Mock private lateinit var idleLock: LooperIdleLock
+ @Mock private lateinit var iconCacheUpdateHandler: IconCacheUpdateHandler
+
+ @Spy private var userManagerState: UserManagerState = UserManagerState()
@get:Rule val setFlagsRule = SetFlagsRule()
- private val app: LauncherAppState
- get() = context.appComponent.launcherAppState
+ private val testComponent: TestComponent
+ get() = context.appComponent as TestComponent
+
+ private val bgDataModel: BgDataModel
+ get() = testComponent.getDataModel()
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- setFlagsRule.enableFlags(Flags.FLAG_ENABLE_TIERED_WIDGETS_BY_DEFAULT_IN_PICKER)
- launcherModel = mock(LauncherModel::class.java)
mockitoSession =
ExtendedMockito.mockitoSession()
.strictness(Strictness.LENIENT)
@@ -127,15 +125,17 @@
`when`(launcherModel.beginLoader(any())).thenReturn(transaction)
`when`(launcherModel.modelDbController)
.thenReturn(FactitiousDbController(context, INSERTION_STATEMENT_FILE))
+ `when`(launcherModel.modelDelegate).thenReturn(modelDelegate)
`when`(launcherBinder.newIdleLock(any())).thenReturn(idleLock)
`when`(idleLock.awaitLocked(1000)).thenReturn(false)
`when`(iconCache.getUpdateHandler()).thenReturn(iconCacheUpdateHandler)
- `when`(widgetsFilterDataProvider.getDefaultWidgetsFilter()).thenReturn(Predicate { true })
+
context.initDaggerComponent(
DaggerLoaderTaskTest_TestComponent.builder()
.bindUserCache(userCache)
.bindIconCache(iconCache)
.bindLauncherModel(launcherModel)
+ .bindAllAppsList(bgAllAppsList)
)
context.appComponent.idp.apply {
numRows = 5
@@ -155,21 +155,16 @@
@Test
fun loadsDataProperly() =
- with(BgDataModel()) {
+ with(bgDataModel) {
val MAIN_HANDLE = Process.myUserHandle()
val mockUserHandles = arrayListOf<UserHandle>(MAIN_HANDLE)
`when`(userCache.userProfiles).thenReturn(mockUserHandles)
`when`(userCache.getUserInfo(MAIN_HANDLE)).thenReturn(UserIconInfo(MAIN_HANDLE, 1))
- LoaderTask(
- app,
- bgAllAppsList,
- this,
- modelDelegate,
- launcherBinder,
- widgetsFilterDataProvider,
- )
+ testComponent
+ .getLoaderTaskFactory()
+ .newLoaderTask(launcherBinder, userManagerState)
.runSyncOnBackgroundThread()
- Truth.assertThat(
+ assertThat(
itemsIdMap
.filter {
it.container == CONTAINER_DESKTOP || it.container == CONTAINER_HOTSEAT
@@ -177,9 +172,8 @@
.size
)
.isAtLeast(32)
- Truth.assertThat(itemsIdMap.filter { ModelUtils.WIDGET_FILTER.test(it) }.size)
- .isAtLeast(7)
- Truth.assertThat(
+ assertThat(itemsIdMap.filter { ModelUtils.WIDGET_FILTER.test(it) }.size).isAtLeast(7)
+ assertThat(
itemsIdMap
.filter {
it.itemType == ITEM_TYPE_FOLDER || it.itemType == ITEM_TYPE_APP_PAIR
@@ -187,20 +181,14 @@
.size
)
.isAtLeast(8)
- Truth.assertThat(itemsIdMap.size()).isAtLeast(40)
- Truth.assertThat(widgetsModel.defaultWidgetsFilter).isNotNull()
+ assertThat(itemsIdMap.size()).isAtLeast(40)
}
@Test
fun bindsLoadedDataCorrectly() {
- LoaderTask(
- app,
- bgAllAppsList,
- BgDataModel(),
- modelDelegate,
- launcherBinder,
- widgetsFilterDataProvider,
- )
+ testComponent
+ .getLoaderTaskFactory()
+ .newLoaderTask(launcherBinder, userManagerState)
.runSyncOnBackgroundThread()
verify(launcherBinder).bindWorkspace(true, false)
@@ -209,7 +197,6 @@
verify(launcherBinder).bindAllApps()
verify(iconCacheUpdateHandler, times(4)).updateIcons(any(), any<CachingLogic<Any>>(), any())
verify(launcherBinder).bindDeepShortcuts()
- verify(widgetsFilterDataProvider).initPeriodicDataRefresh(any())
verify(launcherBinder).bindWidgets()
verify(modelDelegate).loadAndBindOtherItems(anyOrNull())
verify(iconCacheUpdateHandler).finish()
@@ -219,7 +206,7 @@
@Test
fun setsQuietModeFlagCorrectlyForWorkProfile() =
- with(BgDataModel()) {
+ with(bgDataModel) {
setFlagsRule.enableFlags(Flags.FLAG_ENABLE_PRIVATE_SPACE)
val MAIN_HANDLE = Process.myUserHandle()
val mockUserHandles = arrayListOf<UserHandle>(MAIN_HANDLE)
@@ -227,15 +214,9 @@
`when`(userManagerState?.isUserQuiet(MAIN_HANDLE)).thenReturn(true)
`when`(userCache.getUserInfo(MAIN_HANDLE)).thenReturn(UserIconInfo(MAIN_HANDLE, 1))
- LoaderTask(
- app,
- bgAllAppsList,
- this,
- modelDelegate,
- launcherBinder,
- widgetsFilterDataProvider,
- userManagerState,
- )
+ testComponent
+ .getLoaderTaskFactory()
+ .newLoaderTask(launcherBinder, userManagerState)
.runSyncOnBackgroundThread()
verify(bgAllAppsList)
@@ -248,7 +229,7 @@
@Test
fun setsQuietModeFlagCorrectlyForPrivateProfile() =
- with(BgDataModel()) {
+ with(bgDataModel) {
setFlagsRule.enableFlags(Flags.FLAG_ENABLE_PRIVATE_SPACE)
val MAIN_HANDLE = Process.myUserHandle()
val mockUserHandles = arrayListOf<UserHandle>(MAIN_HANDLE)
@@ -256,15 +237,9 @@
`when`(userManagerState?.isUserQuiet(MAIN_HANDLE)).thenReturn(true)
`when`(userCache.getUserInfo(MAIN_HANDLE)).thenReturn(UserIconInfo(MAIN_HANDLE, 3))
- LoaderTask(
- app,
- bgAllAppsList,
- this,
- modelDelegate,
- launcherBinder,
- widgetsFilterDataProvider,
- userManagerState,
- )
+ testComponent
+ .getLoaderTaskFactory()
+ .newLoaderTask(launcherBinder, userManagerState)
.runSyncOnBackgroundThread()
verify(bgAllAppsList)
@@ -303,14 +278,9 @@
RestoreDbTask.setPending(spyContext)
// When
- LoaderTask(
- app,
- bgAllAppsList,
- BgDataModel(),
- modelDelegate,
- launcherBinder,
- widgetsFilterDataProvider,
- )
+ testComponent
+ .getLoaderTaskFactory()
+ .newLoaderTask(launcherBinder, userManagerState)
.runSyncOnBackgroundThread()
// Then
@@ -378,14 +348,9 @@
Settings.Secure.putInt(spyContext.contentResolver, "launcher_broadcast_installed_apps", 0)
// When
- LoaderTask(
- app,
- bgAllAppsList,
- BgDataModel(),
- modelDelegate,
- launcherBinder,
- widgetsFilterDataProvider,
- )
+ testComponent
+ .getLoaderTaskFactory()
+ .newLoaderTask(launcherBinder, userManagerState)
.runSyncOnBackgroundThread()
// Then
@@ -424,14 +389,9 @@
RestoreDbTask.setPending(spyContext)
// When
- LoaderTask(
- app,
- bgAllAppsList,
- BgDataModel(),
- modelDelegate,
- launcherBinder,
- widgetsFilterDataProvider,
- )
+ testComponent
+ .getLoaderTaskFactory()
+ .newLoaderTask(launcherBinder, userManagerState)
.runSyncOnBackgroundThread()
// Then
@@ -470,14 +430,9 @@
RestoreDbTask.setPending(spyContext)
// When
- LoaderTask(
- app,
- bgAllAppsList,
- BgDataModel(),
- modelDelegate,
- launcherBinder,
- widgetsFilterDataProvider,
- )
+ testComponent
+ .getLoaderTaskFactory()
+ .newLoaderTask(launcherBinder, userManagerState)
.runSyncOnBackgroundThread()
// Then
@@ -508,14 +463,7 @@
val expectedAppInfo = AppInfo().apply { componentName = expectedComponent }
// When
val loader =
- LoaderTask(
- app,
- bgAllAppsList,
- BgDataModel(),
- modelDelegate,
- launcherBinder,
- widgetsFilterDataProvider,
- )
+ testComponent.getLoaderTaskFactory().newLoaderTask(launcherBinder, userManagerState)
val actualIconRequest =
loader.getAppInfoIconRequestInfo(expectedAppInfo, activityInfo, workspaceIconRequests)
// Then
@@ -546,14 +494,7 @@
val expectedAppInfo = AppInfo().apply { componentName = expectedComponent }
// When
val loader =
- LoaderTask(
- app,
- bgAllAppsList,
- BgDataModel(),
- modelDelegate,
- launcherBinder,
- widgetsFilterDataProvider,
- )
+ testComponent.getLoaderTaskFactory().newLoaderTask(launcherBinder, userManagerState)
val actualIconRequest =
loader.getAppInfoIconRequestInfo(expectedAppInfo, activityInfo, workspaceIconRequests)
// Then
@@ -585,14 +526,7 @@
AppInfo().apply { componentName = ComponentName("differentPkg", "differentClass") }
// When
val loader =
- LoaderTask(
- app,
- bgAllAppsList,
- BgDataModel(),
- modelDelegate,
- launcherBinder,
- widgetsFilterDataProvider,
- )
+ testComponent.getLoaderTaskFactory().newLoaderTask(launcherBinder, userManagerState)
val actualIconRequest =
loader.getAppInfoIconRequestInfo(expectedAppInfo, activityInfo, workspaceIconRequests)
// Then
@@ -620,14 +554,7 @@
val expectedAppInfo = AppInfo()
// When
val loader =
- LoaderTask(
- app,
- bgAllAppsList,
- BgDataModel(),
- modelDelegate,
- launcherBinder,
- widgetsFilterDataProvider,
- )
+ testComponent.getLoaderTaskFactory().newLoaderTask(launcherBinder, userManagerState)
val actualIconRequest =
loader.getAppInfoIconRequestInfo(expectedAppInfo, activityInfo, workspaceIconRequests)
// Then
@@ -638,6 +565,11 @@
@LauncherAppSingleton
@Component(modules = [AllModulesForTest::class])
interface TestComponent : LauncherAppComponent {
+
+ fun getLoaderTaskFactory(): LoaderTaskFactory
+
+ fun getDataModel(): BgDataModel
+
@Component.Builder
interface Builder : LauncherAppComponent.Builder {
@BindsInstance fun bindUserCache(userCache: UserCache): Builder
@@ -646,6 +578,8 @@
@BindsInstance fun bindIconCache(iconCache: IconCache): Builder
+ @BindsInstance fun bindAllAppsList(list: AllAppsList): Builder
+
override fun build(): TestComponent
}
}
diff --git a/tests/src/com/android/launcher3/model/WorkspaceItemProcessorExtraTest.kt b/tests/src/com/android/launcher3/model/WorkspaceItemProcessorExtraTest.kt
index 8db049c..fd44023 100644
--- a/tests/src/com/android/launcher3/model/WorkspaceItemProcessorExtraTest.kt
+++ b/tests/src/com/android/launcher3/model/WorkspaceItemProcessorExtraTest.kt
@@ -30,12 +30,13 @@
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.launcher3.Flags.FLAG_ENABLE_SUPPORT_FOR_ARCHIVING
-import com.android.launcher3.LauncherAppState
+import com.android.launcher3.InvariantDeviceProfile
import com.android.launcher3.LauncherSettings.Favorites
import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP
import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
import com.android.launcher3.icons.CacheableShortcutInfo
+import com.android.launcher3.icons.IconCache
import com.android.launcher3.model.data.IconRequestInfo
import com.android.launcher3.model.data.LauncherAppWidgetInfo
import com.android.launcher3.model.data.LauncherAppWidgetInfo.FLAG_RESTORE_STARTED
@@ -52,11 +53,12 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Answers
import org.mockito.ArgumentCaptor
import org.mockito.Mock
-import org.mockito.Mockito
import org.mockito.Mockito.RETURNS_DEEP_STUBS
import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.doAnswer
@@ -71,10 +73,10 @@
@Mock private lateinit var mockWorkspaceInfo: WorkspaceItemInfo
@Mock private lateinit var mockBgDataModel: BgDataModel
@Mock private lateinit var mockContext: Context
- @Mock private lateinit var mockAppState: LauncherAppState
+ @Mock private lateinit var mockIconCache: IconCache
@Mock private lateinit var mockPmHelper: PackageManagerHelper
@Mock private lateinit var mockLauncherApps: LauncherApps
- @Mock private lateinit var mockCursor: LoaderCursor
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var mockCursor: LoaderCursor
@Mock private lateinit var mockUserCache: UserCache
@Mock private lateinit var mockUserManagerState: UserManagerState
@Mock private lateinit var mockWidgetInflater: WidgetInflater
@@ -95,10 +97,9 @@
@Before
fun setup() {
+ MockitoAnnotations.initMocks(this)
+
mUserHandle = UserHandle(0)
- mockIconRequestInfo = mock<IconRequestInfo<WorkspaceItemInfo>>()
- mockWorkspaceInfo = mock<WorkspaceItemInfo>()
- mockBgDataModel = mock<BgDataModel>()
mComponentName = ComponentName("package", "class")
mUnlockedUsersArray = LongSparseArray<Boolean>(1).apply { put(101, true) }
intent =
@@ -107,52 +108,36 @@
`package` = "pkg"
putExtra(ShortcutKey.EXTRA_SHORTCUT_ID, "")
}
- mockLauncherApps =
- mock<LauncherApps>().apply {
- whenever(isPackageEnabled("package", mUserHandle)).thenReturn(true)
- whenever(isActivityEnabled(mComponentName, mUserHandle)).thenReturn(true)
- }
- mockContext =
- mock<Context>().apply {
- whenever(packageManager).thenReturn(mock())
- whenever(packageManager.getUserBadgedLabel(any(), any())).thenReturn("")
- whenever(applicationContext).thenReturn(ApplicationProvider.getApplicationContext())
- whenever(getSystemService(LauncherApps::class.java)).thenReturn(mockLauncherApps)
- }
- mockAppState =
- mock<LauncherAppState>().apply {
- whenever(context).thenReturn(mockContext)
- whenever(iconCache).thenReturn(mock())
- whenever(iconCache.getShortcutIcon(any(), any(), any())).then {}
- }
- mockPmHelper =
- mock<PackageManagerHelper>().apply {
- whenever(getAppLaunchIntent(mComponentName.packageName, mUserHandle))
- .thenReturn(intent)
- }
- mockCursor =
- Mockito.mock(LoaderCursor::class.java, RETURNS_DEEP_STUBS).apply {
- user = mUserHandle
- itemType = ITEM_TYPE_APPLICATION
- id = 1
- restoreFlag = 1
- serialNumber = 101
- whenever(parseIntent()).thenReturn(intent)
- whenever(markRestored()).doAnswer { restoreFlag = 0 }
- whenever(updater().put(Favorites.INTENT, intent.toUri(0)).commit()).thenReturn(1)
- whenever(getAppShortcutInfo(any(), any(), any(), any()))
- .thenReturn(mockWorkspaceInfo)
- whenever(createIconRequestInfo(any(), any())).thenReturn(mockIconRequestInfo)
- }
- mockUserCache =
- mock<UserCache>().apply {
- val userIconInfo =
- mock<UserIconInfo>().apply { whenever(isPrivate).thenReturn(false) }
- whenever(getUserInfo(any())).thenReturn(userIconInfo)
- }
+ mockLauncherApps.apply {
+ whenever(isPackageEnabled("package", mUserHandle)).thenReturn(true)
+ whenever(isActivityEnabled(mComponentName, mUserHandle)).thenReturn(true)
+ }
+ mockContext.apply {
+ whenever(packageManager).thenReturn(mock())
+ whenever(packageManager.getUserBadgedLabel(any(), any())).thenReturn("")
+ whenever(applicationContext).thenReturn(ApplicationProvider.getApplicationContext())
+ whenever(getSystemService(LauncherApps::class.java)).thenReturn(mockLauncherApps)
+ }
- mockUserManagerState = mock<UserManagerState>()
- mockWidgetInflater = mock<WidgetInflater>()
+ whenever(mockIconCache.getShortcutIcon(any(), any(), any())).then {}
+ whenever(mockPmHelper.getAppLaunchIntent(mComponentName.packageName, mUserHandle))
+ .thenReturn(intent)
+ mockCursor.apply {
+ user = mUserHandle
+ itemType = ITEM_TYPE_APPLICATION
+ id = 1
+ restoreFlag = 1
+ serialNumber = 101
+ whenever(parseIntent()).thenReturn(intent)
+ whenever(markRestored()).doAnswer { restoreFlag = 0 }
+ whenever(updater().put(Favorites.INTENT, intent.toUri(0)).commit()).thenReturn(1)
+ whenever(getAppShortcutInfo(any(), any(), any(), any())).thenReturn(mockWorkspaceInfo)
+ whenever(createIconRequestInfo(any(), any())).thenReturn(mockIconRequestInfo)
+ }
+
+ val mockUserInfo = mock<UserIconInfo>().apply { whenever(isPrivate).thenReturn(false) }
+ whenever(mockUserCache.getUserInfo(any())).thenReturn(mockUserInfo)
+
mKeyToPinnedShortcutsMap = mutableMapOf()
mInstallingPkgs = hashMapOf()
mAllDeepShortcuts = mutableListOf()
@@ -283,7 +268,7 @@
userManagerState: UserManagerState = mockUserManagerState,
launcherApps: LauncherApps = mockLauncherApps,
shortcutKeyToPinnedShortcuts: Map<ShortcutKey, ShortcutInfo> = mKeyToPinnedShortcutsMap,
- app: LauncherAppState = mockAppState,
+ context: Context = mockContext,
bgDataModel: BgDataModel = mockBgDataModel,
widgetProvidersMap: MutableMap<ComponentKey, AppWidgetProviderInfo?> = mWidgetProvidersMap,
widgetInflater: WidgetInflater = mockWidgetInflater,
@@ -297,11 +282,11 @@
) =
WorkspaceItemProcessor(
c = cursor,
+ context = context,
memoryLogger = memoryLogger,
userCache = userCache,
userManagerState = userManagerState,
launcherApps = launcherApps,
- app = app,
bgDataModel = bgDataModel,
widgetProvidersMap = widgetProvidersMap,
widgetInflater = widgetInflater,
@@ -313,5 +298,8 @@
shortcutKeyToPinnedShortcuts = shortcutKeyToPinnedShortcuts,
installingPkgs = installingPkgs,
allDeepShortcuts = allDeepShortcuts,
+ idp = InvariantDeviceProfile.INSTANCE.get(context),
+ iconCache = mockIconCache,
+ isSafeMode = false,
)
}
diff --git a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
index 124c18f..e38cfec 100644
--- a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
+++ b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
@@ -10,7 +10,6 @@
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.tapl.TestHelpers;
import com.android.launcher3.util.rule.FailureWatcher;
-import com.android.launcher3.util.window.WindowManagerProxy;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
@@ -97,19 +96,20 @@
}
private void evaluateInLandscape() throws Throwable {
- if (Flags.oneGridSpecs()
- && WindowManagerProxy.INSTANCE.get(mTest.mTargetContext)
- .isTaskbarDrawnInProcess()) {
- mTest.executeOnLauncher(launcher -> LauncherPrefs.get(launcher)
- .put(FIXED_LANDSCAPE_MODE, true)
- );
- }
+ mTest.executeOnLauncher(launcher -> LauncherPrefs.get(launcher)
+ .put(FIXED_LANDSCAPE_MODE, shouldHaveFixedLandscape(launcher)));
mTest.mDevice.setOrientationLeft();
mTest.mLauncher.setExpectedRotation(Surface.ROTATION_90);
AbstractLauncherUiTest.checkDetectedLeaks(mTest.mLauncher, true);
base.evaluate();
mTest.getDevice().pressHome();
}
+
+ private boolean shouldHaveFixedLandscape(Launcher launcher) {
+ return Flags.oneGridSpecs()
+ && !launcher.getDeviceProfile().isTablet
+ && !launcher.getDeviceProfile().isMultiDisplay;
+ }
};
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index de31c4d..e0d2f39 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -929,7 +929,11 @@
waitUntilSystemLauncherObjectGone(OVERVIEW_RES_ID);
waitUntilSystemLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
waitUntilLauncherObjectGone(KEYBOARD_QUICK_SWITCH_RES_ID);
- waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
+ if (isTaskbarShownOnHome()) {
+ waitForSystemLauncherObject(TASKBAR_RES_ID);
+ } else {
+ waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
+ }
return waitForLauncherObject(WORKSPACE_RES_ID);
}
@@ -961,7 +965,8 @@
waitUntilSystemLauncherObjectGone(OVERVIEW_RES_ID);
waitUntilLauncherObjectGone(KEYBOARD_QUICK_SWITCH_RES_ID);
- if (is3PLauncher() && isTablet() && !isTransientTaskbar()) {
+ if ((is3PLauncher() && isTablet() && !isTransientTaskbar())
+ || isTaskbarShownOnHome()) {
waitForSystemLauncherObject(TASKBAR_RES_ID);
} else {
waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
@@ -2423,6 +2428,12 @@
.getBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
+ /** Whether taskbar will be shown on home for current default display. */
+ public boolean isTaskbarShownOnHome() {
+ return getTestInfo(TestProtocol.REQUEST_TASKBAR_SHOWN_ON_HOME).getBoolean(
+ TEST_INFO_RESPONSE_FIELD);
+ }
+
public boolean isImeDocked() {
return getTestInfo(TestProtocol.REQUEST_TASKBAR_IME_DOCKED).getBoolean(
TestProtocol.TEST_INFO_RESPONSE_FIELD);
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
index 8fbb5e3..02c6630 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -73,16 +73,18 @@
return getCombinedSplitTaskHeight();
}
- UiObject2 taskSnapshot1 = findObjectInTask((isDesktop() ? DESKTOP : DEFAULT).snapshotRes);
- return taskSnapshot1.getVisibleBounds().height();
+ if (isDesktop()) {
+ return getTaskSnapshot(DESKTOP).getVisibleBounds().height();
+ }
+ return getTaskSnapshot(DEFAULT).getVisibleBounds().height();
}
/**
* Calculates the visible height for split tasks, containing 2 snapshot tiles and a divider.
*/
private int getCombinedSplitTaskHeight() {
- UiObject2 taskSnapshot1 = findObjectInTask(SPLIT_TOP_OR_LEFT.snapshotRes);
- UiObject2 taskSnapshot2 = findObjectInTask(SPLIT_BOTTOM_OR_RIGHT.snapshotRes);
+ UiObject2 taskSnapshot1 = getTaskSnapshot(SPLIT_TOP_OR_LEFT);
+ UiObject2 taskSnapshot2 = getTaskSnapshot(SPLIT_BOTTOM_OR_RIGHT);
// If the split task is partly off screen, taskSnapshot1 can be invisible.
if (taskSnapshot1 == null) {
@@ -97,34 +99,6 @@
return bottom - top;
}
- /**
- * Returns the width of the visible task, or the combined width of two tasks in split with a
- * divider between.
- */
- int getVisibleWidth() {
- if (isGrouped()) {
- return getCombinedSplitTaskWidth();
- }
-
- UiObject2 taskSnapshot1 = findObjectInTask(DEFAULT.snapshotRes);
- return taskSnapshot1.getVisibleBounds().width();
- }
-
- /**
- * Calculates the visible width for split tasks, containing 2 snapshot tiles and a divider.
- */
- private int getCombinedSplitTaskWidth() {
- UiObject2 taskSnapshot1 = findObjectInTask(SPLIT_TOP_OR_LEFT.snapshotRes);
- UiObject2 taskSnapshot2 = findObjectInTask(SPLIT_BOTTOM_OR_RIGHT.snapshotRes);
-
- int left = Math.min(
- taskSnapshot1.getVisibleBounds().left, taskSnapshot2.getVisibleBounds().left);
- int right = Math.max(
- taskSnapshot1.getVisibleBounds().right, taskSnapshot2.getVisibleBounds().right);
-
- return right - left;
- }
-
public int getTaskCenterX() {
return mTask.getVisibleCenter().x;
}
@@ -142,6 +116,22 @@
}
/**
+ * Returns the task snapshot (thumbnail) for the given `OverviewTaskContainer`.
+ *
+ * For some reason `BySelector` does not work with `hasChild` or `hasParent` so instead we
+ * grab all the views matching the id: "snapshot" and filter for the correct parent.
+ */
+ private UiObject2 getTaskSnapshot(OverviewTaskContainer overviewTaskContainer) {
+ BySelector snapshotSelector = mLauncher.getOverviewObjectSelector("snapshot");
+ List<UiObject2> snapshots = mTask.findObjects(snapshotSelector);
+ return snapshots.stream()
+ .filter(snapshot -> snapshot.getParent().getResourceName()
+ .contains(overviewTaskContainer.taskContentViewRes))
+ .findFirst()
+ .orElse(snapshots.getFirst());
+ }
+
+ /**
* Dismisses the task by swiping up.
*/
public void dismiss() {
@@ -304,17 +294,13 @@
}
}
- private UiObject2 findObjectInTask(String resName) {
- return mTask.findObject(mLauncher.getOverviewObjectSelector(resName));
- }
-
/**
* Returns whether the given String is contained in this Task's contentDescription. Also returns
* true if both Strings are null.
*/
public boolean containsContentDescription(String expected,
OverviewTaskContainer overviewTaskContainer) {
- String actual = findObjectInTask(overviewTaskContainer.snapshotRes).getContentDescription();
+ String actual = getTaskSnapshot(overviewTaskContainer).getContentDescription();
if (actual == null && expected == null) {
return true;
}
@@ -360,19 +346,19 @@
*/
public enum OverviewTaskContainer {
// The main task when the task is not split.
- DEFAULT("snapshot", "icon"),
+ DEFAULT("task_content_view", "icon"),
// The first task in split task.
- SPLIT_TOP_OR_LEFT("snapshot", "icon"),
+ SPLIT_TOP_OR_LEFT("task_content_view", "icon"),
// The second task in split task.
- SPLIT_BOTTOM_OR_RIGHT("bottomright_snapshot", "bottomRight_icon"),
+ SPLIT_BOTTOM_OR_RIGHT("bottomright_task_content_view", "bottomRight_icon"),
// The desktop task.
DESKTOP("background", "icon");
- public final String snapshotRes;
+ public final String taskContentViewRes;
public final String iconAppRes;
- OverviewTaskContainer(String snapshotRes, String iconAppRes) {
- this.snapshotRes = snapshotRes;
+ OverviewTaskContainer(String taskContentViewRes, String iconAppRes) {
+ this.taskContentViewRes = taskContentViewRes;
this.iconAppRes = iconAppRes;
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Taskbar.java b/tests/tapl/com/android/launcher3/tapl/Taskbar.java
index b4aaab7..d4e6d31 100644
--- a/tests/tapl/com/android/launcher3/tapl/Taskbar.java
+++ b/tests/tapl/com/android/launcher3/tapl/Taskbar.java
@@ -115,6 +115,23 @@
}
}
+ /**
+ * Opens the Home all apps page by clicking the taskbar all apps icon. To be used to open all
+ * apps when taskbar is visible on home.
+ */
+ public HomeAllApps openAllAppsOnHome() {
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "want to open home all apps from taskbar");
+ LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+
+ mLauncher.clickLauncherObject(mLauncher.waitForObjectInContainer(
+ mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID),
+ getAllAppsButtonSelector()));
+
+ return mLauncher.getAllApps();
+ }
+ }
+
/** Opens the Taskbar all apps page with the meta keyboard shortcut. */
public TaskbarAllApps openAllAppsFromKeyboardShortcut() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 4230643..68c7049 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -58,7 +58,7 @@
*/
public final class Workspace extends Home {
private static final int FLING_STEPS = 10;
- private static final int DEFAULT_DRAG_STEPS = 15;
+ private static final int DEFAULT_DRAG_STEPS = 20;
private static final String DROP_BAR_RES_ID = "drop_target_bar";
private static final String DELETE_TARGET_TEXT_ID = "delete_target_text";
private static final String UNINSTALL_TARGET_TEXT_ID = "uninstall_target_text";
@@ -609,7 +609,7 @@
launcher,
launchable,
destSupplier,
- /* isDecelerating= */ false,
+ /* isDecelerating= */ !isDraggingToFolder,
() -> launcher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT),
/* expectDropEvents= */ null,
/* startsActivity = */ false,
@@ -682,7 +682,7 @@
launcher.movePointer(dragStart, targetDest,
DEFAULT_DRAG_STEPS, isDecelerating, downTime, SystemClock.uptimeMillis(),
- true, LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
+ !isDraggingToFolder, LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents, startsActivity);
}