Merge "[Contextual Edu] Not update edu stats when swiping down all apps panel" into main
diff --git a/Android.bp b/Android.bp
index bcbd362..9d7aa73 100644
--- a/Android.bp
+++ b/Android.bp
@@ -17,7 +17,7 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
-min_launcher3_sdk_version = "30"
+min_launcher3_sdk_version = "31"
// Targets that don't inherit framework aconfig libs (i.e., those that don't set
// `platform_apis: true`) must manually link them.
@@ -64,11 +64,74 @@
filegroup {
name: "launcher-quickstep-src",
srcs: [
- "quickstep/src/**/*.java",
"quickstep/src/**/*.kt",
+ "quickstep/src/**/*.java",
+ ":launcher-quickstep-processed-protolog-src",
],
}
+// Launcher ProtoLog support
+filegroup {
+ name: "launcher-quickstep-unprocessed-protolog-src",
+ srcs: [
+ "quickstep/src_protolog/**/*.java",
+ ],
+}
+
+java_library {
+ name: "launcher-quickstep_protolog-groups",
+ srcs: [
+ "quickstep/src_protolog/**/*.java",
+ ],
+ static_libs: [
+ "protolog-group",
+ "androidx.annotation_annotation",
+ "com_android_launcher3_flags_lib",
+ ],
+}
+
+genrule {
+ name: "launcher-quickstep-processed-protolog-src",
+ srcs: [
+ ":protolog-impl",
+ ":launcher-quickstep-unprocessed-protolog-src",
+ ":launcher-quickstep_protolog-groups",
+ ],
+ tools: ["protologtool"],
+ cmd: "$(location protologtool) transform-protolog-calls " +
+ "--protolog-class com.android.internal.protolog.common.ProtoLog " +
+ "--loggroups-class com.android.quickstep.util.QuickstepProtoLogGroup " +
+ "--loggroups-jar $(location :launcher-quickstep_protolog-groups) " +
+ "--viewer-config-file-path /system_ext/etc/launcher.quickstep.protolog.pb " +
+ "--output-srcjar $(out) " +
+ "$(locations :launcher-quickstep-unprocessed-protolog-src)",
+ out: ["launcher.quickstep.protolog.srcjar"],
+}
+
+genrule {
+ name: "gen-launcher.quickstep.protolog.pb",
+ srcs: [
+ ":launcher-quickstep-unprocessed-protolog-src",
+ ":launcher-quickstep_protolog-groups",
+ ],
+ tools: ["protologtool"],
+ cmd: "$(location protologtool) generate-viewer-config " +
+ "--protolog-class com.android.internal.protolog.common.ProtoLog " +
+ "--loggroups-class com.android.quickstep.util.QuickstepProtoLogGroup " +
+ "--loggroups-jar $(location :launcher-quickstep_protolog-groups) " +
+ "--viewer-config-type proto " +
+ "--viewer-config $(out) " +
+ "$(locations :launcher-quickstep-unprocessed-protolog-src)",
+ out: ["launcher.quickstep.protolog.pb"],
+}
+
+prebuilt_etc {
+ name: "launcher.quickstep.protolog.pb",
+ system_ext_specific: true,
+ src: ":gen-launcher.quickstep.protolog.pb",
+ filename_from_src: true,
+}
+
// Source code for quickstep dagger
filegroup {
name: "launcher-quickstep-dagger",
@@ -405,6 +468,7 @@
"SystemUISharedLib",
"SettingsLibSettingsTheme",
"dagger2",
+ "protolog-group",
],
manifest: "quickstep/AndroidManifest.xml",
min_sdk_version: "current",
@@ -503,7 +567,10 @@
"Launcher2",
"Launcher3",
],
- required: ["privapp_whitelist_com.android.launcher3"],
+ required: [
+ "privapp_whitelist_com.android.launcher3",
+ "launcher.quickstep.protolog.pb",
+ ],
resource_dirs: ["quickstep/res"],
diff --git a/aconfig/Android.bp b/aconfig/Android.bp
index bca7494..5413601 100644
--- a/aconfig/Android.bp
+++ b/aconfig/Android.bp
@@ -20,7 +20,7 @@
aconfig_declarations {
name: "com_android_launcher3_flags",
package: "com.android.launcher3",
- container: "system_ext",
+ container: "system",
srcs: ["**/*.aconfig"],
}
diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig
index 3769124..38af572 100644
--- a/aconfig/launcher.aconfig
+++ b/aconfig/launcher.aconfig
@@ -1,5 +1,5 @@
package: "com.android.launcher3"
-container: "system_ext"
+container: "system"
flag {
name: "enable_expanding_pause_work_button"
@@ -398,3 +398,38 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "enable_dismiss_prediction_undo"
+ namespace: "launcher"
+ description: "Show an 'Undo' snackbar when users dismiss a predicted hotseat item"
+ bug: "270394476"
+}
+
+flag {
+ name: "enable_all_apps_button_in_hotseat"
+ namespace: "launcher"
+ description: "Enables displaying the all apps button in the hotseat."
+ bug: "270393897"
+}
+
+flag {
+ name: "taskbar_quiet_mode_change_support"
+ namespace: "launcher"
+ description: "Support changing quiet mode for user profiles in taskbar."
+ bug: "345760034"
+}
+
+flag {
+ name: "taskbar_overflow"
+ namespace: "launcher"
+ description: "Show recent apps in the taskbar overflow."
+ bug: "368119679"
+}
+
+flag {
+ name: "enable_active_gesture_proto_log"
+ namespace: "launcher"
+ description: "Enables tracking active gesture logs in ProtoLog"
+ bug: "293182501"
+}
diff --git a/aconfig/launcher_overview.aconfig b/aconfig/launcher_overview.aconfig
index 23733a4..853faf8 100644
--- a/aconfig/launcher_overview.aconfig
+++ b/aconfig/launcher_overview.aconfig
@@ -1,5 +1,5 @@
package: "com.android.launcher3"
-container: "system_ext"
+container: "system"
flag {
name: "enable_grid_only_overview"
diff --git a/aconfig/launcher_search.aconfig b/aconfig/launcher_search.aconfig
index b98eee6..72f654e 100644
--- a/aconfig/launcher_search.aconfig
+++ b/aconfig/launcher_search.aconfig
@@ -1,5 +1,5 @@
package: "com.android.launcher3"
-container: "system_ext"
+container: "system"
flag {
name: "enable_private_space"
diff --git a/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java b/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java
index 0fb9718..8c2f5d5 100644
--- a/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java
+++ b/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java
@@ -149,8 +149,7 @@
// Disable Overview Actions for Work Profile apps
boolean isManagedProfileTask =
UserManager.get(mApplicationContext).isManagedProfile(task.key.userId);
- boolean isAllowedByPolicy = mTaskContainer.getThumbnailViewDeprecated().isRealSnapshot()
- && !isManagedProfileTask;
+ boolean isAllowedByPolicy = isRealSnapshot() && !isManagedProfileTask;
getActionsView().setCallbacks(new OverlayUICallbacksGoImpl(isAllowedByPolicy, task));
mTaskPackageName = task.key.getPackageName();
mSharedPreferences = LauncherPrefs.getPrefs(mApplicationContext);
diff --git a/proguard.flags b/proguard.flags
index 31edd8d..da00c00 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -57,3 +57,7 @@
-keep class com.android.quickstep.** {
*;
}
+
+-keep class com.android.internal.protolog.** {
+ *;
+}
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index bf198b6..57bfb4a 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -54,6 +54,7 @@
android:protectionLevel="signature|privileged" />
<application android:backupAgent="com.android.launcher3.LauncherBackupAgent"
+ android:enableOnBackInvokedCallback="true"
android:fullBackupOnly="true"
android:fullBackupContent="@xml/backupscheme"
android:hardwareAccelerated="true"
diff --git a/quickstep/dagger/LauncherAppComponent.java b/quickstep/dagger/LauncherAppComponent.java
index dab2582..068f01c 100644
--- a/quickstep/dagger/LauncherAppComponent.java
+++ b/quickstep/dagger/LauncherAppComponent.java
@@ -16,16 +16,18 @@
package com.android.launcher3.dagger;
-import dagger.Component;
-import javax.inject.Singleton;
+import com.android.quickstep.dagger.QuickStepModule;
+import com.android.quickstep.dagger.QuickstepBaseAppComponent;
+
+import dagger.Component;
/**
* Root component for Dagger injection for Launcher Quickstep.
*/
-@Singleton
-@Component
-public interface LauncherAppComponent extends LauncherBaseAppComponent {
+@LauncherAppSingleton
+@Component(modules = QuickStepModule.class)
+public interface LauncherAppComponent extends QuickstepBaseAppComponent {
/** Builder for quickstep LauncherAppComponent. */
@Component.Builder
interface Builder extends LauncherBaseAppComponent.Builder {
diff --git a/quickstep/res/drawable/taskbar_overflow_icon.xml b/quickstep/res/drawable/taskbar_overflow_icon.xml
new file mode 100644
index 0000000..b93a70e
--- /dev/null
+++ b/quickstep/res/drawable/taskbar_overflow_icon.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportHeight="960"
+ android:viewportWidth="960">
+ <path
+ android:fillColor="@color/taskbar_divider_background"
+ android:pathData="M80,605v-250q0,-28.88 20.59,-49.44t49.5,-20.56q28.91,0 49.41,20.56Q220,326.12 220,355v250q0,28.87 -20.59,49.44Q178.82,675 149.91,675t-49.41,-20.56Q80,633.87 80,605ZM340,760q-24,0 -42,-18t-18,-42v-440q0,-24 18,-42t42,-18h280q24,0 42,18t18,42v440q0,24 -18,42t-42,18L340,760ZM740,605v-250q0,-28.88 20.59,-49.44t49.5,-20.56q28.91,0 49.41,20.56Q880,326.12 880,355v250q0,28.87 -20.59,49.44Q838.82,675 809.91,675t-49.41,-20.56Q740,633.87 740,605ZM340,700h280v-440L340,260v440ZM480,480Z"/>
+</vector>
diff --git a/quickstep/res/layout/activity_allset.xml b/quickstep/res/layout/activity_allset.xml
index 685a151..625d9b3 100644
--- a/quickstep/res/layout/activity_allset.xml
+++ b/quickstep/res/layout/activity_allset.xml
@@ -96,7 +96,6 @@
style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="@string/allset_hint"
android:textSize="@dimen/allset_page_swipe_up_text_size"
android:gravity="center_horizontal"
diff --git a/quickstep/res/layout/bubblebar_flyout.xml b/quickstep/res/layout/bubblebar_flyout.xml
new file mode 100644
index 0000000..fc1e914
--- /dev/null
+++ b/quickstep/res/layout/bubblebar_flyout.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<merge
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools">
+
+ <ImageView
+ android:id="@+id/bubble_flyout_avatar"
+ android:layout_width="50dp"
+ android:layout_height="36dp"
+ android:paddingEnd="@dimen/bubblebar_flyout_avatar_message_space"
+ android:scaleType="centerInside"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ tools:src="#ff0000"/>
+
+ <TextView
+ android:id="@+id/bubble_flyout_name"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:fontFamily="@*android:string/config_bodyFontFamilyMedium"
+ android:maxLines="1"
+ android:ellipsize="end"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintStart_toEndOf="@id/bubble_flyout_avatar"
+ tools:text="Sender"/>
+
+ <TextView
+ android:id="@+id/bubble_flyout_text"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:fontFamily="@*android:string/config_bodyFontFamily"
+ android:maxLines="2"
+ android:ellipsize="end"
+ app:layout_constraintTop_toBottomOf="@id/bubble_flyout_name"
+ app:layout_constraintStart_toEndOf="@id/bubble_flyout_avatar"
+ tools:text="This is a message"/>
+
+</merge>
diff --git a/quickstep/res/layout/task_thumbnail.xml b/quickstep/res/layout/task_thumbnail.xml
index d90d916..afbcdb5 100644
--- a/quickstep/res/layout/task_thumbnail.xml
+++ b/quickstep/res/layout/task_thumbnail.xml
@@ -16,6 +16,7 @@
<com.android.quickstep.task.thumbnail.TaskThumbnailView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/snapshot"
android:layout_width="match_parent"
android:layout_height="match_parent" >
diff --git a/quickstep/res/layout/taskbar_overflow_button.xml b/quickstep/res/layout/taskbar_overflow_button.xml
new file mode 100644
index 0000000..20104f2
--- /dev/null
+++ b/quickstep/res/layout/taskbar_overflow_button.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Note: The actual size will match the taskbar icon sizes in TaskbarView#onLayout(). -->
+<com.android.launcher3.views.IconButtonView xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@style/BaseIcon.Workspace.Taskbar"
+ android:layout_width="@dimen/taskbar_icon_min_touch_size"
+ android:layout_height="@dimen/taskbar_icon_min_touch_size"
+ android:backgroundTint="@android:color/transparent"
+ android:contentDescription="@string/taskbar_overflow_a11y_title" />
diff --git a/quickstep/res/values-af/strings.xml b/quickstep/res/values-af/strings.xml
index 4122637..7d65403 100644
--- a/quickstep/res/values-af/strings.xml
+++ b/quickstep/res/values-af/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Wys altyd Taakbalk"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Verander navigasiemodus"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Taakbalkverdeler"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<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="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{meer app}other{meer apps}}"</string>
diff --git a/quickstep/res/values-am/strings.xml b/quickstep/res/values-am/strings.xml
index 956767ee..28bd69a 100644
--- a/quickstep/res/values-am/strings.xml
+++ b/quickstep/res/values-am/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"ሁልጊዜ የተግባር አሞሌ ያሳዩ"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"የአሰሳ ሁነታን ይለውጡ"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"የተግባር አሞሌ አካፋይ"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ወደ ላይ/ግራ ይውሰዱ"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ወደ ታች/ቀኝ ይውሰዱ"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{ተጨማሪ መተግበሪያ}one{ተጨማሪ መተግበሪያ}other{ተጨማሪ መተግበሪያዎች}}"</string>
diff --git a/quickstep/res/values-ar/strings.xml b/quickstep/res/values-ar/strings.xml
index e691663..6ba2e5d 100644
--- a/quickstep/res/values-ar/strings.xml
+++ b/quickstep/res/values-ar/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"إظهار شريط التطبيقات دائمًا"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"تغيير وضع التنقل"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"مقسِّم شريط التطبيقات"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"الانتقال إلى يمين الشاشة أو أعلاها"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"الانتقال إلى يسار الشاشة أو أسفلها"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{تطبيق واحد آخر}zero{تطبيق آخر}two{تطبيقان آخران}few{تطبيقات أخرى}many{تطبيقًا آخر}other{تطبيق آخر}}"</string>
diff --git a/quickstep/res/values-as/strings.xml b/quickstep/res/values-as/strings.xml
index 912003b..486dc09 100644
--- a/quickstep/res/values-as/strings.xml
+++ b/quickstep/res/values-as/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"টাস্কবাৰ সদায় দেখুৱাওক"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"নেভিগেশ্বন ম’ড সলনি কৰক"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"টাস্কবাৰ বিভাজক"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ওপৰৰ বাঁওফাললৈ নিয়ক"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"তলৰ সোঁফাললৈ নিয়ক"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{অধিক এপ্}one{অধিক এপ্}other{অধিক এপ্}}"</string>
diff --git a/quickstep/res/values-az/strings.xml b/quickstep/res/values-az/strings.xml
index b89b707..c483db3 100644
--- a/quickstep/res/values-az/strings.xml
+++ b/quickstep/res/values-az/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"İşləmə paneli həmişə görünsün"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Naviqasiya rejimini dəyişin"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"İşləmə paneli ayırıcısı"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<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="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{əlavə tətbiq}other{əlavə tətbiq}}"</string>
diff --git a/quickstep/res/values-b+sr+Latn/strings.xml b/quickstep/res/values-b+sr+Latn/strings.xml
index 4ef487e..f08cf83 100644
--- a/quickstep/res/values-b+sr+Latn/strings.xml
+++ b/quickstep/res/values-b+sr+Latn/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Uvek prikazuj traku zadataka"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Promeni režim navigacije"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Razdelnik trake zadataka"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<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="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{dodatna aplikacija}one{dodatna aplikacija}few{dodatne aplikacije}other{dodatnih aplikacija}}"</string>
diff --git a/quickstep/res/values-be/strings.xml b/quickstep/res/values-be/strings.xml
index c506acb..fb5b556 100644
--- a/quickstep/res/values-be/strings.xml
+++ b/quickstep/res/values-be/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Заўсёды паказваць панэль задач"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Змяніць рэжым навігацыі"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Раздзяляльнік панэлі задач"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Перамясціць уверх/улева"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Перамясціць уніз/управа"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{даступная праграма}one{даступная праграма}few{даступныя праграмы}many{даступных праграм}other{даступнай праграмы}}"</string>
diff --git a/quickstep/res/values-bg/strings.xml b/quickstep/res/values-bg/strings.xml
index d03e4f7..3c0d840 100644
--- a/quickstep/res/values-bg/strings.xml
+++ b/quickstep/res/values-bg/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Лентата на задачите винаги да се показва"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Промяна на режима на навигация"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Разделител на лентата на задачите"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Преместване горе/вляво"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Преместване долу/вдясно"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{допълнително приложение}other{допълнителни приложения}}"</string>
diff --git a/quickstep/res/values-bn/strings.xml b/quickstep/res/values-bn/strings.xml
index 0c568d9..a224dac 100644
--- a/quickstep/res/values-bn/strings.xml
+++ b/quickstep/res/values-bn/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"সবসময় টাস্কবার দেখুন"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"\'নেভিগেশন\' মোড পরিবর্তন করুন"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"টাস্কবার ডিভাইডার"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"উপরে/বাঁদিকে সরান"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"নিচে/ডানদিকে সরান"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{আরও অ্যাপ}one{আরও অ্যাপ}other{আরও অ্যাপ}}"</string>
diff --git a/quickstep/res/values-bs/strings.xml b/quickstep/res/values-bs/strings.xml
index 922883e..6588dd0 100644
--- a/quickstep/res/values-bs/strings.xml
+++ b/quickstep/res/values-bs/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Uvijek prikaži traku zadataka"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Promijeni način navigacije"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Razdjelnik trake zadataka"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<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="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{dodatna aplikacija}one{dodatna aplikacija}few{dodatne aplikacije}other{dodatnih aplikacija}}"</string>
diff --git a/quickstep/res/values-ca/strings.xml b/quickstep/res/values-ca/strings.xml
index fe7933b..abb2984 100644
--- a/quickstep/res/values-ca/strings.xml
+++ b/quickstep/res/values-ca/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Barra de tasques sempre visible"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Canvia el mode de navegació"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Separador de la Barra de tasques"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<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="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{aplicació més}other{aplicacions més}}"</string>
diff --git a/quickstep/res/values-cs/strings.xml b/quickstep/res/values-cs/strings.xml
index 3047d05..324b02a 100644
--- a/quickstep/res/values-cs/strings.xml
+++ b/quickstep/res/values-cs/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Vždy zobrazovat panel aplikací"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Změnit režim navigace"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Rozdělovač panelu aplikací"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<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="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{další aplikace}few{další aplikace}many{další aplikace}other{dalších aplikací}}"</string>
diff --git a/quickstep/res/values-da/strings.xml b/quickstep/res/values-da/strings.xml
index 6e2130c..ca30dda 100644
--- a/quickstep/res/values-da/strings.xml
+++ b/quickstep/res/values-da/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Vis altid proceslinjen"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Skift navigationstilstand"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Opdeling af proceslinjen"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Flyt til toppen eller venstre side"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Flyt til bunden eller højre side"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{yderligere app}one{yderligere app}other{yderligere apps}}"</string>
diff --git a/quickstep/res/values-de/strings.xml b/quickstep/res/values-de/strings.xml
index 54961fe..f14eddf 100644
--- a/quickstep/res/values-de/strings.xml
+++ b/quickstep/res/values-de/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Taskleiste immer anzeigen"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Navigationsmodus ändern"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Taskleisten-Teiler"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<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="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{weitere App}other{weitere Apps}}"</string>
diff --git a/quickstep/res/values-el/strings.xml b/quickstep/res/values-el/strings.xml
index 6cbb833..e767c74 100644
--- a/quickstep/res/values-el/strings.xml
+++ b/quickstep/res/values-el/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Εμφάνιση Γραμμής εργαλείων"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Αλλαγή τρόπου πλοήγησης"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Διαχωριστικό Γραμμής εργαλείων"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Μετακίνηση επάνω/αριστερά"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Μετακίνηση κάτω/δεξιά"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{ακόμη εφαρμογή}other{ακόμη εφαρμογές}}"</string>
diff --git a/quickstep/res/values-en-rAU/strings.xml b/quickstep/res/values-en-rAU/strings.xml
index dcbaa7a..a74a17b 100644
--- a/quickstep/res/values-en-rAU/strings.xml
+++ b/quickstep/res/values-en-rAU/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Always show Taskbar"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Change navigation mode"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Taskbar divider"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<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="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{more app}other{more apps}}"</string>
diff --git a/quickstep/res/values-en-rCA/strings.xml b/quickstep/res/values-en-rCA/strings.xml
index c00e6cd..e0787ca 100644
--- a/quickstep/res/values-en-rCA/strings.xml
+++ b/quickstep/res/values-en-rCA/strings.xml
@@ -139,6 +139,7 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Always show Taskbar"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Change navigation mode"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Taskbar Divider"</string>
+ <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="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{more app}other{more apps}}"</string>
diff --git a/quickstep/res/values-en-rGB/strings.xml b/quickstep/res/values-en-rGB/strings.xml
index dcbaa7a..a74a17b 100644
--- a/quickstep/res/values-en-rGB/strings.xml
+++ b/quickstep/res/values-en-rGB/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Always show Taskbar"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Change navigation mode"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Taskbar divider"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<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="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{more app}other{more apps}}"</string>
diff --git a/quickstep/res/values-en-rIN/strings.xml b/quickstep/res/values-en-rIN/strings.xml
index dcbaa7a..a74a17b 100644
--- a/quickstep/res/values-en-rIN/strings.xml
+++ b/quickstep/res/values-en-rIN/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Always show Taskbar"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Change navigation mode"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Taskbar divider"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<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="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{more app}other{more apps}}"</string>
diff --git a/quickstep/res/values-en-rXC/strings.xml b/quickstep/res/values-en-rXC/strings.xml
index 2abef91..8fcbe2b 100644
--- a/quickstep/res/values-en-rXC/strings.xml
+++ b/quickstep/res/values-en-rXC/strings.xml
@@ -139,6 +139,7 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Always show Taskbar"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Change navigation mode"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Taskbar Divider"</string>
+ <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="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{more app}other{more apps}}"</string>
diff --git a/quickstep/res/values-es-rUS/strings.xml b/quickstep/res/values-es-rUS/strings.xml
index f09fae6..e6efd43 100644
--- a/quickstep/res/values-es-rUS/strings.xml
+++ b/quickstep/res/values-es-rUS/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Barra de tareas visible"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Cambiar el modo de navegación"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Divisor de la Barra de tareas"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<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="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{app más}other{apps más}}"</string>
diff --git a/quickstep/res/values-es/strings.xml b/quickstep/res/values-es/strings.xml
index 356a38b..527bdc3 100644
--- a/quickstep/res/values-es/strings.xml
+++ b/quickstep/res/values-es/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Barra de tareas visible"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Cambiar el modo de navegación"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Divisor de Barra de Tareas"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mover arriba/a la izquierda"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mover abajo/a la derecha"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{aplicación más}other{aplicaciones más}}"</string>
diff --git a/quickstep/res/values-et/strings.xml b/quickstep/res/values-et/strings.xml
index fccbeda..0e222bc 100644
--- a/quickstep/res/values-et/strings.xml
+++ b/quickstep/res/values-et/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Kuva tegumiriba alati"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Navigeerimisrežiimi muutmine"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Tegumiriba jagaja"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<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="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{rakendus veel}other{rakendust veel}}"</string>
diff --git a/quickstep/res/values-eu/strings.xml b/quickstep/res/values-eu/strings.xml
index a1d8cc3..7764cb8 100644
--- a/quickstep/res/values-eu/strings.xml
+++ b/quickstep/res/values-eu/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Erakutsi beti zereginen barra"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Aldatu nabigazio modua"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Zereginen barraren zatitzailea"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<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="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{aplikazio gehiago}other{aplikazio gehiago}}"</string>
diff --git a/quickstep/res/values-fa/strings.xml b/quickstep/res/values-fa/strings.xml
index 8f37686..05cf83e 100644
--- a/quickstep/res/values-fa/strings.xml
+++ b/quickstep/res/values-fa/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"نوار وظیفه همیشه نشان داده شود"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"تغییر حالت پیمایش"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"جداکننده نوار وظیفه"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"انتقال به بالا/ چپ"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"انتقال به پایین/ راست"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{برنامه دیگر}one{برنامه دیگر}other{برنامه دیگر}}"</string>
diff --git a/quickstep/res/values-fi/strings.xml b/quickstep/res/values-fi/strings.xml
index 175a896..73b1ba4 100644
--- a/quickstep/res/values-fi/strings.xml
+++ b/quickstep/res/values-fi/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Näytä tehtäväpalkki aina"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Vaihda navigointitilaa"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Tehtäväpalkin jakaja"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<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="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{muu sovellus}other{muuta sovellusta}}"</string>
diff --git a/quickstep/res/values-fr-rCA/strings.xml b/quickstep/res/values-fr-rCA/strings.xml
index 6d6c67c..a554881 100644
--- a/quickstep/res/values-fr-rCA/strings.xml
+++ b/quickstep/res/values-fr-rCA/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Tjrs afficher barre des tâches"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Changer de mode de navigation"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Séparateur de la barre des tâches"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Déplacer vers le coin supérieur gauche de l\'écran"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Déplacer vers le coin inférieur droit de l\'écran"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{autre appli}one{autre appli}other{autres applis}}"</string>
diff --git a/quickstep/res/values-fr/strings.xml b/quickstep/res/values-fr/strings.xml
index a395266..3711dcd 100644
--- a/quickstep/res/values-fr/strings.xml
+++ b/quickstep/res/values-fr/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Barre des tâches tjs visible"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Modifier le mode de navigation"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Séparateur de barre des tâches"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Déplacer en haut ou à gauche"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Déplacer en bas ou à droite"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{autre application}one{autre application}other{autres applications}}"</string>
diff --git a/quickstep/res/values-gl/strings.xml b/quickstep/res/values-gl/strings.xml
index 5b04960..a2c4b73 100644
--- a/quickstep/res/values-gl/strings.xml
+++ b/quickstep/res/values-gl/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Ver sempre a barra de tarefas"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Cambiar modo de navegación"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Divisor da Barra de tarefas"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<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="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{aplicación máis}other{aplicacións máis}}"</string>
diff --git a/quickstep/res/values-gu/strings.xml b/quickstep/res/values-gu/strings.xml
index fc253bd..dea52c3 100644
--- a/quickstep/res/values-gu/strings.xml
+++ b/quickstep/res/values-gu/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"હંમેશાં ટાસ્કબાર બતાવો"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"નૅવિગેશન મોડ બદલો"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"ટાસ્કબાર વિભાજક"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"સૌથી ઉપર ડાબી બાજુએ ખસેડો"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"સૌથી નીચે જમણી બાજુએ ખસેડો"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{વધુ ઍપ}one{વધુ ઍપ}other{વધુ ઍપ}}"</string>
diff --git a/quickstep/res/values-hi/strings.xml b/quickstep/res/values-hi/strings.xml
index 30a17db..e81e942 100644
--- a/quickstep/res/values-hi/strings.xml
+++ b/quickstep/res/values-hi/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"टास्कबार हमेशा दिखाएं"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"नेविगेशन का मोड बदलें"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"टास्कबार डिवाइडर"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ऊपर/बाईं तरफ़ ले जाएं"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"नीचे/दाईं तरफ़ ले जाएं"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{ज़्यादा ऐप्लिकेशन}one{ज़्यादा ऐप्लिकेशन}other{ज़्यादा ऐप्लिकेशन}}"</string>
diff --git a/quickstep/res/values-hr/strings.xml b/quickstep/res/values-hr/strings.xml
index 06511e9..a2bf691 100644
--- a/quickstep/res/values-hr/strings.xml
+++ b/quickstep/res/values-hr/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Uvijek prikaži traku zadataka"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Promijeni način navigacije"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Razdjelnik trake sa zadacima"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<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="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{dodatna aplikacija}one{dodatna aplikacija}few{dodatne aplikacije}other{dodatnih aplikacija}}"</string>
diff --git a/quickstep/res/values-hu/strings.xml b/quickstep/res/values-hu/strings.xml
index 9bd9478..4dc9974 100644
--- a/quickstep/res/values-hu/strings.xml
+++ b/quickstep/res/values-hu/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Mindig megjelenő Feladatsáv"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Navigációs mód módosítása"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Feladatsáv-elválasztó"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<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="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{további alkalmazás}other{további alkalmazás}}"</string>
diff --git a/quickstep/res/values-hy/strings.xml b/quickstep/res/values-hy/strings.xml
index e1481ec..f4fcacf 100644
--- a/quickstep/res/values-hy/strings.xml
+++ b/quickstep/res/values-hy/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Միշտ ցույց տալ վահանակը"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Փոխել նավիգացիայի ռեժիմը"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Հավելվածների վահանակի բաժանիչ"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Տեղափոխել վերևի ձախ անկյուն"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Տեղափոխել ներքևի աջ անկյուն"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{լրացուցիչ հավելված}one{լրացուցիչ հավելված}other{լրացուցիչ հավելված}}"</string>
diff --git a/quickstep/res/values-in/strings.xml b/quickstep/res/values-in/strings.xml
index f8345b5..519dced 100644
--- a/quickstep/res/values-in/strings.xml
+++ b/quickstep/res/values-in/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Selalu tampilkan Taskbar"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Ubah mode navigasi"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Pemisah Taskbar"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<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="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{aplikasi lainnya}other{aplikasi lainnya}}"</string>
diff --git a/quickstep/res/values-is/strings.xml b/quickstep/res/values-is/strings.xml
index 017d108..274e1da 100644
--- a/quickstep/res/values-is/strings.xml
+++ b/quickstep/res/values-is/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Alltaf sýna forritastiku"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Breyta leiðsagnarstillingu"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Skipting forritastiku"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<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="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>
diff --git a/quickstep/res/values-it/strings.xml b/quickstep/res/values-it/strings.xml
index 1983ee3..445474e 100644
--- a/quickstep/res/values-it/strings.xml
+++ b/quickstep/res/values-it/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Mostra sempre barra app"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Cambia modalità di navigazione"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Divisore barra delle app"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<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="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{altra app}other{altre app}}"</string>
diff --git a/quickstep/res/values-iw/strings.xml b/quickstep/res/values-iw/strings.xml
index a7115e3..5bb51ce 100644
--- a/quickstep/res/values-iw/strings.xml
+++ b/quickstep/res/values-iw/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"סרגל האפליקציות מוצג תמיד"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"שינוי מצב הניווט"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"המחיצה בסרגל האפליקציות"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"העברה לפינה השמאלית/העליונה"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"העברה לפינה הימנית/התחתונה"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{אפליקציה נוספת}one{אפליקציות נוספות}two{אפליקציות נוספות}other{אפליקציות נוספות}}"</string>
diff --git a/quickstep/res/values-ja/strings.xml b/quickstep/res/values-ja/strings.xml
index fa3c01d..0cff4c1 100644
--- a/quickstep/res/values-ja/strings.xml
+++ b/quickstep/res/values-ja/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"常にタスクバーを表示する"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"ナビゲーション モードを変更"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"タスクバーの区切り"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"上 / 左に移動"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"下 / 右に移動"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{個のその他のアプリ}other{個のその他のアプリ}}"</string>
diff --git a/quickstep/res/values-ka/strings.xml b/quickstep/res/values-ka/strings.xml
index a032731..efff980 100644
--- a/quickstep/res/values-ka/strings.xml
+++ b/quickstep/res/values-ka/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"ამოცანათა ზოლის მუდამ ჩვენება"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"შეცვალეთ ნავიგაციის რეჟიმი"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"ამოცანათა ზოლის გამყოფი"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ზემოთ/მარცხნივ გადატანა"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ქვემოთ/მარჯვნივ გადატანა"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{სხვა აპი}other{სხვა აპი}}"</string>
diff --git a/quickstep/res/values-kk/strings.xml b/quickstep/res/values-kk/strings.xml
index e1a9e8e..193b606 100644
--- a/quickstep/res/values-kk/strings.xml
+++ b/quickstep/res/values-kk/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Тапсырма жолағын үнемі көрсету"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Навигация режимін өзгерту"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Тапсырмалар жолағын бөлгіш"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Жоғары/солға жылжыту"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Төмен/оңға жылжыту"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{қосымша қолданба}other{қосымша қолданба}}"</string>
diff --git a/quickstep/res/values-km/strings.xml b/quickstep/res/values-km/strings.xml
index 2eb3114..aadfd67 100644
--- a/quickstep/res/values-km/strings.xml
+++ b/quickstep/res/values-km/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"បង្ហាញរបារកិច្ចការជានិច្ច"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"ប្ដូរមុខងាររុករក"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"បន្ទាត់ខណ្ឌចែករបារកិច្ចការ"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ផ្លាស់ទីទៅខាងលើ/ឆ្វេង"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ផ្លាស់ទីទៅខាងក្រោម/ស្ដាំ"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{កម្មវិធីច្រើនទៀត}other{កម្មវិធីច្រើនទៀត}}"</string>
diff --git a/quickstep/res/values-kn/strings.xml b/quickstep/res/values-kn/strings.xml
index bb60620..9a04ffd 100644
--- a/quickstep/res/values-kn/strings.xml
+++ b/quickstep/res/values-kn/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"ಯಾವಾಗಲೂ ಟಾಸ್ಕ್ಬಾರ್ ತೋರಿಸಿ"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"ನ್ಯಾವಿಗೇಶನ್ ಮೋಡ್ ಬದಲಾಯಿಸಿ"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"ಟಾಸ್ಕ್ಬಾರ್ ಡಿವೈಡರ್"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ಮೇಲಿನ/ಎಡಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ಕೆಳಗಿನ/ಬಲಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{ಹೆಚ್ಚಿನ ಆ್ಯಪ್}one{ಹೆಚ್ಚಿನ ಆ್ಯಪ್ಗಳು}other{ಹೆಚ್ಚಿನ ಆ್ಯಪ್ಗಳು}}"</string>
diff --git a/quickstep/res/values-ko/strings.xml b/quickstep/res/values-ko/strings.xml
index e6a80c3..38d5bbb 100644
--- a/quickstep/res/values-ko/strings.xml
+++ b/quickstep/res/values-ko/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"태스크 바 항상 표시"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"탐색 모드 변경"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"태스크 바 분할"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"상단/왼쪽으로 이동"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"하단/오른쪽으로 이동"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{추가 앱}other{추가 앱}}"</string>
diff --git a/quickstep/res/values-ky/strings.xml b/quickstep/res/values-ky/strings.xml
index 04e7f6e..8eba2a5 100644
--- a/quickstep/res/values-ky/strings.xml
+++ b/quickstep/res/values-ky/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Такта ар дайым көрүнсүн"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Өтүү режимин өзгөртүү"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Тапшырмалар панелин бөлгүч"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Жогорку/сол бурчка жылдыруу"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Төмөнкү/оң бурчка жылдыруу"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{колдонмо бар}other{колдонмо бар}}"</string>
diff --git a/quickstep/res/values-land/dimens.xml b/quickstep/res/values-land/dimens.xml
index 2239f8b..efdc7de 100644
--- a/quickstep/res/values-land/dimens.xml
+++ b/quickstep/res/values-land/dimens.xml
@@ -81,7 +81,7 @@
<dimen name="taskbar_contextual_button_suw_margin">48dp</dimen>
<dimen name="taskbar_contextual_button_suw_height">48dp</dimen>
<dimen name="taskbar_suw_frame">96dp</dimen>
- <dimen name="taskbar_suw_insets">24dp</dimen>
+ <dimen name="taskbar_suw_insets">48dp</dimen>
<!-- Keyboard Quick Switch -->
<dimen name="keyboard_quick_switch_taskview_width">217.6dp</dimen>
diff --git a/quickstep/res/values-lo/strings.xml b/quickstep/res/values-lo/strings.xml
index b3ca116..284cdc7 100644
--- a/quickstep/res/values-lo/strings.xml
+++ b/quickstep/res/values-lo/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"ສະແດງແຖບໜ້າວຽກສະເໝີ"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"ປ່ຽນໂໝດການນຳທາງ"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"ເສັ້ນແບ່ງແຖບໜ້າວຽກ"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ຍ້າຍໄປຊ້າຍ/ເທິງ"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ຍ້າຍໄປຂວາ/ລຸ່ມ"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{ແອັບເພີ່ມເຕີມ}other{ແອັບເພີ່ມເຕີມ}}"</string>
diff --git a/quickstep/res/values-lt/strings.xml b/quickstep/res/values-lt/strings.xml
index 4f3f36e..1c20015 100644
--- a/quickstep/res/values-lt/strings.xml
+++ b/quickstep/res/values-lt/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Visada rodyti užduočių juostą"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Keisti naršymo režimą"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Užduočių juostos daliklis"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<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="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{papildoma programa}one{papildoma programa}few{papildomos programos}many{papildomos programos}other{papildomų programų}}"</string>
diff --git a/quickstep/res/values-lv/strings.xml b/quickstep/res/values-lv/strings.xml
index fd75fc4..408f3c3 100644
--- a/quickstep/res/values-lv/strings.xml
+++ b/quickstep/res/values-lv/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Vienmēr rādīt uzdevumu joslu"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Mainīt navigācijas režīmu"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Uzdevumu joslas atdalītājs"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<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="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{papildu lietotne}zero{papildu lietotņu}one{papildu lietotne}other{papildu lietotnes}}"</string>
diff --git a/quickstep/res/values-mk/strings.xml b/quickstep/res/values-mk/strings.xml
index b50277b..588578e 100644
--- a/quickstep/res/values-mk/strings.xml
+++ b/quickstep/res/values-mk/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Секогаш прикажувај „Лента“"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Променете режим на навигација"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Разделник на „Лента со задачи“"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Премести горе лево"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Премести долу десно"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{дополнителна апликација}one{дополнителна апликација}other{дополнителни апликации}}"</string>
diff --git a/quickstep/res/values-ml/strings.xml b/quickstep/res/values-ml/strings.xml
index ef72da1..bb5c014 100644
--- a/quickstep/res/values-ml/strings.xml
+++ b/quickstep/res/values-ml/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"ടാസ്ക്ബാർ എപ്പോഴും കാണിക്കൂ"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"നാവിഗേഷൻ മോഡ് മാറ്റുക"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"ടാസ്ക്ബാർ ഡിവൈഡർ"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"മുകളിലേക്കോ ഇടത്തേക്കോ നീക്കുക"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"താഴേക്കോ വലത്തേക്കോ നീക്കുക"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{കൂടുതൽ ആപ്പ്}other{കൂടുതൽ ആപ്പുകൾ}}"</string>
diff --git a/quickstep/res/values-mn/strings.xml b/quickstep/res/values-mn/strings.xml
index 2ca2e26..9223139 100644
--- a/quickstep/res/values-mn/strings.xml
+++ b/quickstep/res/values-mn/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Ажлын хэсгийг үргэлж харуулах"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Навигацын горимыг өөрчлөх"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Ажлын хэсгийг хуваагч"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Зүүн дээд хэсэг рүү зөөх"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Баруун доод хэсэг рүү зөөх"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{бусад апп}other{бусад апп}}"</string>
diff --git a/quickstep/res/values-mr/strings.xml b/quickstep/res/values-mr/strings.xml
index 159368d..3eef060 100644
--- a/quickstep/res/values-mr/strings.xml
+++ b/quickstep/res/values-mr/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"नेहमी टास्कबार दाखवा"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"नेव्हिगेशन मोड बदला"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"टास्कबार विभाजक"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"सर्वात वरती/डावीकडे हलवा"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"तळाशी/उजवीकडे हलवा"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{आणखी अॅप}other{आणखी अॅप्स}}"</string>
diff --git a/quickstep/res/values-ms/strings.xml b/quickstep/res/values-ms/strings.xml
index 9a27e51..9bdc973 100644
--- a/quickstep/res/values-ms/strings.xml
+++ b/quickstep/res/values-ms/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Papar Bar Tugas selalu"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Tukar mod navigasi"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Pembahagi Bar Tugas"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<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="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{apl lagi}other{apl lagi}}"</string>
diff --git a/quickstep/res/values-my/strings.xml b/quickstep/res/values-my/strings.xml
index 7e298fb..6ea946a 100644
--- a/quickstep/res/values-my/strings.xml
+++ b/quickstep/res/values-my/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Taskbar အမြဲပြရန်"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"ရွှေ့ကြည့်သည့်မုဒ် ပြောင်းရန်"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"လုပ်ဆောင်စရာဘား ပိုင်းခြားစနစ်"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"အပေါ်/ဘယ်ဘက်သို့ ရွှေ့ရန်"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"အောက်ခြေ/ညာဘက်သို့ ရွှေ့ရန်"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{နောက်ထပ်အက်ပ်}other{နောက်ထပ်အက်ပ်များ}}"</string>
diff --git a/quickstep/res/values-nb/strings.xml b/quickstep/res/values-nb/strings.xml
index 4b982ef..33cde5a 100644
--- a/quickstep/res/values-nb/strings.xml
+++ b/quickstep/res/values-nb/strings.xml
@@ -97,7 +97,7 @@
<string name="action_share" msgid="2648470652637092375">"Del"</string>
<string name="action_screenshot" msgid="8171125848358142917">"Skjermbilde"</string>
<string name="action_split" msgid="2098009717623550676">"Del opp"</string>
- <string name="action_save_app_pair" msgid="5974823919237645229">"Lagre apptilkobling"</string>
+ <string name="action_save_app_pair" msgid="5974823919237645229">"Lagre app-paret"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Trykk på en annen app for å bruke delt skjerm"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Velg en annen app for å bruke delt skjerm"</string>
<string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Avbryt"</string>
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Vis alltid oppgavelinjen"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Endre navigasjonsmodus"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Skille for oppgavelinjen"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Flytt til øverst/venstre"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Flytt til nederst/høyre"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{app til}other{apper til}}"</string>
diff --git a/quickstep/res/values-ne/strings.xml b/quickstep/res/values-ne/strings.xml
index 326222b..4c9747f 100644
--- a/quickstep/res/values-ne/strings.xml
+++ b/quickstep/res/values-ne/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"टास्कबार सधैँ देखाउनुहोस्"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"नेभिगेसन मोड बदल्नुहोस्"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"टास्कबार डिभाइडर"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"सिरान/बायाँतिर सार्नुहोस्"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"फेद/दायाँतिर सार्नुहोस्"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{थप एप}other{थप एपहरू}}"</string>
diff --git a/quickstep/res/values-night/colors.xml b/quickstep/res/values-night/colors.xml
index 2052446..98e4871 100644
--- a/quickstep/res/values-night/colors.xml
+++ b/quickstep/res/values-night/colors.xml
@@ -25,7 +25,7 @@
<color name="all_set_page_background">@android:color/system_neutral1_900</color>
<!-- Turn on work apps button -->
- <color name="work_turn_on_stroke">?androidprv:attr/colorAccentSecondaryVariant</color>
+ <color name="work_turn_on_stroke">?attr/materialColorPrimary</color>
<color name="work_fab_bg_color">?attr/materialColorPrimaryFixedDim</color>
<color name="work_fab_icon_color">?attr/materialColorOnPrimaryFixed</color>
</resources>
\ No newline at end of file
diff --git a/quickstep/res/values-nl/strings.xml b/quickstep/res/values-nl/strings.xml
index 8a923b5..a4949ac 100644
--- a/quickstep/res/values-nl/strings.xml
+++ b/quickstep/res/values-nl/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Taakbalk altijd tonen"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Navigatiemodus wijzigen"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Scheiding voor Taakbalk"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<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="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{extra app}other{extra apps}}"</string>
diff --git a/quickstep/res/values-or/strings.xml b/quickstep/res/values-or/strings.xml
index 3150ded..7753153 100644
--- a/quickstep/res/values-or/strings.xml
+++ b/quickstep/res/values-or/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"ସର୍ବଦା ଟାସ୍କବାର ଦେଖାନ୍ତୁ"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"ନାଭିଗେସନ ମୋଡ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"ଟାସ୍କବାର ଡିଭାଇଡର"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ଶୀର୍ଷ/ବାମକୁ ମୁଭ କରନ୍ତୁ"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ନିମ୍ନ/ଡାହାଣକୁ ମୁଭ କରନ୍ତୁ"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{ଅଧିକ ଆପ}other{ଅଧିକ ଆପ୍ସ}}"</string>
diff --git a/quickstep/res/values-pa/strings.xml b/quickstep/res/values-pa/strings.xml
index 7da7555..011fa6e 100644
--- a/quickstep/res/values-pa/strings.xml
+++ b/quickstep/res/values-pa/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"ਹਮੇਸ਼ਾਂ ਟਾਸਕਬਾਰ ਦਿਖਾਓ"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"ਨੈਵੀਗੇਸ਼ਨ ਮੋਡ ਬਦਲੋ"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"ਟਾਸਕਬਾਰ ਵਿਭਾਜਕ"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ਸਿਖਰਲੇ/ਖੱਬੇ ਪਾਸੇ ਲੈ ਕੇ ਜਾਓ"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ਹੇਠਾਂ/ਸੱਜੇ ਪਾਸੇ ਲੈ ਕੇ ਜਾਓ"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{ਹੋਰ ਐਪ}one{ਹੋਰ ਐਪ}other{ਹੋਰ ਐਪਾਂ}}"</string>
diff --git a/quickstep/res/values-pl/strings.xml b/quickstep/res/values-pl/strings.xml
index 0cc49e2..df0f74c 100644
--- a/quickstep/res/values-pl/strings.xml
+++ b/quickstep/res/values-pl/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Zawsze pokazuj pasek aplikacji"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Zmień tryb nawigacji"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Linia dzielenia paska aplikacji"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<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="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{inna aplikacja}few{inne aplikacje}many{innych aplikacji}other{innej aplikacji}}"</string>
diff --git a/quickstep/res/values-pt-rPT/strings.xml b/quickstep/res/values-pt-rPT/strings.xml
index 33b87df..5ad32af 100644
--- a/quickstep/res/values-pt-rPT/strings.xml
+++ b/quickstep/res/values-pt-rPT/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Ver sempre Barra de tarefas"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Alterar modo de navegação"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Divisor da Barra de tarefas"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<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="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{outra app}other{outras apps}}"</string>
diff --git a/quickstep/res/values-pt/strings.xml b/quickstep/res/values-pt/strings.xml
index 0aa6295..28b3414 100644
--- a/quickstep/res/values-pt/strings.xml
+++ b/quickstep/res/values-pt/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Sempre mostrar a Barra de tarefas"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Mudar o modo de navegação"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Separador da Barra de tarefas"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<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="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{outro app}one{outro app}other{outros apps}}"</string>
diff --git a/quickstep/res/values-ro/strings.xml b/quickstep/res/values-ro/strings.xml
index 2f9d287..6bd3e0f 100644
--- a/quickstep/res/values-ro/strings.xml
+++ b/quickstep/res/values-ro/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Afișează mereu bara"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Schimbă modul de navigare"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Separator pentru bara de activități"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<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="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{aplicație suplimentară}few{mai multe aplicații}other{mai multe aplicații}}"</string>
diff --git a/quickstep/res/values-ru/strings.xml b/quickstep/res/values-ru/strings.xml
index 2722ca9..20f579d 100644
--- a/quickstep/res/values-ru/strings.xml
+++ b/quickstep/res/values-ru/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Всегда показывать панель задач"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Изменить режим навигации"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Разделитель панели задач"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Переместить вверх или влево"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Переместить вниз или вправо"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{дополнительное приложение}one{дополнительное приложение}few{дополнительных приложения}many{дополнительных приложений}other{дополнительного приложения}}"</string>
diff --git a/quickstep/res/values-si/strings.xml b/quickstep/res/values-si/strings.xml
index 19b61f7..dd9739c 100644
--- a/quickstep/res/values-si/strings.xml
+++ b/quickstep/res/values-si/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"සෑම විටම කාර්ය තීරුව පෙන්වන්න"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"සංචාලන ප්රකාරය වෙනස් කරන්න"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"කාර්ය තීරු බෙදනය"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ඉහළ/වම වෙත ගෙන යන්න"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"පහළ/දකුණ වෙත ගෙන යන්න"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{තව යෙදුම}one{තවත් යෙදුම්}other{තවත් යෙදුම්}}"</string>
diff --git a/quickstep/res/values-sk/strings.xml b/quickstep/res/values-sk/strings.xml
index 0f0ec27..6241ad2 100644
--- a/quickstep/res/values-sk/strings.xml
+++ b/quickstep/res/values-sk/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Zobrazovať panel aplikácií"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Zmeniť režim navigácie"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Rozdeľovač panela aplikácií"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<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="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>
diff --git a/quickstep/res/values-sl/strings.xml b/quickstep/res/values-sl/strings.xml
index 972ced5..9394a5a 100644
--- a/quickstep/res/values-sl/strings.xml
+++ b/quickstep/res/values-sl/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Stalen prikaz oprav. vrstice"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Spreminjanje načina navigacije"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Razdelilnik opravilne vrstice"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<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="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{dodatna aplikacija}one{dodatna aplikacija}two{dodatni aplikaciji}few{dodatne aplikacije}other{dodatnih aplikacij}}"</string>
diff --git a/quickstep/res/values-sq/strings.xml b/quickstep/res/values-sq/strings.xml
index d2a7281..12635c6 100644
--- a/quickstep/res/values-sq/strings.xml
+++ b/quickstep/res/values-sq/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Shfaq gjithmonë shiritin e detyrave"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Ndrysho modalitetin e navigimit"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Ndarësi i shiritit të detyrave"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<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="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{aplikacion tjetër}other{aplikacione të tjera}}"</string>
diff --git a/quickstep/res/values-sr/strings.xml b/quickstep/res/values-sr/strings.xml
index 81204af..a7760b9 100644
--- a/quickstep/res/values-sr/strings.xml
+++ b/quickstep/res/values-sr/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Увек приказуј траку задатака"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Промени режим навигације"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Разделник траке задатака"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Премести горе лево"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Премести доле десно"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{додатна апликација}one{додатна апликација}few{додатне апликације}other{додатних апликација}}"</string>
diff --git a/quickstep/res/values-sv/strings.xml b/quickstep/res/values-sv/strings.xml
index 5d0c7a3..d4c3a69 100644
--- a/quickstep/res/values-sv/strings.xml
+++ b/quickstep/res/values-sv/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Visa alltid aktivitetsfältet"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Ändra navigeringsläge"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Avdelare för aktivitetsfältet"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Flytta högst upp/till vänster"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Flytta längst ned/till höger"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{app till}other{appar till}}"</string>
diff --git a/quickstep/res/values-sw/strings.xml b/quickstep/res/values-sw/strings.xml
index e133ba3..5b74600 100644
--- a/quickstep/res/values-sw/strings.xml
+++ b/quickstep/res/values-sw/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Onyesha Zana kila wakati"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Badilisha hali ya usogezaji"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Kitenganishi cha Upauzana"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<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="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{programu nyingine}other{programu zingine}}"</string>
diff --git a/quickstep/res/values-sw600dp-land/dimens.xml b/quickstep/res/values-sw600dp-land/dimens.xml
index 5e9a177..49239aa 100644
--- a/quickstep/res/values-sw600dp-land/dimens.xml
+++ b/quickstep/res/values-sw600dp-land/dimens.xml
@@ -33,4 +33,6 @@
<!-- The bottom margin above the bottom row of tasks in grid only overview -->
<dimen name="overview_bottom_margin_grid_only">40dp</dimen>
+ <dimen name="taskbar_suw_insets">24dp</dimen>
+
</resources>
diff --git a/quickstep/res/values-ta/strings.xml b/quickstep/res/values-ta/strings.xml
index 95d0fa9..39dc6bd 100644
--- a/quickstep/res/values-ta/strings.xml
+++ b/quickstep/res/values-ta/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"செயல் பட்டியை எப்போதும் காட்டு"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"வழிசெலுத்தல் பயன்முறையை மாற்று"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"செயல் பட்டிப் பிரிப்பான்"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"மேலே/இடதுபுறம் நகர்த்தும்"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"கீழே/வலதுபுறம் நகர்த்தும்"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{கூடுதல் ஆப்ஸ்}other{கூடுதல் ஆப்ஸ்}}"</string>
diff --git a/quickstep/res/values-te/strings.xml b/quickstep/res/values-te/strings.xml
index 116d388..86161b4 100644
--- a/quickstep/res/values-te/strings.xml
+++ b/quickstep/res/values-te/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"టాస్క్బార్ను నిరంతరం చూపండి"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"నావిగేషన్ మోడ్ను మార్చండి"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"టాస్క్బార్ డివైడర్"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ఎగువ/ఎడమ వైపునకు తరలించండి"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"దిగువ/కుడి వైపునకు తరలించండి"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{మరో యాప్}other{మరిన్ని యాప్లు}}"</string>
diff --git a/quickstep/res/values-th/strings.xml b/quickstep/res/values-th/strings.xml
index ecdb3b5..c6fddfb 100644
--- a/quickstep/res/values-th/strings.xml
+++ b/quickstep/res/values-th/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"แสดงแถบงานเสมอ"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"เปลี่ยนโหมดการนําทาง"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"ตัวแบ่งแถบงาน"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ย้ายไปที่ด้านบนหรือด้านซ้าย"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ย้ายไปที่ด้านล่างหรือด้านขวา"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{แอปเพิ่มเติม}other{แอปเพิ่มเติม}}"</string>
diff --git a/quickstep/res/values-tl/strings.xml b/quickstep/res/values-tl/strings.xml
index da646a4..91f9675 100644
--- a/quickstep/res/values-tl/strings.xml
+++ b/quickstep/res/values-tl/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Ipakita lagi ang Taskbar"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Magpalit ng navigation mode"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Divider ng Taskbar"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<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="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{pang app}one{pang app}other{pang app}}"</string>
diff --git a/quickstep/res/values-tr/strings.xml b/quickstep/res/values-tr/strings.xml
index 7be5464..f5f98bb 100644
--- a/quickstep/res/values-tr/strings.xml
+++ b/quickstep/res/values-tr/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Görev çubuğunu daima göster"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Gezinme modunu değiştir"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Görev Çubuğu Ayırıcısı"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<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="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{uygulama daha}other{uygulama daha}}"</string>
diff --git a/quickstep/res/values-uk/strings.xml b/quickstep/res/values-uk/strings.xml
index 216ae0b..93d974f 100644
--- a/quickstep/res/values-uk/strings.xml
+++ b/quickstep/res/values-uk/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Завжди показув. панель завдань"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Змінити режим навігації"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Розділювач панелі завдань"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Перемістити вгору або вліво"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Перемістити вниз або вправо"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{інший додаток}one{інший додаток}few{інші додатки}many{інших додатків}other{іншого додатка}}"</string>
diff --git a/quickstep/res/values-ur/strings.xml b/quickstep/res/values-ur/strings.xml
index 2307cb1..76a2c81 100644
--- a/quickstep/res/values-ur/strings.xml
+++ b/quickstep/res/values-ur/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"ہمیشہ ٹاسک بار دکھائیں"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"نیویگیشن موڈ تبدیل کریں"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"ٹاسک بار ڈیوائیڈر"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"اوپر/بائیں طرف منتقل کریں"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"نیچے/دائیں طرف منتقل کریں"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{مزید ایپ}other{مزید ایپس}}"</string>
diff --git a/quickstep/res/values-uz/strings.xml b/quickstep/res/values-uz/strings.xml
index 9f98b55..0d6c09d 100644
--- a/quickstep/res/values-uz/strings.xml
+++ b/quickstep/res/values-uz/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Vazifalar paneli doim chiqarilsin"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Navigatsiya rejimini oʻzgartirish"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Vazifalar panelini ajratkich"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<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="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{boshqa ilova}other{boshqa ilovalar}}"</string>
diff --git a/quickstep/res/values-vi/strings.xml b/quickstep/res/values-vi/strings.xml
index 50acbbc..cf38392 100644
--- a/quickstep/res/values-vi/strings.xml
+++ b/quickstep/res/values-vi/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Luôn hiện Thanh tác vụ"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Thay đổi chế độ điều hướng"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Đường phân chia Taskbar"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<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="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{ứng dụng khác}other{ứng dụng khác}}"</string>
diff --git a/quickstep/res/values-zh-rCN/strings.xml b/quickstep/res/values-zh-rCN/strings.xml
index 1b6c4e4..9017482 100644
--- a/quickstep/res/values-zh-rCN/strings.xml
+++ b/quickstep/res/values-zh-rCN/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"始终显示任务栏"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"更改导航模式"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"任务栏分隔线"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"移到顶部/左侧"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"移到底部/右侧"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{多个应用}other{多个应用}}"</string>
diff --git a/quickstep/res/values-zh-rHK/strings.xml b/quickstep/res/values-zh-rHK/strings.xml
index 38608e5..672a61c 100644
--- a/quickstep/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-zh-rHK/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"一律顯示工作列"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"變更導覽模式"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"工作列分隔線"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"移至上方/左側"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"移至底部/右側"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{個其他應用程式}other{個其他應用程式}}"</string>
diff --git a/quickstep/res/values-zh-rTW/strings.xml b/quickstep/res/values-zh-rTW/strings.xml
index 3e46779..257c996 100644
--- a/quickstep/res/values-zh-rTW/strings.xml
+++ b/quickstep/res/values-zh-rTW/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"一律顯示工作列"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"變更操作模式"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"工作列分隔線"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"移到上方/左側"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"移到底部/右側"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{個其他應用程式}other{個其他應用程式}}"</string>
diff --git a/quickstep/res/values-zu/strings.xml b/quickstep/res/values-zu/strings.xml
index 120b387..9d7ecd7 100644
--- a/quickstep/res/values-zu/strings.xml
+++ b/quickstep/res/values-zu/strings.xml
@@ -139,6 +139,8 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Bonisa i-Taskbar njalo."</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Shintsha imodi yokufuna"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Isihlukanisi se-Taskbar"</string>
+ <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
+ <skip />
<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="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{i-app eyengeziwe}one{ama-app engeziwe}other{ama-app engeziwe}}"</string>
diff --git a/quickstep/res/values/colors.xml b/quickstep/res/values/colors.xml
index 0bb971e..4c48bd3 100644
--- a/quickstep/res/values/colors.xml
+++ b/quickstep/res/values/colors.xml
@@ -94,7 +94,7 @@
<color name="lottie_yellow600">#f9ab00</color>
<!-- Turn on work apps button -->
- <color name="work_turn_on_stroke">?androidprv:attr/colorAccentPrimaryVariant</color>
+ <color name="work_turn_on_stroke">?attr/materialColorPrimary</color>
<color name="work_fab_bg_color">?attr/materialColorPrimaryFixedDim</color>
<color name="work_fab_icon_color">?attr/materialColorOnPrimaryFixed</color>
</resources>
\ No newline at end of file
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index ce3f3ac..8957e0d 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -350,7 +350,6 @@
<dimen name="taskbar_back_button_left_margin_kids">48dp</dimen>
<dimen name="taskbar_home_button_left_margin_kids">48dp</dimen>
<dimen name="taskbar_icon_size_kids">32dp</dimen>
- <dimen name="taskbar_all_apps_button_translation_x_offset">6dp</dimen>
<dimen name="taskbar_all_apps_search_button_translation_x_offset">6dp</dimen>
<dimen name="taskbar_contextual_button_suw_margin">64dp</dimen>
<dimen name="taskbar_contextual_button_suw_height">64dp</dimen>
@@ -480,6 +479,17 @@
<dimen name="bubble_expanded_view_drop_target_padding">24dp</dimen>
<dimen name="bubble_expanded_view_drop_target_margin">16dp</dimen>
+ <!-- Bubble bar flyout view -->
+ <dimen name="bubblebar_flyout_padding">16dp</dimen>
+ <dimen name="bubblebar_flyout_elevation">4dp</dimen>
+ <dimen name="bubblebar_flyout_avatar_message_space">14dp</dimen>
+ <dimen name="bubblebar_flyout_min_width">238dp</dimen>
+ <dimen name="bubblebar_flyout_max_width">276dp</dimen>
+ <dimen name="bubblebar_flyout_triangle_width">12dp</dimen>
+ <dimen name="bubblebar_flyout_triangle_height">10dp</dimen>
+ <dimen name="bubblebar_flyout_triangle_overlap_amount">1dp</dimen>
+ <dimen name="bubblebar_flyout_triangle_radius">2dp</dimen>
+
<!-- Launcher splash screen -->
<!-- Note: keep this value in sync with the WindowManager/Shell dimens.xml -->
<!-- starting_surface_exit_animation_window_shift_length -->
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index f72f3c5..008766b 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -320,6 +320,8 @@
<string name="change_navigation_mode">Change navigation mode</string>
<!-- Accessibility title for the Taskbar vertical divider icon. [CHAR_LIMIT=NONE] -->
<string name="taskbar_divider_a11y_title">Taskbar Divider</string>
+ <!-- Accessibility title for the Taskbar Overflow icon. [CHAR_LIMIT=NONE] -->
+ <string name="taskbar_overflow_a11y_title">Taskbar Overflow</string>
<!-- Label for moving drop target to the top or left side of the screen, depending on orientation (from the Taskbar only). -->
diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
index e940553..a63ba0f 100644
--- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
+++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
@@ -26,7 +26,6 @@
import android.animation.AnimatorSet;
import android.content.Context;
import android.os.Handler;
-import android.os.RemoteException;
import android.util.Log;
import android.view.IRemoteAnimationFinishedCallback;
import android.view.RemoteAnimationTarget;
@@ -210,7 +209,7 @@
* animation finished runnable.
*/
@Override
- public void onAnimationFinished() throws RemoteException {
+ public void onAnimationFinished() {
mASyncFinishRunnable.run();
}
}
@@ -240,12 +239,5 @@
@Override
@UiThread
default void onAnimationCancelled() {}
-
- /**
- * Returns whether this animation factory supports a tightly coupled return animation.
- */
- default boolean supportsReturnTransition() {
- return false;
- }
}
}
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 2457cfd..a64936d 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -51,7 +51,6 @@
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.Utilities.mapBoundToRange;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_BACK_SWIPE_HOME_ANIMATION;
import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
import static com.android.launcher3.testing.shared.TestProtocol.WALLPAPER_OPEN_ANIMATION_FINISHED_MESSAGE;
import static com.android.launcher3.util.DisplayController.isTransientTaskbar;
@@ -111,6 +110,7 @@
import android.view.animation.PathInterpolator;
import android.window.RemoteTransition;
import android.window.TransitionFilter;
+import android.window.WindowAnimationState;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -141,6 +141,9 @@
import com.android.quickstep.RemoteAnimationTargets;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskViewUtils;
+import com.android.quickstep.util.AlreadyStartedBackAnimState;
+import com.android.quickstep.util.AnimatorBackState;
+import com.android.quickstep.util.BackAnimState;
import com.android.quickstep.util.MultiValueUpdateListener;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.RectFSpringAnim.DefaultSpringConfig;
@@ -176,9 +179,6 @@
*/
public class QuickstepTransitionManager implements OnDeviceProfileChangeListener {
- private static final String TRANSITION_COOKIE_PREFIX =
- "com.android.launcher3.QuickstepTransitionManager_activityLaunch";
-
private static final boolean ENABLE_SHELL_STARTING_SURFACE =
SystemProperties.getBoolean("persist.debug.shell_starting_surface", true);
@@ -347,14 +347,7 @@
IRemoteCallback endCallback = completeRunnableListCallback(onEndCallback);
options.setOnAnimationAbortListener(endCallback);
options.setOnAnimationFinishedListener(endCallback);
-
- IBinder cookie = mAppLaunchRunner.supportsReturnTransition()
- ? ((ContainerAnimationRunner) mAppLaunchRunner).getCookie() : null;
- addLaunchCookie(cookie, itemInfo, options);
-
- // Register the return animation so it can be triggered on back from the app to home.
- maybeRegisterAppReturnTransition(v);
-
+ options.setLaunchCookie(StableViewInfo.toLaunchCookie(itemInfo));
return new ActivityOptionsWrapper(options, onEndCallback);
}
@@ -367,21 +360,9 @@
ItemInfo tag = (ItemInfo) v.getTag();
ContainerAnimationRunner containerRunner = null;
if (tag != null && tag.shouldUseBackgroundAnimation()) {
- // The cookie should only override the default used by launcher if container return
- // animations are enabled.
- ActivityTransitionAnimator.TransitionCookie cookie =
- checkReturnAnimationsFlags()
- ? new ActivityTransitionAnimator.TransitionCookie(
- TRANSITION_COOKIE_PREFIX + tag.id)
- : null;
- ContainerAnimationRunner launchAnimationRunner =
- ContainerAnimationRunner.fromView(
- v, cookie, true /* forLaunch */, mLauncher, mStartingWindowListener,
- onEndCallback);
-
- if (launchAnimationRunner != null) {
- containerRunner = launchAnimationRunner;
- }
+ containerRunner = ContainerAnimationRunner.fromView(
+ v, true /* forLaunch */, mLauncher, mStartingWindowListener, onEndCallback,
+ null /* windowState */);
}
mAppLaunchRunner = containerRunner != null
@@ -391,51 +372,6 @@
}
/**
- * If container return animations are enabled and the current launch runner is itself a
- * {@link ContainerAnimationRunner}, registers a matching return animation that de-registers
- * itself after it has run once or is made obsolete by the view going away.
- */
- private void maybeRegisterAppReturnTransition(View v) {
- if (!checkReturnAnimationsFlags() || !mAppLaunchRunner.supportsReturnTransition()) {
- return;
- }
-
- ActivityTransitionAnimator.TransitionCookie cookie =
- ((ContainerAnimationRunner) mAppLaunchRunner).getCookie();
- RunnableList onEndCallback = new RunnableList();
- ContainerAnimationRunner runner =
- ContainerAnimationRunner.fromView(
- v, cookie, false /* forLaunch */, mLauncher, mStartingWindowListener,
- onEndCallback);
- RemoteTransition transition =
- new RemoteTransition(
- new LauncherAnimationRunner(
- mHandler, runner, true /* startAtFrontOfQueue */
- ).toRemoteTransition()
- );
-
- SystemUiProxy.INSTANCE.get(mLauncher).registerRemoteTransition(
- transition, ContainerAnimationRunner.buildBackToHomeFilter(cookie, mLauncher));
- ContainerAnimationRunner.setUpRemoteAnimationCleanup(
- v, transition, onEndCallback, mLauncher);
- }
-
- /**
- * Adds a new launch cookie for the activity launch if supported.
- * Prioritizes the explicitly provided cookie, falling back on extracting one from the given
- * {@link ItemInfo} if necessary.
- */
- private void addLaunchCookie(IBinder cookie, ItemInfo info, ActivityOptions options) {
- if (cookie == null) {
- cookie = StableViewInfo.toLaunchCookie(info);
- }
-
- if (cookie != null) {
- options.setLaunchCookie(cookie);
- }
- }
-
- /**
* Whether the launch is a recents app transition and we should do a launch animation
* from the recents view. Note that if the remote animation targets are not provided, this
* may not always be correct as we may resolve the opening app to a task when the animation
@@ -1613,19 +1549,48 @@
* Creates the {@link RectFSpringAnim} and {@link AnimatorSet} required to animate
* the transition.
*/
- public Pair<RectFSpringAnim, AnimatorSet> createWallpaperOpenAnimations(
+ @NonNull
+ public BackAnimState createWallpaperOpenAnimations(
RemoteAnimationTarget[] appTargets,
- RemoteAnimationTarget[] wallpaperTargets,
+ RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonAppTargets,
RectF startRect,
float startWindowCornerRadius,
boolean fromPredictiveBack) {
+ View launcherView = findLauncherView(appTargets);
+ if (checkReturnAnimationsFlags()
+ && launcherView != null
+ && launcherView.getTag() instanceof ItemInfo info
+ && info.shouldUseBackgroundAnimation()) {
+ // Try to create a return animation
+ RunnableList onEndCallback = new RunnableList();
+ WindowAnimationState windowState = new WindowAnimationState();
+ windowState.bounds = startRect;
+ windowState.bottomLeftRadius = windowState.bottomRightRadius =
+ windowState.topLeftRadius = windowState.topRightRadius =
+ startWindowCornerRadius;
+ ContainerAnimationRunner runner = ContainerAnimationRunner.fromView(
+ launcherView, false /* forLaunch */, mLauncher, mStartingWindowListener,
+ onEndCallback, windowState);
+ if (runner != null) {
+ runner.startAnimation(TRANSIT_CLOSE,
+ appTargets, wallpapers, nonAppTargets,
+ new IRemoteAnimationFinishedCallback.Stub() {
+ @Override
+ public void onAnimationFinished() {
+ onEndCallback.executeAllAndDestroy();
+ }
+ });
+ return new AlreadyStartedBackAnimState(onEndCallback);
+ }
+ }
+
AnimatorSet anim = new AnimatorSet();
RectFSpringAnim rectFSpringAnim = null;
final boolean launcherIsForceInvisibleOrOpening = mLauncher.isForceInvisible()
|| launcherIsATargetWithMode(appTargets, MODE_OPENING);
- View launcherView = findLauncherView(appTargets);
boolean playFallBackAnimation = (launcherView == null
&& launcherIsForceInvisibleOrOpening)
|| mLauncher.getWorkspace().isOverlayShown()
@@ -1633,7 +1598,7 @@
boolean playWorkspaceReveal = !fromPredictiveBack;
boolean skipAllAppsScale = false;
- if (ENABLE_BACK_SWIPE_HOME_ANIMATION.get() && !playFallBackAnimation) {
+ if (!playFallBackAnimation) {
PointF velocity;
if (enableScalingRevealHomeAnimation()) {
velocity = new PointF();
@@ -1725,7 +1690,7 @@
}
}
- return new Pair(rectFSpringAnim, anim);
+ return new AnimatorBackState(rectFSpringAnim, anim);
}
public static int getTaskbarToHomeDuration() {
@@ -1775,14 +1740,14 @@
}
}
- Pair<RectFSpringAnim, AnimatorSet> pair = createWallpaperOpenAnimations(
- appTargets, wallpaperTargets, resolveRectF,
+ BackAnimState bankAnimState = createWallpaperOpenAnimations(
+ appTargets, wallpaperTargets, nonAppTargets, resolveRectF,
QuickStepContract.getWindowCornerRadius(mLauncher),
false /* fromPredictiveBack */);
TaskViewUtils.createSplitAuxiliarySurfacesAnimator(nonAppTargets, false, null);
mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL);
- result.setAnimation(pair.second, mLauncher);
+ bankAnimState.applyToAnimationResult(result, mLauncher);
}
}
@@ -1850,29 +1815,19 @@
/** The delegate runner that handles the actual animation. */
private final RemoteAnimationDelegate<IRemoteAnimationFinishedCallback> mDelegate;
- @Nullable
- private final ActivityTransitionAnimator.TransitionCookie mCookie;
-
private ContainerAnimationRunner(
- RemoteAnimationDelegate<IRemoteAnimationFinishedCallback> delegate,
- ActivityTransitionAnimator.TransitionCookie cookie) {
+ RemoteAnimationDelegate<IRemoteAnimationFinishedCallback> delegate) {
mDelegate = delegate;
- mCookie = cookie;
- }
-
- @Nullable
- ActivityTransitionAnimator.TransitionCookie getCookie() {
- return mCookie;
}
@Nullable
static ContainerAnimationRunner fromView(
View v,
- ActivityTransitionAnimator.TransitionCookie cookie,
boolean forLaunch,
Launcher launcher,
StartingWindowListener startingWindowListener,
- RunnableList onEndCallback) {
+ RunnableList onEndCallback,
+ @Nullable WindowAnimationState windowState) {
if (!forLaunch && !checkReturnAnimationsFlags()) {
throw new IllegalStateException(
"forLaunch cannot be false when the enableContainerReturnAnimations or "
@@ -1882,7 +1837,7 @@
// First the controller is created. This is used by the runner to animate the
// origin/target view.
ActivityTransitionAnimator.Controller controller =
- buildController(v, cookie, forLaunch);
+ buildController(v, forLaunch, windowState);
if (controller == null) {
return null;
}
@@ -1907,8 +1862,7 @@
return new ContainerAnimationRunner(
new ActivityTransitionAnimator.AnimationDelegate(
- MAIN_EXECUTOR, controller, callback, listener),
- cookie);
+ MAIN_EXECUTOR, controller, callback, listener));
}
/**
@@ -1918,7 +1872,7 @@
*/
@Nullable
private static ActivityTransitionAnimator.Controller buildController(
- View v, ActivityTransitionAnimator.TransitionCookie cookie, boolean isLaunching) {
+ View v, boolean isLaunching, @Nullable WindowAnimationState windowState) {
View viewToUse = findLaunchableViewWithBackground(v);
if (viewToUse == null) {
return null;
@@ -1949,8 +1903,8 @@
@Nullable
@Override
- public ActivityTransitionAnimator.TransitionCookie getTransitionCookie() {
- return cookie;
+ public WindowAnimationState getWindowAnimatorState() {
+ return windowState;
}
};
}
@@ -1964,81 +1918,26 @@
View view) {
View current = view;
while (current.getBackground() == null || !(current instanceof LaunchableView)) {
- if (!(current.getParent() instanceof View)) {
+ if (current.getParent() instanceof View v) {
+ current = v;
+ } else {
return null;
}
-
- current = (View) current.getParent();
}
-
return (T) current;
}
- /**
- * Builds the filter used by WM Shell to match app closing transitions (only back, no home
- * button/gesture) to the given launch cookie.
- */
- static TransitionFilter buildBackToHomeFilter(
- ActivityTransitionAnimator.TransitionCookie cookie, Launcher launcher) {
- // Closing activity must include the cookie in its list of launch cookies.
- TransitionFilter.Requirement appRequirement = new TransitionFilter.Requirement();
- appRequirement.mActivityType = ACTIVITY_TYPE_STANDARD;
- appRequirement.mLaunchCookie = cookie;
- appRequirement.mModes = new int[]{TRANSIT_CLOSE, TRANSIT_TO_BACK};
- // Opening activity must be Launcher.
- TransitionFilter.Requirement launcherRequirement = new TransitionFilter.Requirement();
- launcherRequirement.mActivityType = ACTIVITY_TYPE_HOME;
- launcherRequirement.mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT};
- launcherRequirement.mTopActivity = launcher.getComponentName();
- // Transition types CLOSE and TO_BACK match the back button/gesture but not the home
- // button/gesture.
- TransitionFilter filter = new TransitionFilter();
- filter.mTypeSet = new int[]{TRANSIT_CLOSE, TRANSIT_TO_BACK};
- filter.mRequirements =
- new TransitionFilter.Requirement[]{appRequirement, launcherRequirement};
- return filter;
- }
-
- /**
- * Creates various conditions to ensure that the given transition is cleaned up correctly
- * when necessary:
- * - if the transition has run, it is the callback that unregisters it;
- * - if the associated view is detached before the transition has had an opportunity to run,
- * a {@link View.OnAttachStateChangeListener} allows us to do the same (and removes
- * itself).
- */
- static void setUpRemoteAnimationCleanup(
- View v, RemoteTransition transition, RunnableList callback, Launcher launcher) {
- View.OnAttachStateChangeListener listener = new View.OnAttachStateChangeListener() {
- @Override
- public void onViewAttachedToWindow(@NonNull View v) {}
-
- @Override
- public void onViewDetachedFromWindow(@NonNull View v) {
- SystemUiProxy.INSTANCE.get(launcher)
- .unregisterRemoteTransition(transition);
- v.removeOnAttachStateChangeListener(this);
- }
- };
-
- // Remove the animation as soon as it has run once.
- callback.add(() -> {
- SystemUiProxy.INSTANCE.get(launcher).unregisterRemoteTransition(transition);
- if (v != null) {
- v.removeOnAttachStateChangeListener(listener);
- }
- });
-
- // Remove the animation when the view is detached from the hierarchy.
- // This is so that if back is not invoked (e.g. if we go back home through the home
- // gesture) we don't have obsolete transitions staying registered.
- v.addOnAttachStateChangeListener(listener);
- }
-
@Override
public void onAnimationStart(int transit, RemoteAnimationTarget[] appTargets,
RemoteAnimationTarget[] wallpaperTargets, RemoteAnimationTarget[] nonAppTargets,
LauncherAnimationRunner.AnimationResult result) {
+ startAnimation(
+ transit, appTargets, wallpaperTargets, nonAppTargets, result);
+ }
+
+ public void startAnimation(int transit, RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets, RemoteAnimationTarget[] nonAppTargets,
+ IRemoteAnimationFinishedCallback result) {
mDelegate.onAnimationStart(
transit, appTargets, wallpaperTargets, nonAppTargets, result);
}
@@ -2047,11 +1946,6 @@
public void onAnimationCancelled() {
mDelegate.onAnimationCancelled();
}
-
- @Override
- public boolean supportsReturnTransition() {
- return true;
- }
}
/**
diff --git a/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java b/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
index 7a8b58e..32fda48 100644
--- a/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
@@ -31,6 +31,7 @@
import android.view.accessibility.AccessibilityManager;
import androidx.annotation.ColorInt;
+import androidx.annotation.VisibleForTesting;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -253,4 +254,9 @@
public View getFocusedChild() {
return null;
}
+
+ @VisibleForTesting
+ public DividerType getDividerType() {
+ return mDividerType;
+ }
}
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
index f29980b..3dcb2ac 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
@@ -16,9 +16,9 @@
package com.android.launcher3.statehandlers;
import static android.view.View.VISIBLE;
+import static android.window.flags.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.wm.shell.shared.desktopmode.DesktopModeFlags.WALLPAPER_ACTIVITY;
import android.content.Context;
import android.os.Debug;
@@ -167,7 +167,8 @@
notifyDesktopVisibilityListeners(areDesktopTasksVisibleNow);
}
- if (!WALLPAPER_ACTIVITY.isEnabled(mContext) && wasVisible != isVisible) {
+ if (!ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()
+ && wasVisible != isVisible) {
// TODO: b/333533253 - Remove after flag rollout
if (mVisibleDesktopTasksCount > 0) {
setLauncherViewsVisibility(View.INVISIBLE);
@@ -225,7 +226,7 @@
notifyDesktopVisibilityListeners(areDesktopTasksVisibleNow);
}
- if (WALLPAPER_ACTIVITY.isEnabled(mContext)) {
+ if (ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()) {
return;
}
// TODO: b/333533253 - Clean up after flag rollout
@@ -252,7 +253,7 @@
for (DesktopVisibilityListener listener : mDesktopVisibilityListeners) {
listener.onDesktopVisibilityChanged(areDesktopTasksVisible);
}
- DisplayController.handleInfoChangeForDesktopMode(mContext);
+ DisplayController.INSTANCE.get(mContext).notifyConfigChange();
}
private void notifyTaskbarDesktopModeListeners(boolean doesAnyTaskRequireTaskbarRounding) {
@@ -341,7 +342,7 @@
if (mContext == null) {
return;
}
- if (WALLPAPER_ACTIVITY.isEnabled(mContext)) {
+ if (ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()) {
return;
}
if (DEBUG) {
@@ -376,7 +377,7 @@
if (mContext == null) {
return;
}
- if (WALLPAPER_ACTIVITY.isEnabled(mContext)) {
+ if (ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()) {
return;
}
if (DEBUG) {
@@ -396,7 +397,7 @@
if (mContext == null) {
return;
}
- if (WALLPAPER_ACTIVITY.isEnabled(mContext)) {
+ if (ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()) {
return;
}
if (DEBUG) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
index ea432f3..9912c6c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
@@ -17,13 +17,17 @@
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
+import android.view.MotionEvent;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import com.android.launcher3.Flags;
import com.android.launcher3.R;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayDragLayer;
+import com.android.launcher3.util.TouchController;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.util.DesktopTask;
import com.android.quickstep.util.GroupTask;
@@ -36,6 +40,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@@ -43,7 +48,7 @@
* Handles initialization of the {@link KeyboardQuickSwitchViewController}.
*/
public final class KeyboardQuickSwitchController implements
- TaskbarControllers.LoggableTaskbarController {
+ TaskbarControllers.LoggableTaskbarController, TouchController {
@VisibleForTesting
public static final int MAX_TASKS = 6;
@@ -64,6 +69,7 @@
private TaskbarControllers mControllers;
@Nullable private KeyboardQuickSwitchViewController mQuickSwitchViewController;
+ @Nullable private TaskbarOverlayContext mOverlayContext;
private boolean mHasDesktopTask = false;
private boolean mWasDesktopTaskFilteredOut = false;
@@ -95,7 +101,21 @@
openQuickSwitchView(-1);
}
+ /**
+ * Opens the view with a filtered list of tasks.
+ * @param taskIdsToExclude A list of tasks to exclude in the opened view.
+ */
+ void openQuickSwitchView(@NonNull Set<Integer> taskIdsToExclude) {
+ openQuickSwitchView(-1, taskIdsToExclude, true);
+ }
+
private void openQuickSwitchView(int currentFocusedIndex) {
+ openQuickSwitchView(currentFocusedIndex, Collections.emptySet(), false);
+ }
+
+ private void openQuickSwitchView(int currentFocusedIndex,
+ @NonNull Set<Integer> taskIdsToExclude,
+ boolean wasOpenedFromTaskbar) {
if (mQuickSwitchViewController != null) {
if (!mQuickSwitchViewController.isCloseAnimationRunning()) {
return;
@@ -103,21 +123,25 @@
// Allow the KQS to be reopened during the close animation to make it more responsive
closeQuickSwitchView(false);
}
- TaskbarOverlayContext overlayContext =
- mControllers.taskbarOverlayController.requestWindow();
+ mOverlayContext = mControllers.taskbarOverlayController.requestWindow();
+ if (Flags.taskbarOverflow()) {
+ mOverlayContext.getDragLayer().addTouchController(this);
+ }
KeyboardQuickSwitchView keyboardQuickSwitchView =
- (KeyboardQuickSwitchView) overlayContext.getLayoutInflater()
+ (KeyboardQuickSwitchView) mOverlayContext.getLayoutInflater()
.inflate(
R.layout.keyboard_quick_switch_view,
- overlayContext.getDragLayer(),
+ mOverlayContext.getDragLayer(),
/* attachToRoot= */ false);
mQuickSwitchViewController = new KeyboardQuickSwitchViewController(
- mControllers, overlayContext, keyboardQuickSwitchView, mControllerCallbacks);
+ mControllers, mOverlayContext, keyboardQuickSwitchView, mControllerCallbacks);
final boolean onDesktop =
mControllers.taskbarDesktopModeController.getAreDesktopTasksVisible();
- if (mModel.isTaskListValid(mTaskListChangeId)) {
+ // TODO(b/368119679) For now we will re-process the task list every time, but this can be
+ // optimized if we have the same set of task ids to exclude.
+ if (mModel.isTaskListValid(mTaskListChangeId) && !Flags.taskbarOverflow()) {
// When we are opening the KQS with no focus override, check if the first task is
// running. If not, focus that first task.
mQuickSwitchViewController.openQuickSwitchView(
@@ -128,7 +152,8 @@
? 0 : currentFocusedIndex,
onDesktop,
mHasDesktopTask,
- mWasDesktopTaskFilteredOut);
+ mWasDesktopTaskFilteredOut,
+ wasOpenedFromTaskbar);
return;
}
@@ -136,9 +161,9 @@
mHasDesktopTask = false;
mWasDesktopTaskFilteredOut = false;
if (onDesktop) {
- processLoadedTasksOnDesktop(tasks);
+ processLoadedTasksOnDesktop(tasks, taskIdsToExclude);
} else {
- processLoadedTasks(tasks);
+ processLoadedTasks(tasks, taskIdsToExclude);
}
// Check if the first task is running after the recents model has updated so that we use
// the correct index.
@@ -150,15 +175,21 @@
? 0 : currentFocusedIndex,
onDesktop,
mHasDesktopTask,
- mWasDesktopTaskFilteredOut);
+ mWasDesktopTaskFilteredOut,
+ wasOpenedFromTaskbar);
});
}
- private void processLoadedTasks(List<GroupTask> tasks) {
+ private boolean shouldExcludeTask(GroupTask task, Set<Integer> taskIdsToExclude) {
+ return Flags.taskbarOverflow() && taskIdsToExclude.contains(task.task1.key.id);
+ }
+
+ private void processLoadedTasks(List<GroupTask> tasks, Set<Integer> taskIdsToExclude) {
// Only store MAX_TASK tasks, from most to least recent
Collections.reverse(tasks);
mTasks = tasks.stream()
- .filter(task -> !(task instanceof DesktopTask))
+ .filter(task -> !(task instanceof DesktopTask)
+ && !shouldExcludeTask(task, taskIdsToExclude))
.limit(MAX_TASKS)
.collect(Collectors.toList());
@@ -176,12 +207,15 @@
tasks.size() - (mWasDesktopTaskFilteredOut ? 1 : 0) - MAX_TASKS);
}
- private void processLoadedTasksOnDesktop(List<GroupTask> tasks) {
+ private void processLoadedTasksOnDesktop(List<GroupTask> tasks, Set<Integer> taskIdsToExclude) {
// Find the single desktop task that contains a grouping of desktop tasks
DesktopTask desktopTask = findDesktopTask(tasks);
if (desktopTask != null) {
- mTasks = desktopTask.tasks.stream().map(GroupTask::new).collect(Collectors.toList());
+ mTasks = desktopTask.tasks.stream()
+ .map(GroupTask::new)
+ .filter(task -> !shouldExcludeTask(task, taskIdsToExclude))
+ .collect(Collectors.toList());
// All other tasks, apart from the grouped desktop task, are hidden
mNumHiddenTasks = Math.max(0, tasks.size() - 1);
} else {
@@ -220,6 +254,27 @@
? -1 : mQuickSwitchViewController.launchFocusedTask();
}
+ @Override
+ public boolean onControllerTouchEvent(MotionEvent ev) {
+ return false;
+ }
+
+ @Override
+ public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+ if (mQuickSwitchViewController == null
+ || mOverlayContext == null
+ || !Flags.taskbarOverflow()) {
+ return false;
+ }
+
+ TaskbarOverlayDragLayer dragLayer = mOverlayContext.getDragLayer();
+ if (ev.getAction() == MotionEvent.ACTION_DOWN
+ && !mQuickSwitchViewController.isEventOverKeyboardQuickSwitch(dragLayer, ev)) {
+ closeQuickSwitchView(true);
+ }
+ return false;
+ }
+
void onDestroy() {
if (mQuickSwitchViewController != null) {
mQuickSwitchViewController.onDestroy();
@@ -279,6 +334,11 @@
}
void onCloseComplete() {
+ if (Flags.taskbarOverflow() && mOverlayContext != null) {
+ mOverlayContext.getDragLayer()
+ .removeTouchController(KeyboardQuickSwitchController.this);
+ }
+ mOverlayContext = null;
mQuickSwitchViewController = null;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
index b4102a9..fc8204a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
@@ -58,8 +58,12 @@
import java.util.Locale;
/**
- * View that allows quick switching between recent tasks through keyboard alt-tab and alt-shift-tab
- * commands.
+ * View that allows quick switching between recent tasks.
+ *
+ * Can be access via:
+ * - keyboard alt-tab
+ * - alt-shift-tab
+ * - taskbar overflow button
*/
public class KeyboardQuickSwitchView extends ConstraintLayout {
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
index 40e77e2..1c8a094 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
@@ -19,7 +19,9 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.view.Gravity;
import android.view.KeyEvent;
+import android.view.MotionEvent;
import android.view.animation.AnimationUtils;
import android.window.RemoteTransition;
@@ -30,6 +32,8 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayDragLayer;
+import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.util.SlideInRemoteTransition;
@@ -83,7 +87,9 @@
int currentFocusIndexOverride,
boolean onDesktop,
boolean hasDesktopTask,
- boolean wasDesktopTaskFilteredOut) {
+ boolean wasDesktopTaskFilteredOut,
+ boolean wasOpenedFromTaskbar) {
+ positionView(wasOpenedFromTaskbar);
mOverlayContext.getDragLayer().addView(mKeyboardQuickSwitchView);
mOnDesktop = onDesktop;
mWasDesktopTaskFilteredOut = wasDesktopTaskFilteredOut;
@@ -98,6 +104,19 @@
/* useDesktopTaskView= */ !onDesktop && hasDesktopTask);
}
+ protected void positionView(boolean wasOpenedFromTaskbar) {
+ if (!wasOpenedFromTaskbar) {
+ // Keep the default positioning.
+ return;
+ }
+
+ BaseDragLayer.LayoutParams lp = new BaseDragLayer.LayoutParams(
+ mKeyboardQuickSwitchView.getLayoutParams());
+ lp.width = BaseDragLayer.LayoutParams.WRAP_CONTENT;
+ lp.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
+ mKeyboardQuickSwitchView.setLayoutParams(lp);
+ }
+
boolean isCloseAnimationRunning() {
return mCloseAnimation != null;
}
@@ -219,6 +238,13 @@
pw.println(prefix + "\tmWasDesktopTaskFilteredOut=" + mWasDesktopTaskFilteredOut);
}
+ /**
+ * @return True if the MotionEvent is over the {@link KeyboardQuickSwitchView}.
+ */
+ protected boolean isEventOverKeyboardQuickSwitch(TaskbarOverlayDragLayer dl, MotionEvent ev) {
+ return dl.isEventOverView(mKeyboardQuickSwitchView, ev);
+ }
+
class ViewCallbacks {
boolean onKeyUp(int keyCode, KeyEvent event, boolean isRTL, boolean allowTraversal) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 5513599..477f90c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -15,15 +15,15 @@
*/
package com.android.launcher3.taskbar;
+import static android.window.flags.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY;
+
import static com.android.launcher3.QuickstepTransitionManager.TRANSIENT_TASKBAR_TRANSITION_DURATION;
import static com.android.launcher3.statemanager.BaseState.FLAG_NON_INTERACTIVE;
import static com.android.launcher3.taskbar.TaskbarEduTooltipControllerKt.TOOLTIP_STEP_FEATURES;
import static com.android.launcher3.taskbar.TaskbarLauncherStateController.FLAG_VISIBLE;
-import static com.android.wm.shell.shared.desktopmode.DesktopModeFlags.WALLPAPER_ACTIVITY;
import android.animation.Animator;
import android.animation.AnimatorSet;
-import android.graphics.Rect;
import android.window.RemoteTransition;
import androidx.annotation.NonNull;
@@ -50,6 +50,7 @@
import com.android.quickstep.util.TISBindHelper;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import java.io.PrintWriter;
import java.util.Arrays;
@@ -126,7 +127,7 @@
@Override
protected void onDestroy() {
- onLauncherVisibilityChanged(false);
+ onLauncherVisibilityChanged(false /* isVisible */, true /* fromInitOrDestroy */);
super.onDestroy();
mTaskbarLauncherStateController.onDestroy();
@@ -135,11 +136,6 @@
mHomeState.removeListener(mVisibilityChangeListener);
}
- /** Returns {@code true} if launcher is currently presenting the home screen. */
- public boolean isOnHome() {
- return mTaskbarLauncherStateController.isOnHome();
- }
-
private void onInAppDisplayProgressChanged() {
if (mControllers != null) {
// Update our shared state so we can restore it if taskbar gets recreated.
@@ -163,6 +159,16 @@
shouldDelayLauncherStateAnim);
}
+ @Override
+ public void stashHotseat(boolean stash) {
+ mTaskbarLauncherStateController.stashHotseat(stash);
+ }
+
+ @Override
+ public void unStashHotseatInstantly() {
+ mTaskbarLauncherStateController.unStashHotseatInstantly();
+ }
+
/**
* Adds the Launcher resume animator to the given animator set.
*
@@ -183,35 +189,20 @@
}
/**
- * Returns the bounds of launcher's hotseat.
- */
- public void getHotseatBounds(Rect hotseatBoundsOut) {
- DeviceProfile launcherDP = mLauncher.getDeviceProfile();
- if (launcherDP.isQsbInline) {
- // Not currently supported.
- hotseatBoundsOut.setEmpty();
- return;
- }
- int left = (launcherDP.widthPx - launcherDP.getHotseatWidthPx()
- - mLauncher.getHotseat().getUnusedHorizontalSpace()) / 2;
- int right = left + launcherDP.getHotseatWidthPx();
- int bottom = launcherDP.getHotseatLayoutPadding(mLauncher).bottom;
- int top = bottom - launcherDP.hotseatCellHeightPx;
- hotseatBoundsOut.set(left, top, right, bottom);
- }
-
- /**
* Should be called from onResume() and onPause(), and animates the Taskbar accordingly.
*/
@Override
public void onLauncherVisibilityChanged(boolean isVisible) {
+ if (DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(mLauncher)) {
+ DisplayController.INSTANCE.get(mLauncher).notifyConfigChange();
+ }
onLauncherVisibilityChanged(isVisible, false /* fromInit */);
}
- private void onLauncherVisibilityChanged(boolean isVisible, boolean fromInit) {
+ private void onLauncherVisibilityChanged(boolean isVisible, boolean fromInitOrDestroy) {
onLauncherVisibilityChanged(
isVisible,
- fromInit,
+ fromInitOrDestroy,
/* startAnimation= */ true,
DisplayController.isTransientTaskbar(mLauncher)
? TRANSIENT_TASKBAR_TRANSITION_DURATION
@@ -222,7 +213,7 @@
@Nullable
private Animator onLauncherVisibilityChanged(
- boolean isVisible, boolean fromInit, boolean startAnimation, int duration) {
+ boolean isVisible, boolean fromInitOrDestroy, boolean startAnimation, int duration) {
// Launcher is resumed during the swipe-to-overview gesture under shell-transitions, so
// avoid updating taskbar state in that situation (when it's non-interactive -- or
// "background") to avoid premature animations.
@@ -233,14 +224,14 @@
return null;
}
- if (!WALLPAPER_ACTIVITY.isEnabled(mLauncher)
+ if (!ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()
&& mControllers.taskbarDesktopModeController.getAreDesktopTasksVisible()) {
// TODO: b/333533253 - Remove after flag rollout
isVisible = false;
}
mTaskbarLauncherStateController.updateStateForFlag(FLAG_VISIBLE, isVisible);
- if (fromInit || mControllers == null) {
+ if (fromInitOrDestroy) {
duration = 0;
}
return mTaskbarLauncherStateController.applyState(duration, startAnimation);
@@ -476,4 +467,13 @@
protected String getTaskbarUIControllerName() {
return "LauncherTaskbarUIController";
}
+
+ @Override
+ public void onSwipeToUnstashTaskbar() {
+ // Once taskbar is unstashed, the user cannot return back to the overlay. We can
+ // clear it here to set the expected state once the user goes home.
+ if (mLauncher.getWorkspace().isOverlayShown()) {
+ mLauncher.getWorkspace().onOverlayScrollChanged(0);
+ }
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index a979d58..2ac5793 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -91,6 +91,7 @@
import com.android.launcher3.anim.AlphaUpdateListener;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarButton;
+import com.android.launcher3.taskbar.bubbles.BubbleBarController;
import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory;
import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.NavButtonLayoutter;
import com.android.launcher3.taskbar.navbutton.NearestTouchFrame;
@@ -106,6 +107,7 @@
import com.android.systemui.shared.statusbar.phone.BarTransitions;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -115,7 +117,8 @@
/**
* Controller for managing nav bar buttons in taskbar
*/
-public class NavbarButtonsViewController implements TaskbarControllers.LoggableTaskbarController {
+public class NavbarButtonsViewController implements TaskbarControllers.LoggableTaskbarController,
+ BubbleBarController.BubbleBarLocationListener {
private final Rect mTempRect = new Rect();
@@ -1168,6 +1171,62 @@
mHitboxExtender.onAnimationProgressToOverview(alignment);
}
+ /** Adjusts navigation buttons layout accordingly to the bubble bar position. */
+ @Override
+ public void onBubbleBarLocationUpdated(BubbleBarLocation location) {
+ mNavButtonContainer.setTranslationX(getNavBarTranslationX(location));
+ }
+
+ /** Animates navigation buttons accordingly to the bubble bar position. */
+ @Override
+ public void onBubbleBarLocationAnimated(BubbleBarLocation location) {
+ // TODO(b/346381754) add the teleport animation similarly to the bubble bar
+ mNavButtonContainer.setTranslationX(getNavBarTranslationX(location));
+ }
+
+ private int getNavBarTranslationX(BubbleBarLocation location) {
+ boolean isNavbarOnRight = location.isOnLeft(mNavButtonsView.isLayoutRtl());
+ DeviceProfile dp = mContext.getDeviceProfile();
+ float navBarTargetStartX;
+ if (mContext.shouldStartAlignTaskbar()) {
+ int navBarSpacing = dp.inlineNavButtonsEndSpacingPx;
+ // If the taskbar is start aligned the navigation bar is aligned to the start or end of
+ // the container, depending on the bubble bar location
+ if (isNavbarOnRight) {
+ navBarTargetStartX = dp.widthPx - navBarSpacing - mNavButtonContainer.getWidth();
+ } else {
+ navBarTargetStartX = navBarSpacing;
+ }
+ } else {
+ // If the task bar is not start aligned, the navigation bar is located in the center
+ // between the taskbar and screen edges, depending on the bubble bar location.
+ float navbarWidth = mNavButtonContainer.getWidth();
+ Rect taskbarBounds = mControllers.taskbarViewController.getIconLayoutBounds();
+ if (isNavbarOnRight) {
+ if (mNavButtonsView.isLayoutRtl()) {
+ float taskBarEnd = taskbarBounds.right;
+ navBarTargetStartX = (dp.widthPx + taskBarEnd - navbarWidth) / 2;
+ } else {
+ navBarTargetStartX = mNavButtonContainer.getLeft();
+ }
+ } else {
+ float taskBarStart = taskbarBounds.left;
+ navBarTargetStartX = (taskBarStart - navbarWidth) / 2;
+ }
+ }
+ return (int) navBarTargetStartX - mNavButtonContainer.getLeft();
+ }
+
+ /** Adjusts the navigation buttons layout position according to the bubble bar location. */
+ public void onTaskbarLayoutChange() {
+ if (com.android.wm.shell.Flags.enableBubbleBarInPersistentTaskBar()
+ && mControllers.bubbleControllers.isPresent()) {
+ BubbleBarLocation bubblesLocation = mControllers.bubbleControllers.get()
+ .bubbleBarViewController.getBubbleBarLocation();
+ onBubbleBarLocationUpdated(bubblesLocation);
+ }
+ }
+
private class RotationButtonListener implements RotationButton.RotationButtonUpdatesCallback {
@Override
public void onVisibilityChanged(boolean isVisible) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/NewWindowTaskbarShortcut.kt b/quickstep/src/com/android/launcher3/taskbar/NewWindowTaskbarShortcut.kt
new file mode 100644
index 0000000..dc66e0b
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/NewWindowTaskbarShortcut.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.taskbar
+
+import android.content.Context
+import android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK
+import android.view.View
+import com.android.launcher3.AbstractFloatingView
+import com.android.launcher3.R
+import com.android.launcher3.model.data.ItemInfo
+import com.android.launcher3.popup.SystemShortcut
+import com.android.launcher3.views.ActivityContext
+
+/**
+ * A single menu item shortcut to execute creating a new instance of an app. Default interaction for
+ * [onClick] is to launch the app in full screen or as a floating window in Desktop Mode.
+ */
+class NewWindowTaskbarShortcut<T>(target: T, itemInfo: ItemInfo?, originalView: View?) :
+ SystemShortcut<T>(
+ R.drawable.desktop_mode_ic_taskbar_menu_new_window,
+ R.string.new_window_option_taskbar,
+ target,
+ itemInfo,
+ originalView
+ ) where T : Context?, T : ActivityContext? {
+
+ override fun onClick(v: View?) {
+ val intent = mItemInfo.intent ?: return
+ intent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK)
+ mTarget?.startActivitySafely(v, intent, mItemInfo)
+ AbstractFloatingView.closeAllOpenViews(mTarget)
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
index fabf3a5..7273fac 100644
--- a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
@@ -208,11 +208,8 @@
* Creates and returns a {@link RevealOutlineAnimation} Animator that updates the stashed handle
* shape and size. When stashed, the shape is a thin rounded pill. When unstashed, the shape
* morphs into the size of where the taskbar icons will be.
- *
- * @param taskbarToHotseatOffsets A Rect of offsets used to transform the bounds of the
- * stashed handle to wrap around the hotseat items.
*/
- public Animator createRevealAnimToIsStashed(boolean isStashed, Rect taskbarToHotseatOffsets) {
+ public Animator createRevealAnimToIsStashed(boolean isStashed) {
Rect visualBounds = mControllers.taskbarViewController.getIconLayoutVisualBounds();
float startRadius = mStashedHandleRadius;
@@ -223,13 +220,6 @@
visualBounds.bottom += heightDiff;
startRadius = visualBounds.height() / 2f;
-
- // We use these offsets to create a larger stashed handle to wrap around the items
- // of the hotseat. This is only used for certain animations.
- visualBounds.top += taskbarToHotseatOffsets.top;
- visualBounds.bottom += taskbarToHotseatOffsets.bottom;
- visualBounds.left += taskbarToHotseatOffsets.left;
- visualBounds.right += taskbarToHotseatOffsets.right;
}
final RevealOutlineAnimation handleRevealProvider = new RoundedRectRevealOutlineProvider(
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 0efa949..a59445b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -29,6 +29,7 @@
import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
import static com.android.launcher3.AbstractFloatingView.TYPE_TASKBAR_OVERLAY_PROXY;
import static com.android.launcher3.Flags.enableCursorHoverStates;
+import static com.android.launcher3.Flags.taskbarOverflow;
import static com.android.launcher3.Utilities.calculateTextHeight;
import static com.android.launcher3.Utilities.isRunningInTestHarness;
import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION;
@@ -37,6 +38,7 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_OPEN;
import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_DRAGGING;
import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_FULLSCREEN;
+import static com.android.launcher3.taskbar.TaskbarStashController.SHOULD_BUBBLES_FOLLOW_DEFAULT_VALUE;
import static com.android.launcher3.testing.shared.ResourceUtils.getBoolByName;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.util.AnimUtils.completeRunnableListCallback;
@@ -106,6 +108,7 @@
import com.android.launcher3.taskbar.allapps.TaskbarAllAppsController;
import com.android.launcher3.taskbar.bubbles.BubbleBarController;
import com.android.launcher3.taskbar.bubbles.BubbleBarPinController;
+import com.android.launcher3.taskbar.bubbles.BubbleBarSwipeController;
import com.android.launcher3.taskbar.bubbles.BubbleBarView;
import com.android.launcher3.taskbar.bubbles.BubbleBarViewController;
import com.android.launcher3.taskbar.bubbles.BubbleControllers;
@@ -129,11 +132,11 @@
import com.android.launcher3.touch.ItemClickHandler.ItemClickProxy;
import com.android.launcher3.util.ActivityOptionsWrapper;
import com.android.launcher3.util.ApiWrapper;
+import com.android.launcher3.util.ApplicationInfoWrapper;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.NavigationMode;
-import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.SettingsCache;
import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource;
@@ -277,9 +280,11 @@
BubbleBarController.onTaskbarRecreated();
if (BubbleBarController.isBubbleBarEnabled() && bubbleBarView != null) {
Optional<BubbleStashedHandleViewController> bubbleHandleController = Optional.empty();
+ Optional<BubbleBarSwipeController> bubbleBarSwipeController = Optional.empty();
if (isTransientTaskbar) {
bubbleHandleController = Optional.of(
new BubbleStashedHandleViewController(this, bubbleHandleView));
+ bubbleBarSwipeController = Optional.of(new BubbleBarSwipeController(this));
}
TaskbarHotseatDimensionsProvider dimensionsProvider =
new DeviceProfileDimensionsProviderAdapter(this);
@@ -297,6 +302,7 @@
() -> DisplayController.INSTANCE.get(this).getInfo().currentSize),
new BubblePinController(this, mDragLayer,
() -> DisplayController.INSTANCE.get(this).getInfo().currentSize),
+ bubbleBarSwipeController,
new BubbleCreator(this)
));
}
@@ -360,38 +366,6 @@
}
/**
- * Calculate the offsets needed to transform the transient taskbar bounds to the hotseat bounds.
- * @return The offsets will be stored in a Rect
- */
- public Rect calculateTaskbarToHotseatOffsets(Rect hotseatBounds) {
- Rect taskbar = getTransientTaskbarBounds();
- Rect offsets = new Rect();
-
- offsets.left = hotseatBounds.left - taskbar.left;
- offsets.right = hotseatBounds.right - taskbar.right;
-
- int heightDiff = hotseatBounds.height() - taskbar.height();
- offsets.top = (taskbar.height() - heightDiff) / 2;
-
- int gleanedTaskbarPadding = (mDeviceProfile.taskbarHeight
- - getTransientTaskbarBounds().height()) / 2;
- offsets.left -= gleanedTaskbarPadding;
- offsets.top -= gleanedTaskbarPadding;
- offsets.right += gleanedTaskbarPadding;
-
- // Bottom is relative to the bottom of layout, so we can calculate it with padding included.
- offsets.bottom = (hotseatBounds.height() - taskbar.height()) / 2;
-
- // Update bounds in taskbar background
- if (hotseatBounds.isEmpty()) {
- mDragLayer.getTaskbarToHotseatOffsetRect().setEmpty();
- } else {
- mDragLayer.getTaskbarToHotseatOffsetRect().set(offsets);
- }
- return offsets;
- }
-
- /**
* Copy the original DeviceProfile, match the number of hotseat icons and qsb width and update
* the icon size
*/
@@ -687,6 +661,11 @@
return mNavMode == NavigationMode.THREE_BUTTONS;
}
+ /** Returns whether taskbar should start align. */
+ public boolean shouldStartAlignTaskbar() {
+ return isThreeButtonNav() && mDeviceProfile.startAlignTaskbar;
+ }
+
public boolean isGestureNav() {
return mNavMode == NavigationMode.NO_BUTTON;
}
@@ -1215,6 +1194,11 @@
RecentsView recents = taskbarUIController.getRecentsView();
boolean shouldCloseAllOpenViews = true;
Object tag = view.getTag();
+
+ if (taskbarOverflow()) {
+ mControllers.keyboardQuickSwitchController.closeQuickSwitchView(false);
+ }
+
if (tag instanceof GroupTask groupTask) {
handleGroupTaskLaunch(
groupTask,
@@ -1253,7 +1237,8 @@
Intent intent = new Intent(info.getIntent())
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
- if (mIsSafeModeEnabled && !PackageManagerHelper.isSystemApp(this, intent)) {
+ if (mIsSafeModeEnabled
+ && !new ApplicationInfoWrapper(this, intent).isSystem()) {
Toast.makeText(this, R.string.safemode_shortcut_error,
Toast.LENGTH_SHORT).show();
} else if (info.isPromise()) {
@@ -1550,10 +1535,14 @@
/**
* Called when we want to unstash taskbar when user performs swipes up gesture.
+ * @param delayTaskbarBackground whether we will delay the taskbar background animation
*/
- public void onSwipeToUnstashTaskbar() {
+ public void onSwipeToUnstashTaskbar(boolean delayTaskbarBackground) {
+ mControllers.uiController.onSwipeToUnstashTaskbar();
+
boolean wasStashed = mControllers.taskbarStashController.isStashed();
- mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(/* stash= */ false);
+ mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(/* stash= */ false,
+ SHOULD_BUBBLES_FOLLOW_DEFAULT_VALUE, delayTaskbarBackground);
boolean isStashed = mControllers.taskbarStashController.isStashed();
if (isStashed != wasStashed) {
VibratorWrapper.INSTANCE.get(this).vibrateForTaskbarUnstash();
@@ -1687,7 +1676,7 @@
duration);
View allAppsButton = mControllers.taskbarViewController.getAllAppsButtonView();
- if (allAppsButton != null && !FeatureFlags.ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT.get()) {
+ if (allAppsButton != null && !FeatureFlags.enableAllAppsButtonInHotseat()) {
ValueAnimator alphaOverride = ValueAnimator.ofFloat(0, 1);
alphaOverride.setDuration(duration);
alphaOverride.addUpdateListener(a -> {
@@ -1701,18 +1690,6 @@
}
/**
- * Returns the bounds of launcher's hotseat (if exists).
- */
- public void getHotseatBounds(Rect hotseatBoundsOut) {
- TaskbarUIController uiController = mControllers.uiController;
- if (uiController instanceof LauncherTaskbarUIController launcherController) {
- launcherController.getHotseatBounds(hotseatBoundsOut);
- } else {
- hotseatBoundsOut.setEmpty();
- }
- }
-
- /**
* Called when we determine the touchable region.
*
* @param exclude {@code true} then the magnification region computation will omit the window.
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
index d6ce3a4..c0e921e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
@@ -21,7 +21,6 @@
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Path
-import android.graphics.Rect
import android.graphics.RectF
import com.android.app.animation.Interpolators
import com.android.internal.policy.ScreenDecorationsUtils
@@ -60,9 +59,6 @@
var translationYForSwipe = 0f
var translationYForStash = 0f
- // When not empty, we can use this to transform transient taskbar background to hotseat bounds.
- val taskbarToHotseatOffsetRect = Rect()
-
private val transientBackgroundBounds = context.transientTaskbarBounds
private val shadowAlpha: Float
@@ -230,12 +226,6 @@
val radius = newBackgroundHeight / 2f
val bottomMarginProgress = bottomMargin * ((1f - progress) / 2f)
- // Used to transform the background so that it wraps around the items on the hotseat.
- val hotseatOffsetLeft = taskbarToHotseatOffsetRect.left * progress
- val hotseatOffsetTop = taskbarToHotseatOffsetRect.top * progress
- val hotseatOffsetRight = taskbarToHotseatOffsetRect.right * progress
- val hotseatOffsetBottom = taskbarToHotseatOffsetRect.bottom * progress
-
// Aligns the bottom with the bottom of the stashed handle.
val bottom =
canvas.height - bottomMargin +
@@ -260,10 +250,10 @@
strokePaint.alpha = (paint.alpha * strokeAlpha) / 255
lastDrawnTransientRect.set(
- transientBackgroundBounds.left + halfWidthDelta + hotseatOffsetLeft,
- bottom - newBackgroundHeight + hotseatOffsetTop,
- transientBackgroundBounds.right - halfWidthDelta + hotseatOffsetRight,
- bottom + hotseatOffsetBottom,
+ transientBackgroundBounds.left + halfWidthDelta,
+ bottom - newBackgroundHeight,
+ transientBackgroundBounds.right - halfWidthDelta,
+ bottom
)
val horizontalInset = fullWidth * widthInsetPercentage
lastDrawnTransientRect.inset(horizontalInset, 0f)
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
index a090956..e16c76d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
@@ -22,7 +22,6 @@
import android.content.Context;
import android.graphics.Canvas;
-import android.graphics.Rect;
import android.graphics.RectF;
import android.media.permission.SafeCloseable;
import android.util.AttributeSet;
@@ -100,7 +99,7 @@
public TaskbarDragLayer(@NonNull Context context, @Nullable AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
super(context, attrs, 1 /* alphaChannelCount */);
- mBackgroundRenderer = new TaskbarBackgroundRenderer(mActivity);
+ mBackgroundRenderer = new TaskbarBackgroundRenderer(mContainer);
mTaskbarBackgroundAlpha = new MultiPropertyFactory<>(this, BG_ALPHA, INDEX_COUNT,
(a, b) -> a * b, 1f);
@@ -109,7 +108,7 @@
public void init(TaskbarDragLayerController.TaskbarDragLayerCallbacks callbacks) {
mControllerCallbacks = callbacks;
- mBackgroundRenderer.updateStashedHandleWidth(mActivity, getResources());
+ mBackgroundRenderer.updateStashedHandleWidth(mContainer, getResources());
recreateControllers();
}
@@ -260,11 +259,6 @@
return mBackgroundRenderer.getLastDrawnTransientRect();
}
- /** Returns the rect used to transform transient taskbar to the hotseat */
- public Rect getTaskbarToHotseatOffsetRect() {
- return mBackgroundRenderer.getTaskbarToHotseatOffsetRect();
- }
-
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
TestLogging.recordMotionEvent(TestProtocol.SEQUENCE_MAIN, "Touch event", ev);
@@ -275,7 +269,7 @@
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getAction() == ACTION_UP && event.getKeyCode() == KEYCODE_BACK) {
- AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
+ AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mContainer);
if (topView != null && topView.canHandleBack()) {
topView.onBackInvoked();
// Handled by the floating view.
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index 221504d..685c109 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -147,7 +147,11 @@
defaultTouchableRegion.addBoundsToRegion(bubbleBarViewController.bubbleBarBounds)
}
}
- if (taskbarStashController.isInApp || taskbarStashController.isInOverview) {
+ if (
+ taskbarStashController.isInApp ||
+ taskbarStashController.isInOverview ||
+ DisplayController.showLockedTaskbarOnHome(context)
+ ) {
// only add the taskbar touch region if not on home
val bottom = windowLayoutParams.height
val top = bottom - taskbarTouchableHeight
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 6c6bd71..876221b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -16,9 +16,13 @@
package com.android.launcher3.taskbar;
import static com.android.app.animation.Interpolators.EMPHASIZED;
+import static com.android.launcher3.Hotseat.ALPHA_CHANNEL_TASKBAR_ALIGNMENT;
+import static com.android.launcher3.Hotseat.ALPHA_CHANNEL_TASKBAR_STASH;
+import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_APP;
import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_OVERVIEW;
import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_STASHED_LAUNCHER_STATE;
+import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_STASHED_FOR_BUBBLES;
import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_HOME;
import static com.android.launcher3.util.FlagDebugUtils.appendFlag;
import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange;
@@ -40,6 +44,7 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Hotseat.HotseatQsbAlphaId;
import com.android.launcher3.LauncherState;
import com.android.launcher3.QuickstepTransitionManager;
import com.android.launcher3.Utilities;
@@ -48,6 +53,7 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
import com.android.quickstep.RecentsAnimationCallbacks;
import com.android.quickstep.RecentsAnimationController;
@@ -144,7 +150,7 @@
private AnimatedFloat mTaskbarBackgroundAlpha;
private AnimatedFloat mTaskbarAlpha;
private AnimatedFloat mTaskbarCornerRoundness;
- private MultiProperty mIconAlphaForHome;
+ private MultiProperty mTaskbarAlphaForHome;
private QuickstepLauncher mLauncher;
private boolean mIsDestroyed = false;
@@ -174,11 +180,11 @@
if (mIsQsbInline && !dp.isQsbInline) {
// We only modify QSB alpha if isQsbInline = true. If we switch to a DP
// where isQsbInline = false, then we need to reset the alpha.
- mLauncher.getHotseat().setQsbAlpha(1f);
+ mLauncher.getHotseat().setQsbAlpha(1f, ALPHA_CHANNEL_TASKBAR_ALIGNMENT);
}
mIsQsbInline = dp.isQsbInline;
TaskbarLauncherStateController.this.updateIconAlphaForHome(
- mIconAlphaForHome.getValue());
+ mTaskbarAlphaForHome.getValue(), ALPHA_CHANNEL_TASKBAR_ALIGNMENT);
}
};
@@ -241,7 +247,7 @@
.getTaskbarBackgroundAlpha();
mTaskbarAlpha = mControllers.taskbarDragLayerController.getTaskbarAlpha();
mTaskbarCornerRoundness = mControllers.getTaskbarCornerRoundness();
- mIconAlphaForHome = mControllers.taskbarViewController
+ mTaskbarAlphaForHome = mControllers.taskbarViewController
.getTaskbarIconAlpha().get(ALPHA_INDEX_HOME);
resetIconAlignment();
@@ -265,7 +271,7 @@
mIconAlignment.finishAnimation();
- mLauncher.getHotseat().setIconsAlpha(1f);
+ mLauncher.getHotseat().setIconsAlpha(1f, ALPHA_CHANNEL_TASKBAR_ALIGNMENT);
mLauncher.getStateManager().removeStateListener(mStateListener);
mCanSyncViews = !mControllers.taskbarActivityContext.isPhoneMode();
@@ -294,6 +300,7 @@
stashController.updateStateForFlag(FLAG_IN_APP, false);
updateStateForFlag(FLAG_TRANSITION_TO_VISIBLE, true);
+ mLauncherState = toState;
animatorSet.play(stashController.createApplyStateAnimator(duration));
animatorSet.play(applyState(duration, false));
@@ -440,11 +447,6 @@
return animator;
}
- /** Returns {@code true} if launcher is currently presenting the home screen. */
- public boolean isOnHome() {
- return isInLauncher() && mLauncherState == LauncherState.NORMAL;
- }
-
private Animator onStateChangeApplied(int changedFlags, long duration, boolean start) {
final boolean isInLauncher = isInLauncher();
final boolean isIconAlignedWithHotseat = isIconAlignedWithHotseat();
@@ -456,9 +458,11 @@
+ ", toAlignment: " + toAlignment);
}
mControllers.bubbleControllers.ifPresent(controllers -> {
- // Show the bubble bar when on launcher home or in overview.
+ // Show the bubble bar when on launcher home (hotseat icons visible) or in overview
boolean onOverview = mLauncherState == LauncherState.OVERVIEW;
- controllers.bubbleStashController.setBubblesShowingOnHome(isOnHome());
+ boolean hotseatIconsVisible = isInLauncher && mLauncherState.areElementsVisible(
+ mLauncher, HOTSEAT_ICONS);
+ controllers.bubbleStashController.setBubblesShowingOnHome(hotseatIconsVisible);
controllers.bubbleStashController.setBubblesShowingOnOverview(onOverview);
});
@@ -660,6 +664,9 @@
* This refers to the intended state - a transition to this state might be in progress.
*/
public boolean isTaskbarAlignedWithHotseat() {
+ if (DisplayController.showLockedTaskbarOnHome(mLauncher) && isInLauncher()) {
+ return false;
+ }
return mLauncherState.isTaskbarAlignedWithHotseat(mLauncher);
}
@@ -671,8 +678,7 @@
boolean isInStashedState = mLauncherState.isTaskbarStashed(mLauncher);
boolean willStashVisually = isInStashedState
&& mControllers.taskbarStashController.supportsVisualStashing();
- boolean isTaskbarAlignedWithHotseat =
- mLauncherState.isTaskbarAlignedWithHotseat(mLauncher);
+ boolean isTaskbarAlignedWithHotseat = isTaskbarAlignedWithHotseat();
return isTaskbarAlignedWithHotseat && !willStashVisually;
} else {
return false;
@@ -703,14 +709,17 @@
public void onAnimationEnd(Animator animation) {
if (isInStashedState && committed) {
// Reset hotseat alpha to default
- mLauncher.getHotseat().setIconsAlpha(1);
+ mLauncher.getHotseat().setIconsAlpha(1, ALPHA_CHANNEL_TASKBAR_ALIGNMENT);
}
}
@Override
public void onAnimationStart(Animator animation) {
- if (mLauncher.getHotseat().getIconsAlpha() > 0) {
- updateIconAlphaForHome(mLauncher.getHotseat().getIconsAlpha());
+ float hotseatIconsAlpha = mLauncher.getHotseat()
+ .getIconsAlpha(ALPHA_CHANNEL_TASKBAR_ALIGNMENT)
+ .getValue();
+ if (hotseatIconsAlpha > 0) {
+ updateIconAlphaForHome(hotseatIconsAlpha, ALPHA_CHANNEL_TASKBAR_ALIGNMENT);
}
}
});
@@ -739,6 +748,33 @@
}
}
+ protected void stashHotseat(boolean stash) {
+ TaskbarStashController stashController = mControllers.taskbarStashController;
+ stashController.updateStateForFlag(FLAG_STASHED_FOR_BUBBLES, stash);
+ Runnable swapHotseatWithTaskbar = new Runnable() {
+ @Override
+ public void run() {
+ updateIconAlphaForHome(stash ? 1 : 0, ALPHA_CHANNEL_TASKBAR_STASH);
+ }
+ };
+ if (stash) {
+ stashController.applyState();
+ // if we stashing the hotseat we need to immediately swap it with the animating taskbar
+ swapHotseatWithTaskbar.run();
+ } else {
+ // if we revert stashing make swap after taskbar animation is complete
+ stashController.applyState(/* postApplyAction = */ swapHotseatWithTaskbar);
+ }
+ }
+
+ protected void unStashHotseatInstantly() {
+ TaskbarStashController stashController = mControllers.taskbarStashController;
+ stashController.updateStateForFlag(FLAG_STASHED_FOR_BUBBLES, false);
+ stashController.applyState(/* duration = */ 0);
+ updateIconAlphaForHome(/* taskbarAlpha = */ 0,
+ ALPHA_CHANNEL_TASKBAR_STASH, /* updateTaskbarAlpha = */ false);
+ }
+
/**
* Resets and updates the icon alignment.
*/
@@ -748,7 +784,7 @@
}
private void onIconAlignmentRatioChanged() {
- float currentValue = mIconAlphaForHome.getValue();
+ float currentValue = mTaskbarAlphaForHome.getValue();
boolean taskbarWillBeVisible = mIconAlignment.value < 1;
boolean firstFrameVisChanged = (taskbarWillBeVisible && Float.compare(currentValue, 1) != 0)
|| (!taskbarWillBeVisible && Float.compare(currentValue, 0) != 0);
@@ -756,8 +792,10 @@
mControllers.taskbarViewController.setLauncherIconAlignment(
mIconAlignment.value, mLauncher.getDeviceProfile());
mControllers.navbarButtonsViewController.updateTaskbarAlignment(mIconAlignment.value);
- // Switch taskbar and hotseat in last frame
- updateIconAlphaForHome(taskbarWillBeVisible ? 1 : 0);
+ // Switch taskbar and hotseat in last frame and if taskbar is not hidden for bubbles
+ boolean isHiddenForBubbles = mControllers.taskbarStashController.isHiddenForBubbles();
+ updateIconAlphaForHome(taskbarWillBeVisible ? 1 : 0, ALPHA_CHANNEL_TASKBAR_ALIGNMENT,
+ /* updateTaskbarAlpha = */ !isHiddenForBubbles);
// Sync the first frame where we swap taskbar and hotseat.
if (firstFrameVisChanged && mCanSyncViews && !Utilities.isRunningInTestHarness()) {
@@ -767,12 +805,20 @@
}
}
- private void updateIconAlphaForHome(float alpha) {
+ private void updateIconAlphaForHome(float taskbarAlpha, @HotseatQsbAlphaId int alphaChannel) {
+ updateIconAlphaForHome(taskbarAlpha, alphaChannel, /* updateTaskbarAlpha = */ true);
+ }
+
+ private void updateIconAlphaForHome(float taskbarAlpha,
+ @HotseatQsbAlphaId int alphaChannel,
+ boolean updateTaskbarAlpha) {
if (mIsDestroyed) {
return;
}
- mIconAlphaForHome.setValue(alpha);
- boolean hotseatVisible = alpha == 0
+ if (updateTaskbarAlpha) {
+ mTaskbarAlphaForHome.setValue(taskbarAlpha);
+ }
+ boolean hotseatVisible = taskbarAlpha == 0
|| mControllers.taskbarActivityContext.isPhoneMode()
|| (!mControllers.uiController.isHotseatIconOnTopWhenAligned()
&& mIconAlignment.value > 0);
@@ -780,9 +826,10 @@
* Hide Launcher Hotseat icons when Taskbar icons have opacity. Both icon sets
* should not be visible at the same time.
*/
- mLauncher.getHotseat().setIconsAlpha(hotseatVisible ? 1 : 0);
+ float targetAlpha = hotseatVisible ? 1 : 0;
+ mLauncher.getHotseat().setIconsAlpha(targetAlpha, alphaChannel);
if (mIsQsbInline) {
- mLauncher.getHotseat().setQsbAlpha(hotseatVisible ? 1 : 0);
+ mLauncher.getHotseat().setQsbAlpha(targetAlpha, alphaChannel);
}
}
@@ -870,7 +917,7 @@
pw.println(String.format(
"%s\tmTaskbarBackgroundAlpha=%.2f", prefix, mTaskbarBackgroundAlpha.value));
pw.println(String.format(
- "%s\tmIconAlphaForHome=%.2f", prefix, mIconAlphaForHome.getValue()));
+ "%s\tmTaskbarAlphaForHome=%.2f", prefix, mTaskbarAlphaForHome.getValue()));
pw.println(String.format("%s\tmPrevState=%s", prefix,
mPrevState == null ? null : getStateString(mPrevState)));
pw.println(String.format("%s\tmState=%s", prefix, getStateString(mState)));
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
index 5024cd8..bdefea6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
@@ -250,6 +250,7 @@
Map<PackageUserKey, Integer> packageUserKeytoUidMap) {
Preconditions.assertUIThread();
mControllers.taskbarAllAppsController.setApps(apps, flags, packageUserKeytoUidMap);
+ mControllers.taskbarPopupController.setApps(apps);
}
protected void dumpLogs(String prefix, PrintWriter pw) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
index 2cee77d..70d4bb1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.taskbar;
+import static com.android.launcher3.model.data.AppInfo.COMPONENT_KEY_COMPARATOR;
import static com.android.launcher3.util.SplitConfigurationOptions.getLogEventForPosition;
import android.content.Intent;
@@ -29,11 +30,13 @@
import com.android.internal.logging.InstanceId;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.Flags;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.dot.FolderDotInfo;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -51,9 +54,11 @@
import com.android.launcher3.views.ActivityContext;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.LogUtils;
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
@@ -79,6 +84,7 @@
// Initialized in init.
private TaskbarControllers mControllers;
private boolean mAllowInitialSplitSelection;
+ private AppInfo[] mAppInfosList;
public TaskbarPopupController(TaskbarActivityContext context) {
mContext = context;
@@ -195,6 +201,10 @@
if (com.android.wm.shell.Flags.enableBubbleAnything()) {
shortcuts.add(BUBBLE);
}
+ if (Flags.enableMultiInstanceMenuTaskbar()
+ && DesktopModeStatus.canEnterDesktopMode(mContext)) {
+ shortcuts.addAll(getMultiInstanceMenuOptions().toList());
+ }
return shortcuts.stream();
}
@@ -258,7 +268,55 @@
originalView, position, mAllowInitialSplitSelection);
}
- /**
+ /**
+ * Set the list of AppInfos to be able to pull from later
+ */
+ public void setApps(AppInfo[] apps) {
+ mAppInfosList = apps;
+ }
+
+ /**
+ * Finds and returns an AppInfo object from a list, using its ComponentKey for identification.
+ * Based off of {@link com.android.launcher3.allapps.AllAppsStore#getApp(ComponentKey)}
+ * since we cannot access AllAppsStore from here.
+ */
+ public AppInfo getApp(ComponentKey key) {
+ if (key == null) {
+ return null;
+ }
+ AppInfo tempInfo = new AppInfo();
+ tempInfo.componentName = key.componentName;
+ tempInfo.user = key.user;
+ int index = Arrays.binarySearch(mAppInfosList, tempInfo, COMPONENT_KEY_COMPARATOR);
+ return index < 0 ? null : mAppInfosList[index];
+ }
+
+ /**
+ * Returns a stream of Multi Instance menu options if an app supports it.
+ */
+ Stream<SystemShortcut.Factory<BaseTaskbarContext>> getMultiInstanceMenuOptions() {
+ SystemShortcut.Factory<BaseTaskbarContext> factory = createNewWindowShortcutFactory();
+ return factory != null ? Stream.of(factory) : Stream.empty();
+
+ }
+
+ /**
+ * Creates a factory function representing a "New Window" menu item only if the calling app
+ * supports multi-instance.
+ * @return A factory function to be used in populating the long-press menu.
+ */
+ SystemShortcut.Factory<BaseTaskbarContext> createNewWindowShortcutFactory() {
+ return (context, itemInfo, originalView) -> {
+ ComponentKey key = itemInfo.getComponentKey();
+ AppInfo app = getApp(key);
+ if (app != null && app.supportsMultiInstance()) {
+ return new NewWindowTaskbarShortcut<>(context, itemInfo, originalView);
+ }
+ return null;
+ };
+ }
+
+ /**
* A single menu item ("Split left," "Split right," or "Split top") that executes a split
* from the taskbar, as if the user performed a drag and drop split.
* Includes an onClick method that initiates the actual split.
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt
index 72bdafe..57d4dbb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt
@@ -16,6 +16,7 @@
package com.android.launcher3.taskbar
import android.content.Context
+import android.window.flags.DesktopModeFlags
import androidx.annotation.VisibleForTesting
import com.android.launcher3.Flags.enableRecentsInTaskbar
import com.android.launcher3.model.data.ItemInfo
@@ -26,7 +27,6 @@
import com.android.quickstep.RecentsModel
import com.android.quickstep.util.DesktopTask
import com.android.quickstep.util.GroupTask
-import com.android.wm.shell.shared.desktopmode.DesktopModeFlags
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import java.io.PrintWriter
@@ -40,7 +40,7 @@
var canShowRunningApps =
DesktopModeStatus.canEnterDesktopMode(context) &&
- DesktopModeFlags.TASKBAR_RUNNING_APPS.isEnabled(context)
+ DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TASKBAR_RUNNING_APPS.isTrue
@VisibleForTesting
set(isEnabledFromTest) {
field = isEnabledFromTest
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
index 2370dfd..bf086b4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
@@ -27,6 +27,8 @@
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
+import androidx.annotation.VisibleForTesting;
+
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.taskbar.bubbles.BubbleControllers;
import com.android.launcher3.util.DisplayController;
@@ -77,7 +79,7 @@
public void onTaskbarVisibilityChanged(int visibility) {
mTaskbarVisible = visibility == VISIBLE;
if (shouldShowScrim()) {
- showScrim(true, getScrimAlpha(), false /* skipAnim */);
+ showScrim(true, computeScrimAlpha(), false /* skipAnim */);
} else if (mScrimView.getScrimAlpha() > 0f) {
showScrim(false, 0, false /* skipAnim */);
}
@@ -96,7 +98,7 @@
return;
}
mSysUiStateFlags = stateFlags;
- showScrim(shouldShowScrim(), getScrimAlpha(), skipAnim);
+ showScrim(shouldShowScrim(), computeScrimAlpha(), skipAnim);
}
private boolean shouldShowScrim() {
@@ -115,10 +117,11 @@
return bubblesExpanded && !mControllers.navbarButtonsViewController.isImeVisible()
&& !isShadeVisible
&& !mControllers.taskbarStashController.isStashed()
- && (mTaskbarVisible || showScrimForBubbles);
+ && (mTaskbarVisible || showScrimForBubbles)
+ && !mControllers.taskbarStashController.isHiddenForBubbles();
}
- private float getScrimAlpha() {
+ private float computeScrimAlpha() {
final boolean isPersistentTaskBarVisible =
mTaskbarVisible && !DisplayController.isTransientTaskbar(mScrimView.getContext());
final boolean manageMenuExpanded =
@@ -139,7 +142,7 @@
mScrimView.setOnClickListener(showScrim ? (view) -> onClick() : null);
mScrimView.setClickable(showScrim);
if (skipAnim) {
- mScrimView.setScrimAlpha(alpha);
+ mScrimAlpha.updateValue(alpha);
} else {
ObjectAnimator anim = mScrimAlpha.animateToValue(showScrim ? alpha : 0);
anim.setInterpolator(showScrim ? SCRIM_ALPHA_IN : SCRIM_ALPHA_OUT);
@@ -166,4 +169,14 @@
pw.println(prefix + "\tmScrimAlpha.value=" + mScrimAlpha.value);
}
+
+ @VisibleForTesting
+ TaskbarScrimView getScrimView() {
+ return mScrimView;
+ }
+
+ @VisibleForTesting
+ float getScrimAlpha() {
+ return mScrimAlpha.value;
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index c1ed39a..e39e904 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -42,7 +42,6 @@
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.app.RemoteAction;
-import android.graphics.Rect;
import android.graphics.drawable.Icon;
import android.os.SystemClock;
import android.util.Log;
@@ -62,6 +61,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatedFloat;
+import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
@@ -82,6 +82,13 @@
private static final String TAG = "TaskbarStashController";
private static final boolean DEBUG = false;
+ private static boolean sEnableSoftwareImeForTests = false;
+
+ /**
+ * Def. value for @param shouldBubblesFollow in
+ * {@link #updateAndAnimateTransientTaskbar(boolean)} */
+ public static boolean SHOULD_BUBBLES_FOLLOW_DEFAULT_VALUE = true;
+
public static final int FLAG_IN_APP = 1 << 0;
public static final int FLAG_STASHED_IN_APP_SYSUI = 1 << 1; // shade open, ...
public static final int FLAG_STASHED_IN_APP_SETUP = 1 << 2; // setup wizard and AllSetActivity
@@ -94,6 +101,9 @@
public static final int FLAG_STASHED_SYSUI = 1 << 9; // app pinning,...
public static final int FLAG_STASHED_DEVICE_LOCKED = 1 << 10; // device is locked: keyguard, ...
public static final int FLAG_IN_OVERVIEW = 1 << 11; // launcher is in overview
+ // An internal no-op flag to determine whether we should delay the taskbar background animation
+ private static final int FLAG_DELAY_TASKBAR_BG_TAG = 1 << 12;
+ public static final int FLAG_STASHED_FOR_BUBBLES = 1 << 13; // show handle for stashed hotseat
// If any of these flags are enabled, isInApp should return true.
private static final int FLAGS_IN_APP = FLAG_IN_APP | FLAG_IN_SETUP;
@@ -115,26 +125,30 @@
// If any of these flags are enabled, the taskbar must be stashed.
private static final int FLAGS_FORCE_STASHED = FLAG_STASHED_SYSUI | FLAG_STASHED_DEVICE_LOCKED
- | FLAG_STASHED_IN_TASKBAR_ALL_APPS | FLAG_STASHED_SMALL_SCREEN;
+ | FLAG_STASHED_IN_TASKBAR_ALL_APPS | FLAG_STASHED_SMALL_SCREEN
+ | FLAG_STASHED_FOR_BUBBLES;
/**
* How long to stash/unstash when manually invoked via long press.
*
* Use {@link #getStashDuration()} to query duration
*/
- private static final long TASKBAR_STASH_DURATION = InsetsController.ANIMATION_DURATION_RESIZE;
+ @VisibleForTesting
+ static final long TASKBAR_STASH_DURATION = InsetsController.ANIMATION_DURATION_RESIZE;
/**
* How long to stash/unstash transient taskbar.
*
* Use {@link #getStashDuration()} to query duration.
*/
- private static final long TRANSIENT_TASKBAR_STASH_DURATION = 417;
+ @VisibleForTesting
+ static final long TRANSIENT_TASKBAR_STASH_DURATION = 417;
/**
* How long to stash/unstash when keyboard is appearing/disappearing.
*/
- private static final long TASKBAR_STASH_DURATION_FOR_IME = 80;
+ @VisibleForTesting
+ static final long TASKBAR_STASH_DURATION_FOR_IME = 80;
/**
* The scale TaskbarView animates to when being stashed.
@@ -155,7 +169,7 @@
/**
* How long the icon/stash handle alpha animation plays.
*/
- public static final long TASKBAR_STASH_ALPHA_DURATION = 50;
+ public static final long TRANSIENT_TASKBAR_STASH_ALPHA_DURATION = 50;
/**
* How long to delay the icon/stash handle alpha for the home to app taskbar animation.
@@ -200,7 +214,6 @@
* by not scaling the height of the taskbar background.
*/
private static final int TRANSITION_UNSTASH_SUW_MANUAL = 3;
- private static final Rect EMPTY_RECT = new Rect();
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
@@ -245,7 +258,7 @@
private boolean mEnableBlockingTimeoutDuringTests = false;
private Animator mTaskbarBackgroundAlphaAnimator;
- private long mTaskbarBackgroundDuration;
+ private final long mTaskbarBackgroundDuration;
private boolean mUserIsNotGoingHome = false;
// Evaluate whether the handle should be stashed
@@ -388,6 +401,16 @@
return mIsStashed;
}
+ /** Sets the hotseat stashed. */
+ public void stashHotseat(boolean stash) {
+ mControllers.uiController.stashHotseat(stash);
+ }
+
+ /** Instantly un-stashes the hotseat. */
+ public void unStashHotseatInstantly() {
+ mControllers.uiController.unStashHotseatInstantly();
+ }
+
/**
* Returns whether the taskbar should be stashed in apps (e.g. user long pressed to stash).
*/
@@ -427,6 +450,11 @@
return hasAnyFlag(FLAG_IN_OVERVIEW);
}
+ /** Returns whether taskbar is hidden for bubbles. */
+ public boolean isHiddenForBubbles() {
+ return hasAnyFlag(FLAG_STASHED_FOR_BUBBLES);
+ }
+
/**
* Returns the height that taskbar will be touchable.
*/
@@ -489,9 +517,17 @@
/**
* Stash or unstashes the transient taskbar, using the default TASKBAR_STASH_DURATION.
* If bubble bar exists, it will match taskbars stashing behavior.
+ * Will not delay taskbar background by default.
*/
public void updateAndAnimateTransientTaskbar(boolean stash) {
- updateAndAnimateTransientTaskbar(stash, /* shouldBubblesFollow= */ true);
+ updateAndAnimateTransientTaskbar(stash, SHOULD_BUBBLES_FOLLOW_DEFAULT_VALUE, false);
+ }
+
+ /**
+ * Stash or unstashes the transient taskbar, using the default TASKBAR_STASH_DURATION.
+ */
+ public void updateAndAnimateTransientTaskbar(boolean stash, boolean shouldBubblesFollow) {
+ updateAndAnimateTransientTaskbar(stash, shouldBubblesFollow, false);
}
/**
@@ -499,28 +535,47 @@
*
* @param stash whether transient taskbar should be stashed.
* @param shouldBubblesFollow whether bubbles should match taskbars behavior.
+ * @param delayTaskbarBackground whether we will delay the taskbar background animation
*/
- public void updateAndAnimateTransientTaskbar(boolean stash, boolean shouldBubblesFollow) {
+ public void updateAndAnimateTransientTaskbar(boolean stash, boolean shouldBubblesFollow,
+ boolean delayTaskbarBackground) {
if (!DisplayController.isTransientTaskbar(mActivity)) {
return;
}
- if (
- stash
- && !mControllers.taskbarAutohideSuspendController
- .isSuspendedForTransientTaskbarInLauncher()
- && mControllers.taskbarAutohideSuspendController
- .isTransientTaskbarStashingSuspended()) {
+ if (stash
+ && !mControllers.taskbarAutohideSuspendController
+ .isSuspendedForTransientTaskbarInLauncher()
+ && mControllers.taskbarAutohideSuspendController
+ .isTransientTaskbarStashingSuspended()) {
// Avoid stashing if autohide is currently suspended.
return;
}
+ boolean shouldApplyState = false;
+
+ if (delayTaskbarBackground) {
+ mControllers.taskbarStashController.updateStateForFlag(FLAG_DELAY_TASKBAR_BG_TAG, true);
+ shouldApplyState = true;
+ }
+
if (hasAnyFlag(FLAG_STASHED_IN_APP_AUTO) != stash) {
mTaskbarSharedState.taskbarWasStashedAuto = stash;
updateStateForFlag(FLAG_STASHED_IN_APP_AUTO, stash);
+ shouldApplyState = true;
+ }
+
+ if (shouldApplyState) {
applyState();
}
+ // Effectively a no-opp to remove the tag.
+ if (delayTaskbarBackground) {
+ mControllers.taskbarStashController.updateStateForFlag(FLAG_DELAY_TASKBAR_BG_TAG,
+ false);
+ mControllers.taskbarStashController.applyState(0);
+ }
+
mControllers.bubbleControllers.ifPresent(controllers -> {
if (shouldBubblesFollow) {
final boolean willStash = mIsStashedPredicate.test(mState);
@@ -584,14 +639,14 @@
/**
* Create a stash animation and save to {@link #mAnimator}.
*
- * @param isStashed whether it's a stash animation or an unstash animation
- * @param duration duration of the animation
- * @param animationType what transition type to play.
- * @param skipTaskbarBackgroundDelay Iff true, skips delaying the taskbar background.
- * @param jankTag tag to be used in jank monitor trace.
+ * @param isStashed whether it's a stash animation or an unstash animation
+ * @param duration duration of the animation
+ * @param animationType what transition type to play.
+ * @param shouldDelayBackground whether we should delay the taskbar bg animation
+ * @param jankTag tag to be used in jank monitor trace.
*/
private void createAnimToIsStashed(boolean isStashed, long duration,
- @StashAnimation int animationType, boolean skipTaskbarBackgroundDelay, String jankTag) {
+ @StashAnimation int animationType, boolean shouldDelayBackground, String jankTag) {
if (animationType == TRANSITION_UNSTASH_SUW_MANUAL && isStashed) {
// The STASH_ANIMATION_SUW_MANUAL must only be used during an unstash animation.
Log.e(TAG, "Illegal arguments:Using TRANSITION_UNSTASH_SUW_MANUAL to stash taskbar");
@@ -630,7 +685,7 @@
if (isTransientTaskbar) {
createTransientAnimToIsStashed(mAnimator, isStashed, duration,
- skipTaskbarBackgroundDelay, animationType);
+ shouldDelayBackground, animationType);
} else {
createAnimToIsStashed(mAnimator, isStashed, duration, stashTranslation, animationType);
}
@@ -720,7 +775,7 @@
}
fullLengthAnimatorSet.play(mControllers.stashedHandleViewController
- .createRevealAnimToIsStashed(isStashed, EMPTY_RECT));
+ .createRevealAnimToIsStashed(isStashed));
// Return the stashed handle to its default scale in case it was changed as part of the
// feedforward hint. Note that the reveal animation above also visually scales it.
fullLengthAnimatorSet.play(mTaskbarStashedHandleHintScale.animateToValue(1f));
@@ -736,7 +791,7 @@
}
private void createTransientAnimToIsStashed(AnimatorSet as, boolean isStashed, long duration,
- boolean skipTaskbarBackgroundDelay, @StashAnimation int animationType) {
+ boolean shouldDelayBackground, @StashAnimation int animationType) {
// Target values of the properties this is going to set
final float backgroundOffsetTarget = isStashed ? 1 : 0;
final float iconAlphaTarget = isStashed ? 0 : 1;
@@ -750,14 +805,14 @@
if (animationType == TRANSITION_HANDLE_FADE) {
// When fading, the handle fades in/out at the beginning of the transition with
// TASKBAR_STASH_ALPHA_DURATION.
- backgroundAndHandleAlphaDuration = TASKBAR_STASH_ALPHA_DURATION;
+ backgroundAndHandleAlphaDuration = TRANSIENT_TASKBAR_STASH_ALPHA_DURATION;
// The iconAlphaDuration must be set to duration for the skippable interpolators
// below to work.
iconAlphaDuration = duration;
} else {
iconAlphaStartDelay = TASKBAR_STASH_ALPHA_START_DELAY;
- iconAlphaDuration = TASKBAR_STASH_ALPHA_DURATION;
- backgroundAndHandleAlphaDuration = TASKBAR_STASH_ALPHA_DURATION;
+ iconAlphaDuration = TRANSIENT_TASKBAR_STASH_ALPHA_DURATION;
+ backgroundAndHandleAlphaDuration = TRANSIENT_TASKBAR_STASH_ALPHA_DURATION;
if (isStashed) {
if (animationType == TRANSITION_HOME_TO_APP) {
@@ -770,24 +825,14 @@
}
}
-
- Rect taskbarToHotseatOffsets = new Rect();
- if (enableScalingRevealHomeAnimation() && animationType == TRANSITION_HOME_TO_APP) {
- Rect hotseatRect = new Rect();
- mActivity.getHotseatBounds(hotseatRect);
-
- // Calculate and store offsets so that we can sync with the taskbar stashed handle
- taskbarToHotseatOffsets.set(
- mActivity.calculateTaskbarToHotseatOffsets(hotseatRect));
- as.addListener(AnimatorListeners.forEndCallback(
- () -> mActivity.calculateTaskbarToHotseatOffsets(EMPTY_RECT)));
- }
-
play(as, mTaskbarStashedHandleAlpha.animateToValue(stashedHandleAlphaTarget),
backgroundAndHandleAlphaStartDelay,
backgroundAndHandleAlphaDuration, LINEAR);
- if (enableScalingRevealHomeAnimation() && isStashed && !skipTaskbarBackgroundDelay) {
+
+ if (enableScalingRevealHomeAnimation()
+ && !isStashed
+ && shouldDelayBackground) {
play(as, getTaskbarBackgroundAnimatorWhenNotGoingHome(duration),
0, 0, LINEAR);
as.addListener(AnimatorListeners.forEndCallback(
@@ -828,12 +873,10 @@
}
mControllers.taskbarViewController.addRevealAnimToIsStashed(skippable, isStashed, duration,
- EMPHASIZED, animationType == TRANSITION_UNSTASH_SUW_MANUAL,
- animationType == TRANSITION_HOME_TO_APP);
+ EMPHASIZED, animationType == TRANSITION_UNSTASH_SUW_MANUAL);
play(skippable, mControllers.stashedHandleViewController
- .createRevealAnimToIsStashed(isStashed, taskbarToHotseatOffsets), 0, duration,
- EMPHASIZED);
+ .createRevealAnimToIsStashed(isStashed), 0, duration, EMPHASIZED);
// Return the stashed handle to its default scale in case it was changed as part of the
// feedforward hint. Note that the reveal animation above also visually scales it.
@@ -914,7 +957,7 @@
}
int action = expanding ? InteractionJankMonitor.CUJ_TASKBAR_EXPAND :
InteractionJankMonitor.CUJ_TASKBAR_COLLAPSE;
- animator.addListener(new AnimatorListenerAdapter() {
+ animator.addListener(new AnimationSuccessListener() {
@Override
public void onAnimationStart(@NonNull Animator animation) {
final Configuration.Builder builder =
@@ -926,9 +969,16 @@
}
@Override
- public void onAnimationEnd(@NonNull Animator animation) {
+ public void onAnimationSuccess(@NonNull Animator animator) {
InteractionJankMonitor.getInstance().end(action);
}
+
+ @Override
+ public void onAnimationCancel(@NonNull Animator animation) {
+ super.onAnimationCancel(animation);
+
+ InteractionJankMonitor.getInstance().cancel(action);
+ }
});
}
@@ -958,13 +1008,29 @@
}
public void applyState() {
- applyState(hasAnyFlag(FLAG_IN_SETUP) ? 0 : TASKBAR_STASH_DURATION);
+ applyState(/* postApplyAction = */ null);
+ }
+
+ /** Applies state and performs action after state is applied. */
+ public void applyState(@Nullable Runnable postApplyAction) {
+ applyState(hasAnyFlag(FLAG_IN_SETUP) ? 0 : TASKBAR_STASH_DURATION, postApplyAction);
}
public void applyState(long duration) {
+ applyState(duration, /* postApplyAction = */ null);
+ }
+
+ private void applyState(long duration, @Nullable Runnable postApplyAction) {
Animator animator = createApplyStateAnimator(duration);
if (animator != null) {
+ if (postApplyAction != null) {
+ // performs action on animation end
+ animator.addListener(AnimatorListeners.forEndCallback(postApplyAction));
+ }
animator.start();
+ } else if (postApplyAction != null) {
+ // animator was not created, just execute the action
+ postApplyAction.run();
}
}
@@ -982,6 +1048,9 @@
*/
@Nullable
public Animator createApplyStateAnimator(long duration) {
+ if (mActivity.isPhoneMode()) {
+ return null;
+ }
return mStatePropertyHolder.createSetStateAnimator(mState, duration);
}
@@ -1014,7 +1083,8 @@
/**
* When hiding the IME, delay the unstash animation to align with the end of the transition.
*/
- private long getTaskbarStashStartDelayForIme() {
+ @VisibleForTesting
+ long getTaskbarStashStartDelayForIme() {
if (mIsImeShowing) {
// Only delay when IME is exiting, not entering.
return 0;
@@ -1027,10 +1097,6 @@
/** Called when some system ui state has changed. (See SYSUI_STATE_... in QuickstepContract) */
public void updateStateForSysuiFlags(long systemUiStateFlags, boolean skipAnim) {
- if (mActivity.isPhoneMode()) {
- return;
- }
-
long animDuration = TASKBAR_STASH_DURATION;
long startDelay = 0;
@@ -1074,13 +1140,13 @@
}
// Do not stash if pinned taskbar, hardware keyboard is attached and no IME is docked
- if (mActivity.isHardwareKeyboard() && DisplayController.isPinnedTaskbar(mActivity)
+ if (isHardwareKeyboard() && DisplayController.isPinnedTaskbar(mActivity)
&& !mActivity.isImeDocked()) {
return false;
}
// Do not stash if hardware keyboard is attached, in 3 button nav and desktop windowing mode
- if (mActivity.isHardwareKeyboard()
+ if (isHardwareKeyboard()
&& mActivity.isThreeButtonNav()
&& mControllers.taskbarDesktopModeController.getAreDesktopTasksVisible()) {
return false;
@@ -1094,6 +1160,21 @@
return mIsImeShowing || mIsImeSwitcherShowing;
}
+ private boolean isHardwareKeyboard() {
+ return mActivity.isHardwareKeyboard() && !sEnableSoftwareImeForTests;
+ }
+
+ /**
+ * Overrides {@link #isHardwareKeyboard()} to {@code false} for testing, if enabled.
+ * <p>
+ * Virtual devices are sometimes in hardware keyboard mode, leading to an inconsistent
+ * testing environment.
+ */
+ @VisibleForTesting
+ static void enableSoftwareImeForTests(boolean enable) {
+ sEnableSoftwareImeForTests = enable;
+ }
+
/**
* Updates the proper flag to indicate whether the task bar should be stashed.
*
@@ -1219,7 +1300,7 @@
/**
* Attempts to start timer to auto hide the taskbar based on time.
*/
- public void tryStartTaskbarTimeout() {
+ private void tryStartTaskbarTimeout() {
if (!DisplayController.isTransientTaskbar(mActivity)
|| mIsStashed
|| mEnableBlockingTimeoutDuringTests) {
@@ -1247,6 +1328,11 @@
updateAndAnimateTransientTaskbarForTimeout();
}
+ @VisibleForTesting
+ Alarm getTimeoutAlarm() {
+ return mTimeoutAlarm;
+ }
+
@Override
public void dumpLogs(String prefix, PrintWriter pw) {
pw.println(prefix + "TaskbarStashController:");
@@ -1349,10 +1435,9 @@
mIsStashed = isStashed;
mLastStartedTransitionType = animationType;
- boolean skipTaskbarBgDelay = !hasAnyFlag(FLAG_STASHED_IN_TASKBAR_ALL_APPS)
- && hasAnyFlag(FLAG_STASHED_IN_TASKBAR_ALL_APPS, changedFlags);
+ boolean shouldDelayBackground = hasAnyFlag(FLAG_DELAY_TASKBAR_BG_TAG);
// This sets mAnimator.
- createAnimToIsStashed(mIsStashed, duration, animationType, skipTaskbarBgDelay,
+ createAnimToIsStashed(mIsStashed, duration, animationType, shouldDelayBackground,
computeTaskbarJankMonitorTag(changedFlags));
return mAnimator;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarThresholdUtils.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarThresholdUtils.java
index 5b6fbef..17516f3 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarThresholdUtils.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarThresholdUtils.java
@@ -25,7 +25,6 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
-import com.android.launcher3.config.FeatureFlags;
/**
* Utility class that contains the different taskbar thresholds logic.
@@ -39,10 +38,6 @@
private static int getThreshold(Resources r, DeviceProfile dp, int thresholdDimen,
int multiplierDimen) {
- if (!FeatureFlags.ENABLE_DYNAMIC_TASKBAR_THRESHOLDS.get()) {
- return r.getDimensionPixelSize(thresholdDimen);
- }
-
float landscapeScreenHeight = dp.isLandscape ? dp.heightPx : dp.widthPx;
float screenPart = (landscapeScreenHeight * SCREEN_UNITS);
float defaultDp = dpiFromPx(screenPart, DisplayMetrics.DENSITY_DEVICE_STABLE);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index 6b1173a..9c8c2a9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -428,4 +428,18 @@
public void setSkipLauncherVisibilityChange(boolean skip) {
mSkipLauncherVisibilityChange = skip;
}
+
+ /** Sets whether the hotseat is stashed */
+ public void stashHotseat(boolean stash) {
+ }
+
+ /** Un-stash the hotseat instantly */
+ public void unStashHotseatInstantly() {
+ }
+
+ /**
+ * Called when we want to unstash taskbar when user performs swipes up gesture.
+ */
+ public void onSwipeToUnstashTaskbar() {
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index 32d6561..8763509 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -46,6 +46,7 @@
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Flags;
import com.android.launcher3.Insettable;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -63,6 +64,7 @@
import com.android.launcher3.util.LauncherBindableItemsContainer;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.views.IconButtonView;
import com.android.quickstep.util.DesktopTask;
import com.android.quickstep.util.GroupTask;
import com.android.systemui.shared.recents.model.Task;
@@ -87,6 +89,7 @@
private final boolean mIsRtl;
private final TaskbarActivityContext mActivityContext;
+ @Nullable private BubbleBarLocation mBubbleBarLocation = null;
// Initialized in init.
private TaskbarViewCallbacks mControllerCallbacks;
@@ -99,9 +102,12 @@
// Only non-null when device supports having an All Apps button.
@Nullable private final TaskbarAllAppsButtonContainer mAllAppsButtonContainer;
- // Only non-null when device supports having an All Apps button.
+ // Only non-null when device supports having a Divider button.
@Nullable private TaskbarDividerContainer mTaskbarDividerContainer;
+ // Only non-null when device supports having a Taskbar Overflow button.
+ @Nullable private IconButtonView mTaskbarOverflowView;
+
/**
* Whether the divider is between Hotseat icons and Recents,
* instead of between All Apps button and Hotseat.
@@ -114,6 +120,8 @@
private boolean mShouldTryStartAlign;
+ private final int mMaxNumIcons;
+
public TaskbarView(@NonNull Context context) {
this(context, null);
}
@@ -170,8 +178,27 @@
mTaskbarDividerContainer = new TaskbarDividerContainer(context);
}
+ if (Flags.taskbarOverflow()) {
+ mTaskbarOverflowView = (IconButtonView) LayoutInflater.from(context)
+ .inflate(R.layout.taskbar_overflow_button, this, false);
+ mTaskbarOverflowView.setIconDrawable(
+ resources.getDrawable(R.drawable.taskbar_overflow_icon));
+ mTaskbarOverflowView.setPadding(mItemPadding, mItemPadding, mItemPadding, mItemPadding);
+ }
// TODO: Disable touch events on QSB otherwise it can crash.
mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false);
+
+ mMaxNumIcons = calculateMaxNumIcons();
+ }
+
+ /**
+ // @return the maximum number of 'icons' that can fit in the taskbar.
+ // TODO(368119679): Assumes that they are all the same size.
+ */
+ private int calculateMaxNumIcons() {
+ int availableWidth = mActivityContext.getDeviceProfile().widthPx
+ - (mActivityContext.getDeviceProfile().edgeMarginPx * 2);
+ return Math.floorDiv(availableWidth, mIconTouchSize);
}
@Override
@@ -197,7 +224,7 @@
@Override
public void onDeviceProfileChanged(DeviceProfile dp) {
- mShouldTryStartAlign = mActivityContext.isThreeButtonNav() && dp.startAlignTaskbar;
+ mShouldTryStartAlign = mActivityContext.shouldStartAlignTaskbar();
}
@Override
@@ -262,6 +289,12 @@
if (mTaskbarDividerContainer != null && callbacks.supportsDividerLongPress()) {
mTaskbarDividerContainer.setUpCallbacks(callbacks);
}
+ if (mTaskbarOverflowView != null) {
+ mTaskbarOverflowView.setOnClickListener(
+ mControllerCallbacks.getOverflowOnClickListener());
+ mTaskbarOverflowView.setOnLongClickListener(
+ mControllerCallbacks.getOverflowOnLongClickListener());
+ }
}
private void removeAndRecycle(View view) {
@@ -289,6 +322,9 @@
removeView(mTaskbarDividerContainer);
}
}
+ if (mTaskbarOverflowView != null) {
+ removeView(mTaskbarOverflowView);
+ }
removeView(mQsb);
// Add Hotseat icons.
@@ -376,6 +412,9 @@
if (mTaskbarDividerContainer != null && !recentTasks.isEmpty()) {
addView(mTaskbarDividerContainer, nextViewIndex++);
mAddedDividerForRecents = true;
+ if (mTaskbarOverflowView != null) {
+ addView(mTaskbarOverflowView, nextViewIndex++);
+ }
}
// Add Recent/Running icons.
@@ -448,6 +487,9 @@
addView(mTaskbarDividerContainer, mIsRtl ? (getChildCount() - 1) : 1);
}
}
+
+ updateRecentAppsToFit();
+
if (mActivityContext.getDeviceProfile().isQsbInline) {
addView(mQsb, mIsRtl ? getChildCount() : 0);
// Always set QSB to invisible after re-adding.
@@ -455,6 +497,45 @@
}
}
+ /**
+ * Updates the recent apps portion of the taskbar by:
+ * - Removing overflow affordance if overflow is not needed.
+ * - Removing any recent apps that do not fit.
+ */
+ private void updateRecentAppsToFit() {
+ if (!Flags.taskbarOverflow()) {
+ return;
+ }
+ int indexOfFirstRecentApp = -1;
+ int size = getChildCount();
+ boolean removeOverflowView = true;
+
+ for (int i = 0; i < size; ++i) {
+ if (getChildAt(i).getTag() instanceof GroupTask) {
+ indexOfFirstRecentApp = i;
+ removeOverflowView = false;
+ break;
+ }
+ }
+
+ if (indexOfFirstRecentApp != -1) {
+ // We pre-maturely added the overflow icon, so we can take it out of the count.
+ int numRecentAppsToRemove = Math.max(0, getChildCount() - mMaxNumIcons + 1);
+ if (numRecentAppsToRemove <= 1) {
+ // We can fit all of the recent apps if we remove the overflow icon.
+ removeOverflowView = true;
+ } else {
+ for (int i = 0; i < numRecentAppsToRemove; ++i) {
+ removeAndRecycle(getChildAt(indexOfFirstRecentApp));
+ }
+ }
+ }
+
+ if (removeOverflowView) {
+ removeView(mTaskbarOverflowView);
+ }
+ }
+
/** Binds the GroupTask to the BubbleTextView to be ready to present to the user. */
public void applyGroupTaskToBubbleTextView(BubbleTextView btv, GroupTask groupTask) {
// TODO(b/343289567): support app pairs.
@@ -494,39 +575,60 @@
icon.setOnHoverListener(mControllerCallbacks.getIconOnHoverListener(icon));
}
+ /** Updates taskbar icons accordingly to the new bubble bar location. */
+ public void onBubbleBarLocationUpdated(BubbleBarLocation location) {
+ if (mBubbleBarLocation == location) return;
+ mBubbleBarLocation = location;
+ requestLayout();
+ }
+
+ /**
+ * Returns translation X for the taskbar icons for provided {@link BubbleBarLocation}. If the
+ * bubble bar is not enabled, or location of the bubble bar is the same, or taskbar is not start
+ * aligned - returns 0.
+ */
+ public float getTranslationXForBubbleBarPosition(BubbleBarLocation location) {
+ if (!mControllerCallbacks.isBubbleBarEnabledInPersistentTaskbar()
+ || location == mBubbleBarLocation
+ || !mActivityContext.shouldStartAlignTaskbar()
+ ) {
+ return 0;
+ }
+ Rect iconsBounds = getIconLayoutBounds();
+ return getTaskBarIconsEndForBubbleBarLocation(location) - iconsBounds.right;
+ }
+
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- int count = getChildCount();
- DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
int spaceNeeded = getIconLayoutWidth();
- int navSpaceNeeded = deviceProfile.hotseatBarEndOffset;
boolean layoutRtl = isLayoutRtl();
- int centerAlignIconEnd = right - (right - left - spaceNeeded) / 2;
- int iconEnd;
-
+ DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
+ int navSpaceNeeded = deviceProfile.hotseatBarEndOffset;
+ int centerAlignIconEnd = (right + left + spaceNeeded) / 2;
+ int iconEnd = centerAlignIconEnd;
if (mShouldTryStartAlign) {
- // Taskbar is aligned to the start
int startSpacingPx = deviceProfile.inlineNavButtonsEndSpacingPx;
-
- if (layoutRtl) {
- iconEnd = right - startSpacingPx;
+ if (mControllerCallbacks.isBubbleBarEnabledInPersistentTaskbar()
+ && mBubbleBarLocation != null
+ && mActivityContext.shouldStartAlignTaskbar()) {
+ iconEnd = (int) getTaskBarIconsEndForBubbleBarLocation(mBubbleBarLocation);
} else {
- iconEnd = startSpacingPx + spaceNeeded;
+ if (layoutRtl) {
+ iconEnd = right - startSpacingPx;
+ } else {
+ iconEnd = startSpacingPx + spaceNeeded;
+ }
+ boolean needMoreSpaceForNav = layoutRtl
+ ? navSpaceNeeded > (iconEnd - spaceNeeded)
+ : iconEnd > (right - navSpaceNeeded);
+ if (needMoreSpaceForNav) {
+ // Add offset to account for nav bar when taskbar is centered
+ int offset = layoutRtl
+ ? navSpaceNeeded - (centerAlignIconEnd - spaceNeeded)
+ : (right - navSpaceNeeded) - centerAlignIconEnd;
+ iconEnd = centerAlignIconEnd + offset;
+ }
}
- } else {
- iconEnd = centerAlignIconEnd;
- }
-
- boolean needMoreSpaceForNav = layoutRtl
- ? navSpaceNeeded > (iconEnd - spaceNeeded)
- : iconEnd > (right - navSpaceNeeded);
- if (needMoreSpaceForNav) {
- // Add offset to account for nav bar when taskbar is centered
- int offset = layoutRtl
- ? navSpaceNeeded - (centerAlignIconEnd - spaceNeeded)
- : (right - navSpaceNeeded) - centerAlignIconEnd;
-
- iconEnd = centerAlignIconEnd + offset;
}
// Currently, we support only one device with display cutout and we only are concern about
@@ -558,6 +660,7 @@
mIconLayoutBounds.right = iconEnd;
mIconLayoutBounds.top = (bottom - top - mIconTouchSize) / 2;
mIconLayoutBounds.bottom = mIconLayoutBounds.top + mIconTouchSize;
+ int count = getChildCount();
for (int i = count; i > 0; i--) {
View child = getChildAt(i - 1);
if (child == mQsb) {
@@ -676,6 +779,14 @@
}
/**
+ * Returns the taskbar overflow view in the taskbar.
+ */
+ @Nullable
+ public IconButtonView getTaskbarOverflowView() {
+ return mTaskbarOverflowView;
+ }
+
+ /**
* Returns whether the divider is between Hotseat icons and Recents,
* instead of between All Apps button and Hotseat.
*/
@@ -770,4 +881,19 @@
}
return mAllAppsButtonContainer;
}
+
+ /**
+ * This method only works for bubble bar enabled in persistent task bar and the taskbar is start
+ * aligned.
+ */
+ private float getTaskBarIconsEndForBubbleBarLocation(BubbleBarLocation location) {
+ DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
+ boolean navbarOnRight = location.isOnLeft(isLayoutRtl());
+ int navSpaceNeeded = deviceProfile.hotseatBarEndOffset;
+ if (navbarOnRight) {
+ return getWidth() - navSpaceNeeded;
+ } else {
+ return navSpaceNeeded + getIconLayoutWidth();
+ }
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java
index 5ec00ac..176be1c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java
@@ -28,6 +28,7 @@
import com.android.internal.jank.Cuj;
import com.android.launcher3.taskbar.bubbles.BubbleBarViewController;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
+import com.android.wm.shell.Flags;
import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
/**
@@ -127,4 +128,33 @@
}
return null;
}
+
+ /** Returns true if bubble bar controllers present and enabled in persistent taskbar. */
+ public boolean isBubbleBarEnabledInPersistentTaskbar() {
+ return Flags.enableBubbleBarInPersistentTaskBar()
+ && mControllers.bubbleControllers.isPresent();
+ }
+
+ /** Returns on click listener for the taskbar overflow view. */
+ public View.OnClickListener getOverflowOnClickListener() {
+ return new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mControllers.keyboardQuickSwitchController.openQuickSwitchView(
+ mControllers.taskbarViewController.getTaskIdsForPinnedApps());
+ }
+ };
+ }
+
+ /** Returns on long click listener for the taskbar overflow view. */
+ public View.OnLongClickListener getOverflowOnLongClickListener() {
+ return new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ mControllers.keyboardQuickSwitchController.openQuickSwitchView(
+ mControllers.taskbarViewController.getTaskIdsForPinnedApps());
+ return true;
+ }
+ };
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 292b9ed..83527ab 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -18,6 +18,7 @@
import static com.android.app.animation.Interpolators.FINAL_FRAME;
import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
+import static com.android.launcher3.Flags.taskbarOverflow;
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;
@@ -29,11 +30,13 @@
import static com.android.launcher3.config.FeatureFlags.enableTaskbarPinning;
import static com.android.launcher3.taskbar.TaskbarPinningController.PINNING_PERSISTENT;
import static com.android.launcher3.taskbar.TaskbarPinningController.PINNING_TRANSIENT;
+import static com.android.launcher3.taskbar.bubbles.BubbleBarView.FADE_IN_ANIM_ALPHA_DURATION_MS;
+import static com.android.launcher3.taskbar.bubbles.BubbleBarView.FADE_OUT_ANIM_POSITION_DURATION_MS;
import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
+import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_NAV_BAR_ANIM;
import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_TASKBAR_ALIGNMENT_ANIM;
import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_TASKBAR_PINNING_ANIM;
import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_TASKBAR_REVEAL_ANIM;
-import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
import android.animation.Animator;
import android.animation.AnimatorSet;
@@ -66,6 +69,7 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.TaskItemInfo;
+import com.android.launcher3.taskbar.bubbles.BubbleBarController;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LauncherBindableItemsContainer;
@@ -74,15 +78,21 @@
import com.android.launcher3.util.MultiValueAlpha;
import com.android.quickstep.util.GroupTask;
import com.android.systemui.shared.recents.model.Task;
+import com.android.wm.shell.Flags;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
import java.io.PrintWriter;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
/**
* Handles properties/data collection, then passes the results to TaskbarView to render.
*/
-public class TaskbarViewController implements TaskbarControllers.LoggableTaskbarController {
+public class TaskbarViewController implements TaskbarControllers.LoggableTaskbarController,
+ BubbleBarController.BubbleBarLocationListener {
private static final String TAG = "TaskbarViewController";
@@ -122,13 +132,17 @@
private final AnimatedFloat mTaskbarIconTranslationXForPinning = new AnimatedFloat(
this::updateTaskbarIconTranslationXForPinning);
+ private final AnimatedFloat mIconsTranslationXForNavbar = new AnimatedFloat(
+ this::updateTranslationXForNavBar);
+
+ @Nullable
+ private Animator mTaskbarShiftXAnim;
+ @Nullable
+ private BubbleBarLocation mCurrentBubbleBarLocation;
+
private final AnimatedFloat mTaskbarIconTranslationYForPinning = new AnimatedFloat(
this::updateTranslationY);
- private final View.OnLayoutChangeListener mTaskbarViewLayoutChangeListener =
- (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom)
- -> updateTaskbarIconTranslationXForPinning();
-
private AnimatedFloat mTaskbarNavButtonTranslationY;
private AnimatedFloat mTaskbarNavButtonTranslationYForInAppDisplay;
@@ -143,6 +157,12 @@
// Initialized in init.
private TaskbarControllers mControllers;
+ private final View.OnLayoutChangeListener mTaskbarViewLayoutChangeListener =
+ (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
+ updateTaskbarIconTranslationXForPinning();
+ mControllers.navbarButtonsViewController.onTaskbarLayoutChange();
+ };
+
// Animation to align icons with Launcher, created lazily. This allows the controller to be
// active only during the animation and does not need to worry about layout changes.
private AnimatorPlaybackController mIconAlignControllerLazy = null;
@@ -227,6 +247,54 @@
}
}
+ /** Adjusts start aligned taskbar layout accordingly to the bubble bar position. */
+ @Override
+ public void onBubbleBarLocationUpdated(BubbleBarLocation location) {
+ updateCurrentBubbleBarLocation(location);
+ if (!shouldMoveTaskbarOnBubbleBarLocationUpdate()) return;
+ cancelTaskbarShiftAnimation();
+ // reset translation x, taskbar will position icons with the updated location
+ mIconsTranslationXForNavbar.updateValue(0);
+ mTaskbarView.onBubbleBarLocationUpdated(location);
+ }
+
+ /** Animates start aligned taskbar accordingly to the bubble bar position. */
+ @Override
+ public void onBubbleBarLocationAnimated(BubbleBarLocation location) {
+ if (!updateCurrentBubbleBarLocation(location)
+ || !shouldMoveTaskbarOnBubbleBarLocationUpdate()) {
+ return;
+ }
+ cancelTaskbarShiftAnimation();
+ float translationX = mTaskbarView.getTranslationXForBubbleBarPosition(location);
+ mTaskbarShiftXAnim = createTaskbarIconsShiftAnimator(translationX);
+ mTaskbarShiftXAnim.start();
+ }
+
+ /** Updates the mCurrentBubbleBarLocation, returns {@code} true if location is updated. */
+ private boolean updateCurrentBubbleBarLocation(BubbleBarLocation location) {
+ if (mCurrentBubbleBarLocation == location || location == null) {
+ return false;
+ } else {
+ mCurrentBubbleBarLocation = location;
+ return true;
+ }
+ }
+
+ /** Returns whether taskbar should be moved on the bubble bar location update. */
+ private boolean shouldMoveTaskbarOnBubbleBarLocationUpdate() {
+ return Flags.enableBubbleBarInPersistentTaskBar()
+ && mControllers.bubbleControllers.isPresent()
+ && mActivity.shouldStartAlignTaskbar()
+ && mActivity.isThreeButtonNav();
+ }
+
+ private void cancelTaskbarShiftAnimation() {
+ if (mTaskbarShiftXAnim != null) {
+ mTaskbarShiftXAnim.cancel();
+ }
+ }
+
/**
* Announcement for Accessibility when Taskbar stashes/unstashes.
*/
@@ -460,6 +528,17 @@
+ mTaskbarIconTranslationYForSpringOnStash);
}
+ private void updateTranslationXForNavBar() {
+ View[] iconViews = mTaskbarView.getIconViews();
+ float translationX = mIconsTranslationXForNavbar.value;
+ for (int iconIndex = 0; iconIndex < iconViews.length; iconIndex++) {
+ View iconView = iconViews[iconIndex];
+ MultiTranslateDelegate translateDelegate =
+ ((Reorderable) iconView).getTranslateDelegate();
+ translateDelegate.getTranslationX(INDEX_NAV_BAR_ANIM).setValue(translationX);
+ }
+ }
+
/**
* Computes translation y for taskbar pinning.
*/
@@ -554,6 +633,24 @@
}
}
+ /**
+ * @return A set of Task ids of running apps that are pinned in the taskbar.
+ */
+ protected Set<Integer> getTaskIdsForPinnedApps() {
+ if (!taskbarOverflow()) {
+ return Collections.emptySet();
+ }
+
+ Set<Integer> pinnedAppsWithTasks = new HashSet<>();
+ for (View iconView : getIconViews()) {
+ if (iconView instanceof BubbleTextView btv
+ && btv.getTag() instanceof TaskItemInfo itemInfo) {
+ pinnedAppsWithTasks.add(itemInfo.getTaskId());
+ }
+ }
+ return pinnedAppsWithTasks;
+ }
+
private BubbleTextView.RunningAppState getRunningAppState(
BubbleTextView btv,
Set<Integer> runningTaskIds,
@@ -595,8 +692,7 @@
* @param interpolator The interpolator to use for all animations.
*/
public void addRevealAnimToIsStashed(AnimatorSet as, boolean isStashed, long duration,
- Interpolator interpolator, boolean dispatchOnAnimationStart,
- boolean isHomeToAppAnimation) {
+ Interpolator interpolator, boolean dispatchOnAnimationStart) {
AnimatorSet reveal = new AnimatorSet();
Rect stashedBounds = new Rect();
@@ -645,21 +741,8 @@
reveal.play(ObjectAnimator.ofFloat(mtd.getTranslationX(INDEX_TASKBAR_REVEAL_ANIM),
MULTI_PROPERTY_VALUE, transX)
.setDuration(duration));
-
- if (enableScalingRevealHomeAnimation()) {
- // Delay y-translation by 1 frame to keep icons within the bounds of the bg.
- int delay = isHomeToAppAnimation ? getSingleFrameMs(mActivity) : 0;
- ObjectAnimator yAnimator =
- ObjectAnimator.ofFloat(mtd.getTranslationY(INDEX_TASKBAR_REVEAL_ANIM),
- MULTI_PROPERTY_VALUE, transY)
- .setDuration(Math.max(0, duration - delay));
- yAnimator.setStartDelay(delay);
- reveal.play(yAnimator);
- } else {
- reveal.play(
- ObjectAnimator.ofFloat(mtd.getTranslationY(INDEX_TASKBAR_REVEAL_ANIM),
- MULTI_PROPERTY_VALUE, transY));
- }
+ reveal.play(ObjectAnimator.ofFloat(mtd.getTranslationY(INDEX_TASKBAR_REVEAL_ANIM),
+ MULTI_PROPERTY_VALUE, transY));
as.addListener(forEndCallback(() ->
mtd.setTranslation(INDEX_TASKBAR_REVEAL_ANIM, 0, 0)));
} else {
@@ -755,6 +838,7 @@
View child = mTaskbarView.getChildAt(i);
boolean isAllAppsButton = child == mTaskbarView.getAllAppsButtonContainer();
boolean isTaskbarDividerView = child == mTaskbarView.getTaskbarDividerViewContainer();
+ boolean isTaskbarOverflowView = child == mTaskbarView.getTaskbarOverflowView();
boolean isRecentTask = child.getTag() instanceof GroupTask;
// TODO(b/343522351): show recents on the home screen.
final boolean isRecentsInHotseat = false;
@@ -763,9 +847,10 @@
// plays iconAlignment to 1 really fast, therefore moving the fading towards the end
// to avoid icons disappearing rather than fading out visually.
setter.setViewAlpha(child, 0, Interpolators.clampToProgress(LINEAR, 0.8f, 1f));
- } else if ((isAllAppsButton && !FeatureFlags.ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT.get())
+ } else if ((isAllAppsButton && !FeatureFlags.enableAllAppsButtonInHotseat())
|| (isTaskbarDividerView && enableTaskbarPinning())
- || (isRecentTask && !isRecentsInHotseat)) {
+ || (isRecentTask && !isRecentsInHotseat)
+ || isTaskbarOverflowView) {
if (!isToHome
&& mIsHotseatIconOnTopWhenAligned
&& mIsStashed) {
@@ -1018,4 +1103,12 @@
public static void enableModelLoadingForTests(boolean enable) {
sEnableModelLoadingForTests = enable;
}
+
+ private ObjectAnimator createTaskbarIconsShiftAnimator(float translationX) {
+ ObjectAnimator animator = mIconsTranslationXForNavbar.animateToValue(translationX);
+ animator.setStartDelay(FADE_OUT_ANIM_POSITION_DURATION_MS);
+ animator.setDuration(FADE_IN_ANIM_ALPHA_DURATION_MS);
+ animator.setInterpolator(Interpolators.EMPHASIZED);
+ return animator;
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java
index 5d91acd..a46845a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java
@@ -17,13 +17,10 @@
import android.content.Context;
import android.util.AttributeSet;
-import android.view.View;
import androidx.annotation.Nullable;
-import com.android.launcher3.R;
import com.android.launcher3.allapps.ActivityAllAppsContainerView;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
import java.util.Optional;
@@ -47,23 +44,6 @@
}
@Override
- protected View inflateSearchBar() {
- if (isSearchSupported()) {
- return super.inflateSearchBar();
- }
-
- // Remove top padding of header, since we do not have any search
- mHeader.setPadding(mHeader.getPaddingLeft(), 0,
- mHeader.getPaddingRight(), mHeader.getPaddingBottom());
-
- TaskbarAllAppsFallbackSearchContainer searchView =
- new TaskbarAllAppsFallbackSearchContainer(getContext(), null);
- searchView.setId(R.id.search_container_all_apps);
- searchView.setVisibility(GONE);
- return searchView;
- }
-
- @Override
public void invalidateHeader() {
super.invalidateHeader();
Optional.ofNullable(mOnInvalidateHeaderListener).ifPresent(
@@ -71,11 +51,6 @@
}
@Override
- protected boolean isSearchSupported() {
- return FeatureFlags.ENABLE_ALL_APPS_SEARCH_IN_TASKBAR.get();
- }
-
- @Override
public boolean isInAllApps() {
// All apps is always open
return true;
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsFallbackSearchContainer.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsFallbackSearchContainer.java
deleted file mode 100644
index 53fe06d..0000000
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsFallbackSearchContainer.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.taskbar.allapps;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.ExtendedEditText;
-import com.android.launcher3.allapps.ActivityAllAppsContainerView;
-import com.android.launcher3.allapps.SearchUiManager;
-
-/** Empty search container for Taskbar All Apps used as a fallback if search is not supported. */
-public class TaskbarAllAppsFallbackSearchContainer extends View implements SearchUiManager {
- public TaskbarAllAppsFallbackSearchContainer(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public TaskbarAllAppsFallbackSearchContainer(
- Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
- @Override
- public void initializeSearch(ActivityAllAppsContainerView<?> containerView) {
- // Do nothing.
- }
-
- @Override
- public void resetSearch() {
- // Do nothing.
- }
-
- @Nullable
- @Override
- public ExtendedEditText getEditText() {
- return null;
- }
-}
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
index f5ac66f..5830095 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
@@ -39,7 +39,6 @@
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.taskbar.allapps.TaskbarAllAppsViewController.TaskbarAllAppsCallbacks;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
import com.android.launcher3.util.Themes;
@@ -180,9 +179,7 @@
mContent = mAppsView;
// Setup header protection for search bar, if enabled.
- if (FeatureFlags.ENABLE_ALL_APPS_SEARCH_IN_TASKBAR.get()) {
- mAppsView.setOnInvalidateHeaderListener(this::invalidate);
- }
+ mAppsView.setOnInvalidateHeaderListener(this::invalidate);
DeviceProfile dp = mActivityContext.getDeviceProfile();
setShiftRange(dp.allAppsShiftRange);
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarSearchSessionController.kt b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarSearchSessionController.kt
index 4d0b376..6b72ab6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarSearchSessionController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarSearchSessionController.kt
@@ -21,7 +21,6 @@
import com.android.launcher3.R
import com.android.launcher3.allapps.AllAppsTransitionListener
import com.android.launcher3.anim.PendingAnimation
-import com.android.launcher3.config.FeatureFlags
import com.android.launcher3.dragndrop.DragOptions.PreDragCondition
import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.util.ResourceBasedOverride
@@ -61,16 +60,11 @@
companion object {
@JvmStatic
- fun newInstance(context: Context): TaskbarSearchSessionController {
- if (!FeatureFlags.ENABLE_ALL_APPS_SEARCH_IN_TASKBAR.get()) {
- return TaskbarSearchSessionController()
- }
-
- return Overrides.getObject(
+ fun newInstance(context: Context): TaskbarSearchSessionController =
+ Overrides.getObject(
TaskbarSearchSessionController::class.java,
context,
R.string.taskbar_search_session_controller_class,
)
- }
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
index d70a317..6860004 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
@@ -118,6 +118,7 @@
private Optional<BubbleStashedHandleViewController> mBubbleStashedHandleViewController;
private BubblePinController mBubblePinController;
private BubbleCreator mBubbleCreator;
+ private BubbleBarLocationListener mBubbleBarLocationListener;
// Cache last sent top coordinate to avoid sending duplicate updates to shell
private int mLastSentBubbleBarTop;
@@ -176,6 +177,7 @@
/** Initializes controllers. */
public void init(BubbleControllers bubbleControllers,
+ BubbleBarLocationListener bubbleBarLocationListener,
ImeVisibilityChecker imeVisibilityChecker) {
mImeVisibilityChecker = imeVisibilityChecker;
mBubbleBarViewController = bubbleControllers.bubbleBarViewController;
@@ -183,6 +185,7 @@
mBubbleStashedHandleViewController = bubbleControllers.bubbleStashedHandleViewController;
mBubblePinController = bubbleControllers.bubblePinController;
mBubbleCreator = bubbleControllers.bubbleCreator;
+ mBubbleBarLocationListener = bubbleBarLocationListener;
bubbleControllers.runAfterInit(() -> {
mBubbleBarViewController.setHiddenForBubbles(
@@ -488,12 +491,16 @@
private void updateBubbleBarLocationInternal(BubbleBarLocation location) {
mBubbleBarViewController.setBubbleBarLocation(location);
mBubbleStashController.setBubbleBarLocation(location);
+ mBubbleBarLocationListener.onBubbleBarLocationUpdated(location);
}
@Override
public void animateBubbleBarLocation(BubbleBarLocation bubbleBarLocation) {
MAIN_EXECUTOR.execute(
- () -> mBubbleBarViewController.animateBubbleBarLocation(bubbleBarLocation));
+ () -> {
+ mBubbleBarViewController.animateBubbleBarLocation(bubbleBarLocation);
+ mBubbleBarLocationListener.onBubbleBarLocationAnimated(bubbleBarLocation);
+ });
}
/** Notifies WMShell to show the expanded view. */
@@ -518,4 +525,14 @@
/** Whether the IME is visible. */
boolean isImeVisible();
}
+
+ /** Listener of {@link BubbleBarLocation} updates. */
+ public interface BubbleBarLocationListener {
+
+ /** Called when {@link BubbleBarLocation} is animated, but change is not yet final. */
+ void onBubbleBarLocationAnimated(BubbleBarLocation location);
+
+ /** Called when {@link BubbleBarLocation} is updated permanently. */
+ void onBubbleBarLocationUpdated(BubbleBarLocation location);
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarLocationCompositeListener.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarLocationCompositeListener.kt
new file mode 100644
index 0000000..8e176ac
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarLocationCompositeListener.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.taskbar.bubbles
+
+import com.android.launcher3.taskbar.bubbles.BubbleBarController.BubbleBarLocationListener
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation
+
+/** Composite implementation of [BubbleBarLocationListener] interface */
+class BubbleBarLocationCompositeListener(private val listeners: List<BubbleBarLocationListener>) :
+ BubbleBarLocationListener {
+
+ constructor(vararg listeners: BubbleBarLocationListener) : this(listOf(*listeners))
+
+ override fun onBubbleBarLocationAnimated(location: BubbleBarLocation?) {
+ listeners.forEach { it.onBubbleBarLocationAnimated(location) }
+ }
+
+ override fun onBubbleBarLocationUpdated(location: BubbleBarLocation?) {
+ listeners.forEach { it.onBubbleBarLocationUpdated(location) }
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarPinController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarPinController.kt
index 9c34307..a34fab2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarPinController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarPinController.kt
@@ -27,6 +27,7 @@
import android.widget.FrameLayout
import androidx.core.view.updateLayoutParams
import com.android.launcher3.R
+import com.android.launcher3.taskbar.bubbles.BubbleBarController.BubbleBarLocationListener
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController
import com.android.wm.shell.shared.bubbles.BaseBubblePinController
import com.android.wm.shell.shared.bubbles.BubbleBarLocation
@@ -42,12 +43,17 @@
private lateinit var bubbleBarViewController: BubbleBarViewController
private lateinit var bubbleStashController: BubbleStashController
+ private lateinit var bubbleBarLocationListener: BubbleBarLocationListener
private var exclRectWidth: Float = 0f
private var exclRectHeight: Float = 0f
private var dropTargetView: View? = null
- fun init(bubbleControllers: BubbleControllers) {
+ fun init(
+ bubbleControllers: BubbleControllers,
+ bubbleBarLocationListener: BubbleBarLocationListener
+ ) {
+ this.bubbleBarLocationListener = bubbleBarLocationListener
bubbleBarViewController = bubbleControllers.bubbleBarViewController
bubbleStashController = bubbleControllers.bubbleStashController
exclRectWidth = context.resources.getDimension(R.dimen.bubblebar_dismiss_zone_width)
@@ -86,6 +92,7 @@
val bounds = bubbleBarViewController.bubbleBarBounds
val horizontalMargin = bubbleBarViewController.horizontalMargin
+ bubbleBarLocationListener.onBubbleBarLocationAnimated(location)
dropTargetView?.updateLayoutParams<FrameLayout.LayoutParams> {
width = bounds.width()
height = bounds.height()
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarSwipeController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarSwipeController.kt
new file mode 100644
index 0000000..a831fd7
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarSwipeController.kt
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.taskbar.bubbles
+
+import android.animation.ValueAnimator
+import android.content.Context
+import androidx.annotation.VisibleForTesting
+import androidx.core.animation.doOnEnd
+import androidx.dynamicanimation.animation.SpringForce
+import com.android.launcher3.anim.AnimatedFloat
+import com.android.launcher3.anim.SpringAnimationBuilder
+import com.android.launcher3.taskbar.TaskbarActivityContext
+import com.android.launcher3.taskbar.TaskbarThresholdUtils
+import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController
+import com.android.launcher3.touch.OverScroll
+
+/** Handle swipe events on the bubble bar and handle */
+class BubbleBarSwipeController {
+
+ private val context: Context
+
+ private var bubbleStashedHandleViewController: BubbleStashedHandleViewController? = null
+ private var bubbleBarViewController: BubbleBarViewController? = null
+ private var bubbleStashController: BubbleStashController? = null
+
+ private var springAnimation: ValueAnimator? = null
+ private val animatedSwipeTranslation = AnimatedFloat(this::onSwipeUpdate)
+
+ private val unstashThreshold: Int
+ private val expandThreshold: Int
+ private val maxOverscroll: Int
+
+ private var swipeState: SwipeState = SwipeState()
+
+ constructor(tac: TaskbarActivityContext) : this(tac, DefaultDimensionProvider(tac))
+
+ @VisibleForTesting
+ constructor(context: Context, dimensionProvider: DimensionProvider) {
+ this.context = context
+ unstashThreshold = dimensionProvider.unstashThreshold
+ expandThreshold = dimensionProvider.expandThreshold
+ maxOverscroll = dimensionProvider.maxOverscroll
+ }
+
+ fun init(bubbleControllers: BubbleControllers) {
+ bubbleStashedHandleViewController =
+ bubbleControllers.bubbleStashedHandleViewController.orElse(null)
+ bubbleBarViewController = bubbleControllers.bubbleBarViewController
+ bubbleStashController = bubbleControllers.bubbleStashController
+ }
+
+ /** Start tracking a new swipe gesture */
+ fun start() {
+ if (springAnimation != null) reset()
+ val stashed = bubbleStashController?.isStashed ?: false
+ val barVisible = bubbleStashController?.isBubbleBarVisible() ?: false
+ val expanded = bubbleBarViewController?.isExpanded ?: false
+
+ swipeState =
+ SwipeState(
+ stashedOnStart = stashed,
+ collapsedOnStart = !stashed && barVisible && !expanded,
+ expandedOnStart = expanded,
+ )
+ }
+
+ /** Update swipe distance to [dy] */
+ fun swipeTo(dy: Float) {
+ // Only handle swipe up and stashed or collapsed bar
+ if (dy > 0 || swipeState.expandedOnStart) return
+
+ animatedSwipeTranslation.updateValue(dy)
+
+ val prevState = swipeState
+ // We can pass unstash threshold once per gesture, keep it true if it happened once
+ val passedUnstashThreshold = isUnstash(dy) || prevState.passedUnstashThreshold
+ // Expand happens at the end of the gesture, always keep the current value
+ val passedExpandThreshold = isExpand(dy)
+
+ if (
+ passedUnstashThreshold != prevState.passedUnstashThreshold ||
+ passedExpandThreshold != prevState.passedExpandThreshold
+ ) {
+ swipeState =
+ swipeState.copy(
+ passedUnstashThreshold = passedUnstashThreshold,
+ passedExpandThreshold = passedExpandThreshold,
+ )
+ }
+
+ if (
+ swipeState.stashedOnStart &&
+ swipeState.passedUnstashThreshold &&
+ !prevState.passedUnstashThreshold
+ ) {
+ bubbleStashController?.showBubbleBar(expandBubbles = false)
+ }
+ }
+
+ /** Finish tracking swipe gesture. Animate views back to resting state */
+ fun finish() {
+ if (swipeState.passedExpandThreshold) {
+ bubbleStashController?.showBubbleBar(expandBubbles = true)
+ }
+ springToRest()
+ }
+
+ /** Returns `true` if we are tracking a swipe gesture */
+ fun isSwipeGesture(): Boolean {
+ return swipeState.passedUnstashThreshold || swipeState.passedExpandThreshold
+ }
+
+ private fun isUnstash(dy: Float): Boolean {
+ return dy < -unstashThreshold
+ }
+
+ private fun isExpand(dy: Float): Boolean {
+ return dy < -expandThreshold
+ }
+
+ private fun reset() {
+ springAnimation?.let {
+ if (it.isRunning) {
+ it.removeAllListeners()
+ it.cancel()
+ animatedSwipeTranslation.updateValue(0f)
+ }
+ }
+ springAnimation = null
+ swipeState = SwipeState()
+ }
+
+ private fun onSwipeUpdate(value: Float) {
+ val dampedSwipe = -OverScroll.dampedScroll(-value, maxOverscroll).toFloat()
+ bubbleStashedHandleViewController?.setTranslationYForSwipe(dampedSwipe)
+ bubbleBarViewController?.setTranslationYForSwipe(dampedSwipe)
+ }
+
+ private fun springToRest() {
+ springAnimation =
+ SpringAnimationBuilder(context)
+ .setStartValue(animatedSwipeTranslation.value)
+ .setEndValue(0f)
+ .setDampingRatio(SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY)
+ .setStiffness(SpringForce.STIFFNESS_LOW)
+ .build(animatedSwipeTranslation, AnimatedFloat.VALUE)
+ .also { it.doOnEnd { reset() } }
+ springAnimation?.start()
+ }
+
+ internal data class SwipeState(
+ val stashedOnStart: Boolean = false,
+ val collapsedOnStart: Boolean = false,
+ val expandedOnStart: Boolean = false,
+ val passedUnstashThreshold: Boolean = false,
+ val passedExpandThreshold: Boolean = false,
+ )
+
+ /** Allows overriding the dimension provider for testing */
+ @VisibleForTesting
+ interface DimensionProvider {
+ val unstashThreshold: Int
+ val expandThreshold: Int
+ val maxOverscroll: Int
+ }
+
+ private class DefaultDimensionProvider(taskbarActivityContext: TaskbarActivityContext) :
+ DimensionProvider {
+ override val unstashThreshold: Int
+ override val expandThreshold: Int
+ override val maxOverscroll: Int
+
+ init {
+ val resources = taskbarActivityContext.resources
+ unstashThreshold =
+ TaskbarThresholdUtils.getFromNavThreshold(
+ resources,
+ taskbarActivityContext.deviceProfile,
+ )
+ // TODO(325673340): review threshold with ux
+ expandThreshold =
+ TaskbarThresholdUtils.getAppWindowThreshold(
+ resources,
+ taskbarActivityContext.deviceProfile,
+ )
+ maxOverscroll = taskbarActivityContext.deviceProfile.heightPx - unstashThreshold
+ }
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
index 14f6e3a..d454fd7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
@@ -95,11 +95,11 @@
private static final long FADE_OUT_ANIM_ALPHA_DURATION_MS = 50L;
private static final long FADE_OUT_ANIM_ALPHA_DELAY_MS = 50L;
- private static final long FADE_OUT_ANIM_POSITION_DURATION_MS = 100L;
+ public static final long FADE_OUT_ANIM_POSITION_DURATION_MS = 100L;
// During fade out animation we shift the bubble bar 1/80th of the screen width
private static final float FADE_OUT_ANIM_POSITION_SHIFT = 1 / 80f;
- private static final long FADE_IN_ANIM_ALPHA_DURATION_MS = 100L;
+ public static final long FADE_IN_ANIM_ALPHA_DURATION_MS = 100L;
// Use STIFFNESS_MEDIUMLOW which is not defined in the API constants
private static final float FADE_IN_ANIM_POSITION_SPRING_STIFFNESS = 400f;
// During fade in animation we shift the bubble bar 1/60th of the screen width
@@ -187,6 +187,9 @@
private BubbleView mDismissedByDragBubbleView;
private float mAlphaDuringDrag = 1f;
+ /** Additional translation in the y direction that is applied to each bubble */
+ private float mBubbleOffsetY;
+
private Controller mController;
private int mPreviousLayoutDirection = LayoutDirection.UNDEFINED;
@@ -335,6 +338,16 @@
}
/**
+ * Sets offset of each bubble view in the y direction from the base position in the bar.
+ */
+ public void setBubbleOffsetY(float offsetY) {
+ mBubbleOffsetY = offsetY;
+ for (int i = 0; i < getChildCount(); i++) {
+ getChildAt(i).setTranslationY(getBubbleTranslationY());
+ }
+ }
+
+ /**
* Sets new icon sizes and newBubbleBarPadding between icons and bubble bar borders.
*
* @param newIconSize new icon size
@@ -676,7 +689,7 @@
}
setAlphaDuringBubbleDrag(1f);
setTranslationX(0f);
- if (getBubbleChildCount() > 0) {
+ if (mIsBarExpanded && getBubbleChildCount() > 0) {
setAlpha(1f);
}
}
@@ -997,10 +1010,7 @@
final float expandedWidth = expandedWidth();
final float collapsedWidth = collapsedWidth();
int childCount = getChildCount();
- float viewBottom = mBubbleBarBounds.height() + (isExpanded() ? mPointerSize : 0);
- float bubbleBarAnimatedTop = viewBottom - getBubbleBarHeight();
- // When translating X & Y the scale is ignored, so need to deduct it from the translations
- final float ty = bubbleBarAnimatedTop + mBubbleBarPadding - getScaleIconShift();
+ final float ty = getBubbleTranslationY();
final boolean onLeft = bubbleBarLocation.isOnLeft(isLayoutRtl());
// elevation state is opposite to widthState - when expanded all icons are flat
float elevationState = (1 - widthState);
@@ -1125,15 +1135,25 @@
translationX = 0f;
}
} else {
- if (bubbleIndex == 1 && getBubbleChildCount() >= MAX_VISIBLE_BUBBLES_COLLAPSED) {
- translationX = mIconOverlapAmount;
- } else {
+ // when the bar is on the right, the first bubble always has translation 0. the only
+ // case where another bubble has translation 0 is when we only have 1 bubble and the
+ // overflow. otherwise all other bubbles should be shifted by the overlap amount.
+ if (bubbleIndex == 0 || getBubbleChildCount() == 1) {
translationX = 0f;
+ } else {
+ translationX = mIconOverlapAmount;
}
}
return mBubbleBarPadding + translationX - getScaleIconShift();
}
+ private float getBubbleTranslationY() {
+ float viewBottom = mBubbleBarBounds.height() + (isExpanded() ? mPointerSize : 0);
+ float bubbleBarAnimatedTop = viewBottom - getBubbleBarHeight();
+ // When translating X & Y the scale is ignored, so need to deduct it from the translations
+ return mBubbleOffsetY + bubbleBarAnimatedTop + mBubbleBarPadding - getScaleIconShift();
+ }
+
/**
* Reorders the views to match the provided list.
*/
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index 570b1b9..025c038 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -18,11 +18,8 @@
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
-import static com.android.launcher3.taskbar.bubbles.BubbleView.STASH_TRANSLATION_Y;
-
import android.animation.Animator;
import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.PointF;
@@ -97,6 +94,8 @@
this::updateBackgroundScaleY);
private final AnimatedFloat mBubbleBarTranslationY = new AnimatedFloat(
this::updateTranslationY);
+ private final AnimatedFloat mBubbleOffsetY = new AnimatedFloat(
+ this::updateBubbleOffsetY);
// Modified when swipe up is happening on the bubble bar or task bar.
private float mBubbleBarSwipeUpTranslationY;
@@ -299,6 +298,10 @@
return mBubbleBarTranslationY;
}
+ public AnimatedFloat getBubbleOffsetY() {
+ return mBubbleOffsetY;
+ }
+
public float getBubbleBarCollapsedWidth() {
return mBarView.collapsedWidth();
}
@@ -437,7 +440,7 @@
if (hidden) {
mBarView.setAlpha(0);
mBarView.setExpanded(false);
- updatePersistentTaskbar(/* isBubbleBarExpanded = */ false);
+ adjustTaskbarAndHotseatToBubbleBarState(/* isBubbleBarExpanded = */ false);
}
mActivity.bubbleBarVisibilityChanged(!hidden);
}
@@ -576,6 +579,10 @@
mBarView.setBubbleAlpha(alpha);
}
+ private void updateBubbleOffsetY(float transY) {
+ mBarView.setBubbleOffsetY(transY);
+ }
+
private void updateBackgroundAlpha(float alpha) {
mBarView.setBackgroundAlpha(alpha);
}
@@ -728,7 +735,7 @@
public void setExpanded(boolean isExpanded) {
if (isExpanded != mBarView.isExpanded()) {
mBarView.setExpanded(isExpanded);
- updatePersistentTaskbar(isExpanded);
+ adjustTaskbarAndHotseatToBubbleBarState(isExpanded);
if (!isExpanded) {
mSystemUiProxy.collapseBubbles();
} else {
@@ -739,13 +746,25 @@
}
}
- private void updatePersistentTaskbar(boolean isBubbleBarExpanded) {
- if (mBubbleStashController.isTransientTaskBar()) return;
- boolean hideTaskbar = isBubbleBarExpanded && isIntersectingTaskbar();
- mTaskbarViewPropertiesProvider
- .getIconsAlpha()
- .animateToValue(hideTaskbar ? 0 : 1)
- .start();
+ /**
+ * Hides the persistent taskbar if it is going to intersect with the expanded bubble bar if in
+ * app or overview. Set the hotseat stashed state if on launcher home screen. If not on launcher
+ * home screen and hotseat is stashed immediately un-stashes the hotseat.
+ */
+ private void adjustTaskbarAndHotseatToBubbleBarState(boolean isBubbleBarExpanded) {
+ if (mBubbleStashController.isBubblesShowingOnHome()) {
+ mTaskbarStashController.stashHotseat(isBubbleBarExpanded);
+ } else if (!mBubbleStashController.isTransientTaskBar()) {
+ boolean hideTaskbar = isBubbleBarExpanded && isIntersectingTaskbar();
+ mTaskbarViewPropertiesProvider
+ .getIconsAlpha()
+ .animateToValue(hideTaskbar ? 0 : 1)
+ .start();
+ }
+ if (!mBubbleStashController.isBubblesShowingOnHome()
+ && mTaskbarStashController.isHiddenForBubbles()) {
+ mTaskbarStashController.unStashHotseatInstantly();
+ }
}
/** Return {@code true} if expanded bubble bar would intersect the taskbar. */
@@ -864,6 +883,11 @@
mBoundsChangeListener = listener;
}
+ /** Called when the controller is destroyed. */
+ public void onDestroy() {
+ adjustTaskbarAndHotseatToBubbleBarState(/*isBubbleBarExpanded = */false);
+ }
+
/**
* Create an animator for showing or hiding bubbles when stashed state changes
*
@@ -874,14 +898,9 @@
mBubbleStashController.getHandleBounds(stashedHandleBounds);
int childCount = mBarView.getChildCount();
float newChildWidth = (float) stashedHandleBounds.width() / childCount;
- float stashTranslationY = -mBubbleStashController.getBubbleBarTranslationY();
AnimatorSet animatorSet = new AnimatorSet();
for (int i = 0; i < childCount; i++) {
BubbleView child = (BubbleView) mBarView.getChildAt(i);
- final float startTransY = isStashed ? 0f : stashTranslationY;
- final float endTransY = isStashed ? stashTranslationY : 0f;
- animatorSet.play(
- ObjectAnimator.ofFloat(child, STASH_TRANSLATION_Y, startTransY, endTransY));
animatorSet.play(
createRevealAnimForBubble(child, isStashed, stashedHandleBounds,
newChildWidth));
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java
index e00916a..8230f42 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java
@@ -40,6 +40,7 @@
public final BubbleDismissController bubbleDismissController;
public final BubbleBarPinController bubbleBarPinController;
public final BubblePinController bubblePinController;
+ public final Optional<BubbleBarSwipeController> bubbleBarSwipeController;
public final BubbleCreator bubbleCreator;
private final RunnableList mPostInitRunnables = new RunnableList();
@@ -58,6 +59,7 @@
BubbleDismissController bubbleDismissController,
BubbleBarPinController bubbleBarPinController,
BubblePinController bubblePinController,
+ Optional<BubbleBarSwipeController> bubbleBarSwipeController,
BubbleCreator bubbleCreator) {
this.bubbleBarController = bubbleBarController;
this.bubbleBarViewController = bubbleBarViewController;
@@ -67,6 +69,7 @@
this.bubbleDismissController = bubbleDismissController;
this.bubbleBarPinController = bubbleBarPinController;
this.bubblePinController = bubblePinController;
+ this.bubbleBarSwipeController = bubbleBarSwipeController;
this.bubbleCreator = bubbleCreator;
}
@@ -76,7 +79,14 @@
* in constructors for now, as some controllers may still be waiting for init().
*/
public void init(TaskbarControllers taskbarControllers) {
+ // TODO(b/346381754) add TaskbarLauncherStateController implementation to adjust the hotseat
+ BubbleBarLocationCompositeListener bubbleBarLocationListeners =
+ new BubbleBarLocationCompositeListener(
+ taskbarControllers.navbarButtonsViewController,
+ taskbarControllers.taskbarViewController
+ );
bubbleBarController.init(this,
+ bubbleBarLocationListeners,
taskbarControllers.navbarButtonsViewController::isImeVisible);
bubbleStashedHandleViewController.ifPresent(
controller -> controller.init(/* bubbleControllers = */ this));
@@ -102,8 +112,9 @@
});
bubbleDragController.init(/* bubbleControllers = */ this);
bubbleDismissController.init(/* bubbleControllers = */ this);
- bubbleBarPinController.init(this);
+ bubbleBarPinController.init(this, bubbleBarLocationListeners);
bubblePinController.init(this);
+ bubbleBarSwipeController.ifPresent(c -> c.init(this));
mPostInitRunnables.executeAllAndDestroy();
}
@@ -124,6 +135,7 @@
public void onDestroy() {
bubbleStashedHandleViewController.ifPresent(BubbleStashedHandleViewController::onDestroy);
bubbleBarController.onDestroy();
+ bubbleBarViewController.onDestroy();
}
/** Dumps bubble controllers state. */
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleCreator.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleCreator.java
index 12b1487..340a120 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleCreator.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleCreator.java
@@ -44,8 +44,6 @@
import android.view.LayoutInflater;
import android.view.ViewGroup;
-import androidx.appcompat.content.res.AppCompatResources;
-
import com.android.internal.graphics.ColorUtils;
import com.android.launcher3.R;
import com.android.launcher3.icons.BitmapInfo;
@@ -196,8 +194,7 @@
}
private Bitmap createOverflowBitmap() {
- Drawable iconDrawable = AppCompatResources.getDrawable(mContext,
- R.drawable.bubble_ic_overflow_button);
+ Drawable iconDrawable = mContext.getDrawable(R.drawable.bubble_ic_overflow_button);
final TypedArray ta = mContext.obtainStyledAttributes(
new int[]{
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
index cc6b49a..561df5c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
@@ -27,7 +27,6 @@
import android.os.Bundle;
import android.text.TextUtils;
import android.util.AttributeSet;
-import android.util.FloatProperty;
import android.view.LayoutInflater;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ImageView;
@@ -50,27 +49,12 @@
public static final int DEFAULT_PATH_SIZE = 100;
- public static FloatProperty<BubbleView> STASH_TRANSLATION_Y = new FloatProperty<>(
- "stashTranslationY") {
- @Override
- public void setValue(BubbleView bubbleView, float transY) {
- bubbleView.setStashTranslationY(transY);
- }
-
- @Override
- public Float get(BubbleView bubbleView) {
- return bubbleView.mStashTranslationY;
- }
- };
-
private final ImageView mBubbleIcon;
private final ImageView mAppIcon;
private int mBubbleSize;
private float mDragTranslationX;
private float mOffsetX;
- private float mTranslationY;
- private float mStashTranslationY;
private DotRenderer mDotRenderer;
private DotRenderer.DrawParams mDrawParams;
@@ -177,24 +161,6 @@
setTranslationX(mDragTranslationX + mOffsetX);
}
- /**
- * Set translation in y direction during stash and unstash from handle
- */
- public void setStashTranslationY(float translationY) {
- mStashTranslationY = translationY;
- applyTranslationY();
- }
-
- @Override
- public void setTranslationY(float translationY) {
- mTranslationY = translationY;
- applyTranslationY();
- }
-
- private void applyTranslationY() {
- super.setTranslationY(mTranslationY + mStashTranslationY);
- }
-
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutController.kt
new file mode 100644
index 0000000..49760ff
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutController.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.taskbar.bubbles.flyout
+
+import android.view.Gravity
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import androidx.core.animation.ValueAnimator
+import com.android.launcher3.R
+
+/** Creates and manages the visibility of the [BubbleBarFlyoutView]. */
+class BubbleBarFlyoutController(
+ private val container: FrameLayout,
+ private val positioner: BubbleBarFlyoutPositioner,
+) {
+
+ private var flyout: BubbleBarFlyoutView? = null
+ private val horizontalMargin =
+ container.context.resources.getDimensionPixelSize(R.dimen.transient_taskbar_bottom_margin)
+
+ fun setUpFlyout(message: BubbleBarFlyoutMessage) {
+ flyout?.let(container::removeView)
+ val flyout = BubbleBarFlyoutView(container.context, positioner)
+
+ flyout.translationY = positioner.targetTy
+
+ val lp =
+ FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ Gravity.BOTTOM or if (positioner.isOnLeft) Gravity.LEFT else Gravity.RIGHT,
+ )
+ lp.marginStart = horizontalMargin
+ lp.marginEnd = horizontalMargin
+ container.addView(flyout, lp)
+
+ val animator = ValueAnimator.ofFloat(0f, 1f)
+ animator.addUpdateListener { _ ->
+ flyout.updateExpansionProgress(animator.animatedValue as Float)
+ }
+ flyout.showFromCollapsed(message) { animator.start() }
+ this.flyout = flyout
+ }
+
+ fun hideFlyout() {
+ val flyout = this.flyout ?: return
+ container.removeView(flyout)
+ this.flyout = null
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutMessage.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutMessage.kt
new file mode 100644
index 0000000..7298297
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutMessage.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.taskbar.bubbles.flyout
+
+import android.graphics.drawable.Drawable
+
+data class BubbleBarFlyoutMessage(
+ val senderAvatar: Drawable?,
+ val senderName: CharSequence,
+ val message: CharSequence,
+ val isGroupChat: Boolean,
+)
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutPositioner.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutPositioner.kt
new file mode 100644
index 0000000..2b77dec
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutPositioner.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.taskbar.bubbles.flyout
+
+import android.graphics.PointF
+
+/** Provides positioning data to the flyout view. */
+interface BubbleBarFlyoutPositioner {
+
+ /** Whether the flyout view should be positioned on left or the right edge. */
+ val isOnLeft: Boolean
+
+ /** The target translation Y that the flyout view should have when displayed. */
+ val targetTy: Float
+
+ /**
+ * The distance between the expanded position of the flyout and the collapsed position.
+ *
+ * The distance is calculated between the bottom corner which is aligned with the bubble bar.
+ */
+ val distanceToCollapsedPosition: PointF
+
+ /** The size of the flyout when collapsed. */
+ val collapsedSize: Float
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt
new file mode 100644
index 0000000..8884b64
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.taskbar.bubbles.flyout
+
+import android.content.Context
+import android.content.res.Configuration
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Paint
+import android.graphics.Path
+import android.graphics.PointF
+import android.view.LayoutInflater
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.constraintlayout.widget.ConstraintLayout
+import com.android.launcher3.R
+import com.android.launcher3.popup.RoundedArrowDrawable
+
+/** The flyout view used to notify the user of a new bubble notification. */
+class BubbleBarFlyoutView(context: Context, private val positioner: BubbleBarFlyoutPositioner) :
+ ConstraintLayout(context) {
+
+ private companion object {
+ // the minimum progress of the expansion animation before the triangle is made visible.
+ const val MIN_EXPANSION_PROGRESS_FOR_TRIANGLE = 0.1f
+ }
+
+ private val sender: TextView by
+ lazy(LazyThreadSafetyMode.NONE) { findViewById(R.id.bubble_flyout_name) }
+
+ private val avatar: ImageView by
+ lazy(LazyThreadSafetyMode.NONE) { findViewById(R.id.bubble_flyout_avatar) }
+
+ private val message: TextView by
+ lazy(LazyThreadSafetyMode.NONE) { findViewById(R.id.bubble_flyout_text) }
+
+ private val flyoutPadding by
+ lazy(LazyThreadSafetyMode.NONE) {
+ context.resources.getDimensionPixelSize(R.dimen.bubblebar_flyout_padding)
+ }
+
+ private val triangleHeight by
+ lazy(LazyThreadSafetyMode.NONE) {
+ context.resources.getDimensionPixelSize(R.dimen.bubblebar_flyout_triangle_height)
+ }
+
+ private val triangleOverlap by
+ lazy(LazyThreadSafetyMode.NONE) {
+ context.resources.getDimensionPixelSize(
+ R.dimen.bubblebar_flyout_triangle_overlap_amount
+ )
+ }
+
+ private val triangleWidth by
+ lazy(LazyThreadSafetyMode.NONE) {
+ context.resources.getDimensionPixelSize(R.dimen.bubblebar_flyout_triangle_width)
+ }
+
+ private val triangleRadius by
+ lazy(LazyThreadSafetyMode.NONE) {
+ context.resources.getDimensionPixelSize(R.dimen.bubblebar_flyout_triangle_radius)
+ }
+
+ private val minFlyoutWidth by
+ lazy(LazyThreadSafetyMode.NONE) {
+ context.resources.getDimensionPixelSize(R.dimen.bubblebar_flyout_min_width)
+ }
+
+ private val maxFlyoutWidth by
+ lazy(LazyThreadSafetyMode.NONE) {
+ context.resources.getDimensionPixelSize(R.dimen.bubblebar_flyout_max_width)
+ }
+
+ private val cornerRadius: Float
+ private val triangle: Path = Path()
+ private var backgroundColor = Color.BLACK
+ /** Represents the progress of the expansion animation. 0 when collapsed. 1 when expanded. */
+ private var expansionProgress = 0f
+ /** Translation x-y values to move the flyout to its collapsed position. */
+ private var translationToCollapsedPosition = PointF(0f, 0f)
+ /** The size of the flyout when it's collapsed. */
+ private var collapsedSize = 0f
+ /** The corner radius of the flyout when it's collapsed. */
+ private var collapsedCornerRadius = 0f
+
+ /**
+ * The paint used to draw the background, whose color changes as the flyout transitions to the
+ * tinted notification dot.
+ */
+ private val backgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG or Paint.FILTER_BITMAP_FLAG)
+
+ init {
+ LayoutInflater.from(context).inflate(R.layout.bubblebar_flyout, this, true)
+
+ val ta = context.obtainStyledAttributes(intArrayOf(android.R.attr.dialogCornerRadius))
+ cornerRadius = ta.getDimensionPixelSize(0, 0).toFloat()
+ ta.recycle()
+
+ setWillNotDraw(false)
+ clipChildren = false
+ clipToPadding = false
+
+ val padding = context.resources.getDimensionPixelSize(R.dimen.bubblebar_flyout_padding)
+ // add extra padding to the bottom of the view to include the triangle
+ setPadding(padding, padding, padding, padding + triangleHeight - triangleOverlap)
+ translationZ =
+ context.resources.getDimensionPixelSize(R.dimen.bubblebar_flyout_elevation).toFloat()
+
+ RoundedArrowDrawable.addDownPointingRoundedTriangleToPath(
+ triangleWidth.toFloat(),
+ triangleHeight.toFloat(),
+ triangleRadius.toFloat(),
+ triangle,
+ )
+
+ applyConfigurationColors(resources.configuration)
+ }
+
+ /** Sets the data for the flyout and starts playing the expand animation. */
+ fun showFromCollapsed(flyoutMessage: BubbleBarFlyoutMessage, expandAnimation: () -> Unit) {
+ setData(flyoutMessage)
+ val txToCollapsedPosition =
+ if (positioner.isOnLeft) {
+ positioner.distanceToCollapsedPosition.x
+ } else {
+ -positioner.distanceToCollapsedPosition.x
+ }
+ val tyToCollapsedPosition =
+ positioner.distanceToCollapsedPosition.y + triangleHeight - triangleOverlap
+ translationToCollapsedPosition = PointF(txToCollapsedPosition, tyToCollapsedPosition)
+
+ collapsedSize = positioner.collapsedSize
+ collapsedCornerRadius = collapsedSize / 2
+
+ // post the request to start the expand animation to the looper so the view can measure
+ // itself
+ post(expandAnimation)
+ }
+
+ private fun setData(flyoutMessage: BubbleBarFlyoutMessage) {
+ // the avatar is only displayed in group chat messages
+ if (flyoutMessage.senderAvatar != null && flyoutMessage.isGroupChat) {
+ avatar.visibility = VISIBLE
+ avatar.setImageDrawable(flyoutMessage.senderAvatar)
+ } else {
+ avatar.visibility = GONE
+ }
+
+ val minTextViewWidth: Int
+ val maxTextViewWidth: Int
+ if (avatar.visibility == VISIBLE) {
+ minTextViewWidth = minFlyoutWidth - avatar.width - flyoutPadding * 2
+ maxTextViewWidth = maxFlyoutWidth - avatar.width - flyoutPadding * 2
+ } else {
+ // when there's no avatar, the width of the text view is constant, so we're setting the
+ // min and max to the same value
+ minTextViewWidth = minFlyoutWidth - flyoutPadding * 2
+ maxTextViewWidth = minTextViewWidth
+ }
+
+ if (flyoutMessage.senderName.isEmpty()) {
+ sender.visibility = GONE
+ } else {
+ sender.minWidth = minTextViewWidth
+ sender.maxWidth = maxTextViewWidth
+ sender.text = flyoutMessage.senderName
+ sender.visibility = VISIBLE
+ }
+
+ message.minWidth = minTextViewWidth
+ message.maxWidth = maxTextViewWidth
+ message.text = flyoutMessage.message
+ }
+
+ /** Updates the flyout view with the progress of the animation. */
+ fun updateExpansionProgress(fraction: Float) {
+ expansionProgress = fraction
+ invalidate()
+ }
+
+ override fun onDraw(canvas: Canvas) {
+ // interpolate the width, height, corner radius and translation based on the progress of the
+ // animation
+
+ val currentWidth = collapsedSize + (width - collapsedSize) * expansionProgress
+ val rectBottom = height - triangleHeight + triangleOverlap
+ val currentHeight = collapsedSize + (rectBottom - collapsedSize) * expansionProgress
+ val currentCornerRadius =
+ collapsedCornerRadius + (cornerRadius - collapsedCornerRadius) * expansionProgress
+ val tx = translationToCollapsedPosition.x * (1 - expansionProgress)
+ val ty = translationToCollapsedPosition.y * (1 - expansionProgress)
+
+ canvas.save()
+ canvas.translate(tx, ty)
+ // draw the background starting from the bottom left if we're positioned left, or the bottom
+ // right if we're positioned right.
+ canvas.drawRoundRect(
+ if (positioner.isOnLeft) 0f else width.toFloat() - currentWidth,
+ height.toFloat() - triangleHeight + triangleOverlap - currentHeight,
+ if (positioner.isOnLeft) currentWidth else width.toFloat(),
+ height.toFloat() - triangleHeight + triangleOverlap,
+ currentCornerRadius,
+ currentCornerRadius,
+ backgroundPaint,
+ )
+ if (expansionProgress >= MIN_EXPANSION_PROGRESS_FOR_TRIANGLE) {
+ drawTriangle(canvas, currentCornerRadius)
+ }
+ canvas.restore()
+ super.onDraw(canvas)
+ }
+
+ private fun drawTriangle(canvas: Canvas, currentCornerRadius: Float) {
+ canvas.save()
+ val triangleX =
+ if (positioner.isOnLeft) {
+ currentCornerRadius
+ } else {
+ width - currentCornerRadius - triangleWidth
+ }
+ // instead of scaling the triangle, increasingly reveal it from the background, starting
+ // with half the size. this has the effect of the triangle scaling.
+ val triangleY = height - triangleHeight - 0.5f * triangleHeight * (1 - expansionProgress)
+ canvas.translate(triangleX, triangleY)
+ canvas.drawPath(triangle, backgroundPaint)
+ canvas.restore()
+ }
+
+ private fun applyConfigurationColors(configuration: Configuration) {
+ val nightModeFlags = configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
+ val isNightModeOn = nightModeFlags == Configuration.UI_MODE_NIGHT_YES
+ val defaultBackgroundColor = if (isNightModeOn) Color.BLACK else Color.WHITE
+ val defaultTextColor = if (isNightModeOn) Color.WHITE else Color.BLACK
+ val ta =
+ context.obtainStyledAttributes(
+ intArrayOf(
+ com.android.internal.R.attr.materialColorSurfaceContainer,
+ com.android.internal.R.attr.materialColorOnSurface,
+ com.android.internal.R.attr.materialColorOnSurfaceVariant,
+ )
+ )
+ backgroundColor = ta.getColor(0, defaultBackgroundColor)
+ sender.setTextColor(ta.getColor(1, defaultTextColor))
+ message.setTextColor(ta.getColor(2, defaultTextColor))
+ ta.recycle()
+ backgroundPaint.color = backgroundColor
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt
index 5d1e890..74e3c00 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt
@@ -32,8 +32,8 @@
import com.android.launcher3.anim.AnimatedFloat
import com.android.launcher3.anim.SpringAnimationBuilder
import com.android.launcher3.taskbar.TaskbarInsetsController
-import com.android.launcher3.taskbar.TaskbarStashController.TASKBAR_STASH_ALPHA_DURATION
import com.android.launcher3.taskbar.TaskbarStashController.TASKBAR_STASH_ALPHA_START_DELAY
+import com.android.launcher3.taskbar.TaskbarStashController.TRANSIENT_TASKBAR_STASH_ALPHA_DURATION
import com.android.launcher3.taskbar.bubbles.BubbleBarViewController
import com.android.launcher3.taskbar.bubbles.BubbleStashedHandleViewController
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.Companion.BAR_STASH_DURATION
@@ -47,7 +47,7 @@
class TransientBubbleStashController(
private val taskbarHotseatDimensionsProvider: TaskbarHotseatDimensionsProvider,
- private val context: Context
+ private val context: Context,
) : BubbleStashController {
private lateinit var bubbleBarViewController: BubbleBarViewController
@@ -70,6 +70,7 @@
private lateinit var bubbleBarBubbleAlpha: AnimatedFloat
private lateinit var bubbleBarBackgroundAlpha: AnimatedFloat
private lateinit var bubbleBarTranslationYAnimator: AnimatedFloat
+ private lateinit var bubbleBarBubbleTranslationY: AnimatedFloat
private lateinit var bubbleBarBackgroundScaleX: AnimatedFloat
private lateinit var bubbleBarBackgroundScaleY: AnimatedFloat
private val handleCenterFromScreenBottom =
@@ -143,13 +144,14 @@
taskbarInsetsController: TaskbarInsetsController,
bubbleBarViewController: BubbleBarViewController,
bubbleStashedHandleViewController: BubbleStashedHandleViewController?,
- controllersAfterInitAction: ControllersAfterInitAction
+ controllersAfterInitAction: ControllersAfterInitAction,
) {
this.taskbarInsetsController = taskbarInsetsController
this.bubbleBarViewController = bubbleBarViewController
this.bubbleStashedHandleViewController = bubbleStashedHandleViewController
this.controllersAfterInitAction = controllersAfterInitAction
bubbleBarTranslationYAnimator = bubbleBarViewController.bubbleBarTranslationY
+ bubbleBarBubbleTranslationY = bubbleBarViewController.bubbleOffsetY
// bubble bar has only alpha property, getting it at index 0
bubbleBarAlpha = bubbleBarViewController.bubbleBarAlpha.get(/* index= */ 0)
bubbleBarBubbleAlpha = bubbleBarViewController.bubbleBarBubbleAlpha
@@ -170,7 +172,7 @@
bubbleBarTranslationYAnimator.animateToValue(bubbleBarTranslationY),
bubbleBarAlpha.animateToValue(1f),
bubbleBarBubbleAlpha.animateToValue(1f),
- bubbleBarBackgroundAlpha.animateToValue(1f)
+ bubbleBarBackgroundAlpha.animateToValue(1f),
)
} else {
isStashed = true
@@ -301,23 +303,25 @@
private fun createStashAnimator(isStashed: Boolean, duration: Long): AnimatorSet {
val animatorSet = AnimatorSet()
- val alphaDuration = if (isStashed) duration else TASKBAR_STASH_ALPHA_DURATION
- val alphaDelay = if (isStashed) TASKBAR_STASH_ALPHA_START_DELAY else 0L
animatorSet.play(
createBackgroundAlphaAnimator(isStashed).apply {
+ val alphaDuration =
+ if (isStashed) duration else TRANSIENT_TASKBAR_STASH_ALPHA_DURATION
+ val alphaDelay = if (isStashed) TASKBAR_STASH_ALPHA_START_DELAY else 0L
this.duration = max(0L, alphaDuration - alphaDelay)
this.startDelay = alphaDelay
this.interpolator = LINEAR
}
)
- val iconAlphaTarget = if (isStashed) 0f else 1f
animatorSet.play(
- bubbleBarBubbleAlpha.animateToValue(iconAlphaTarget).apply {
- this.duration = TASKBAR_STASH_ALPHA_DURATION
- this.startDelay = TASKBAR_STASH_ALPHA_START_DELAY
- this.interpolator = LINEAR
- }
+ bubbleBarBubbleAlpha
+ .animateToValue(getBarAlphaStart(isStashed), getBarAlphaEnd(isStashed))
+ .apply {
+ this.duration = TRANSIENT_TASKBAR_STASH_ALPHA_DURATION
+ this.startDelay = TASKBAR_STASH_ALPHA_START_DELAY
+ this.interpolator = LINEAR
+ }
)
animatorSet.play(
@@ -334,6 +338,16 @@
}
)
+ // Animate bubble translation to keep reveal animation in the bounds of the bar
+ val bubbleTyStart = if (isStashed) 0f else -bubbleBarTranslationY
+ val bubbleTyEnd = if (isStashed) -bubbleBarTranslationY else 0f
+ animatorSet.play(
+ bubbleBarBubbleTranslationY.animateToValue(bubbleTyStart, bubbleTyEnd).apply {
+ this.duration = duration
+ this.interpolator = EMPHASIZED
+ }
+ )
+
animatorSet.play(
bubbleStashedHandleViewController?.createRevealAnimToIsStashed(isStashed)?.apply {
this.duration = duration
@@ -359,11 +373,15 @@
)
animatorSet.doOnStart {
- if (!isStashed) {
- bubbleBarBackgroundAlpha.updateValue(0f)
- bubbleBarBubbleAlpha.updateValue(0f)
- bubbleBarAlpha.value = 1f
- }
+ // Update the start value for bubble view and background alpha when the entire animation
+ // begins.
+ // Alpha animation has a delay, and if we set the initial values at the start of the
+ // alpha animation, it will cause flickers.
+ bubbleBarBubbleAlpha.updateValue(getBarAlphaStart(isStashed))
+ bubbleBarBackgroundAlpha.updateValue(getBarAlphaStart(isStashed))
+ // We animate alpha for background and bubble views separately. Make sure the container
+ // is always visible.
+ bubbleBarAlpha.value = 1f
}
animatorSet.doOnEnd {
animator = null
@@ -373,6 +391,9 @@
// reset bubble view alpha
bubbleBarBubbleAlpha.updateValue(1f)
bubbleBarBackgroundAlpha.updateValue(1f)
+ // reset stash translation
+ translationYDuringStash.updateValue(0f)
+ bubbleBarBubbleTranslationY.updateValue(0f)
bubbleBarViewController.isExpanded = false
}
taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
@@ -382,14 +403,29 @@
}
private fun createBackgroundAlphaAnimator(isStashed: Boolean): AnimatorSet {
- val stashHandleAlphaTarget = if (isStashed) 1f else 0f
- val barAlphaTarget = if (isStashed) 0f else 1f
return AnimatorSet().apply {
- play(bubbleBarBackgroundAlpha.animateToValue(barAlphaTarget))
- play(stashHandleViewAlpha?.animateToValue(stashHandleAlphaTarget))
+ play(
+ bubbleBarBackgroundAlpha.animateToValue(
+ getBarAlphaStart(isStashed),
+ getBarAlphaEnd(isStashed),
+ )
+ )
+ play(stashHandleViewAlpha?.animateToValue(getHandleAlphaEnd(isStashed)))
}
}
+ private fun getBarAlphaStart(isStashed: Boolean): Float {
+ return if (isStashed) 1f else 0f
+ }
+
+ private fun getBarAlphaEnd(isStashed: Boolean): Float {
+ return if (isStashed) 0f else 1f
+ }
+
+ private fun getHandleAlphaEnd(isStashed: Boolean): Float {
+ return if (isStashed) 1f else 0f
+ }
+
private fun createSpringOnStashAnimator(isStashed: Boolean): Animator {
if (!isStashed) {
// Animate the stash translation back to 0
diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt
index 726800c..e6c0b2f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt
@@ -30,7 +30,6 @@
import androidx.core.view.setPadding
import com.android.launcher3.R
import com.android.launcher3.Utilities.dpToPx
-import com.android.launcher3.config.FeatureFlags.ENABLE_ALL_APPS_SEARCH_IN_TASKBAR
import com.android.launcher3.config.FeatureFlags.enableTaskbarPinning
import com.android.launcher3.taskbar.TaskbarActivityContext
import com.android.launcher3.taskbar.TaskbarViewCallbacks
@@ -43,11 +42,8 @@
/** Taskbar all apps button container for customizable taskbar. */
class TaskbarAllAppsButtonContainer
@JvmOverloads
-constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyleAttr: Int = 0,
-) : IconButtonView(context, attrs), TaskbarContainer {
+constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) :
+ IconButtonView(context, attrs), TaskbarContainer {
private val activityContext: TaskbarActivityContext = ActivityContext.lookupContext(context)
private var allAppsTouchTriggered = false
@@ -101,23 +97,16 @@
private fun getAllAppsButton(isTransientTaskbar: Boolean): Int {
val shouldSelectTransientIcon =
isTransientTaskbar || (enableTaskbarPinning() && !activityContext.isThreeButtonNav)
- return if (ENABLE_ALL_APPS_SEARCH_IN_TASKBAR.get()) {
- if (shouldSelectTransientIcon) R.drawable.ic_transient_taskbar_all_apps_search_button
- else R.drawable.ic_taskbar_all_apps_search_button
- } else {
- if (shouldSelectTransientIcon) R.drawable.ic_transient_taskbar_all_apps_button
- else R.drawable.ic_taskbar_all_apps_button
- }
+ return if (shouldSelectTransientIcon) R.drawable.ic_transient_taskbar_all_apps_search_button
+ else R.drawable.ic_taskbar_all_apps_search_button
}
@DimenRes
fun getAllAppsButtonTranslationXOffset(isTransientTaskbar: Boolean): Int {
return if (isTransientTaskbar) {
R.dimen.transient_taskbar_all_apps_button_translation_x_offset
- } else if (ENABLE_ALL_APPS_SEARCH_IN_TASKBAR.get()) {
- R.dimen.taskbar_all_apps_search_button_translation_x_offset
} else {
- R.dimen.taskbar_all_apps_button_translation_x_offset
+ R.dimen.taskbar_all_apps_search_button_translation_x_offset
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
index 773b0b9..669850c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
@@ -73,7 +73,7 @@
@Override
public void recreateControllers() {
List<TouchController> controllers = new ArrayList<>();
- controllers.add(mActivity.getDragController());
+ controllers.add(mContainer.getDragController());
controllers.addAll(mTouchControllers);
mControllers = controllers.toArray(new TouchController[0]);
}
@@ -87,7 +87,7 @@
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getAction() == ACTION_UP && event.getKeyCode() == KEYCODE_BACK) {
- AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
+ AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mContainer);
if (topView != null && topView.canHandleBack()) {
topView.onBackInvoked();
return true;
@@ -96,7 +96,7 @@
&& event.getKeyCode() == KeyEvent.KEYCODE_ESCAPE && event.hasNoModifiers()) {
// Ignore escape if pressed in conjunction with any modifier keys. Close each
// floating view one at a time for each key press.
- AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
+ AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mContainer);
if (topView != null) {
topView.close(/* animate= */ true);
return true;
@@ -107,7 +107,7 @@
@Override
public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
- if (mActivity.isAnySystemDragInProgress()) {
+ if (mContainer.isAnySystemDragInProgress()) {
inoutInfo.touchableRegion.setEmpty();
inoutInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
}
@@ -123,7 +123,7 @@
@Override
public void onViewRemoved(View child) {
super.onViewRemoved(child);
- mActivity.getOverlayController().maybeCloseWindow();
+ mContainer.getOverlayController().maybeCloseWindow();
}
/** Adds a {@link TouchController} to this drag layer. */
@@ -147,14 +147,14 @@
* 2) Sets tappableInsets bottom inset to 0.
*/
private WindowInsets updateInsetsDueToStashing(WindowInsets oldInsets) {
- if (!DisplayController.isTransientTaskbar(mActivity)) {
+ if (!DisplayController.isTransientTaskbar(mContainer)) {
return oldInsets;
}
WindowInsets.Builder updatedInsetsBuilder = new WindowInsets.Builder(oldInsets);
Insets oldNavInsets = oldInsets.getInsets(WindowInsets.Type.navigationBars());
Insets newNavInsets = Insets.of(oldNavInsets.left, oldNavInsets.top, oldNavInsets.right,
- mActivity.getStashedTaskbarHeight());
+ mContainer.getStashedTaskbarHeight());
updatedInsetsBuilder.setInsets(WindowInsets.Type.navigationBars(), newNavInsets);
Insets oldTappableInsets = oldInsets.getInsets(WindowInsets.Type.tappableElement());
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index 14d391b..bcad478 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -86,7 +86,7 @@
}
setStateWithAnimationInternal(toState, config, builder);
builder.addEndListener(success -> {
- if (!success) {
+ if (!success && !toState.isRecentsViewVisible) {
mRecentsView.reset();
}
});
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
index 93cbdc7..26a1322 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
@@ -29,7 +29,6 @@
import android.widget.RemoteViews;
import android.window.SplashScreen;
-import com.android.launcher3.Utilities;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.util.ActivityOptionsWrapper;
@@ -66,7 +65,7 @@
Pair<Intent, ActivityOptions> options = remoteResponse.getLaunchOptions(view);
ActivityOptionsWrapper activityOptions = mLauncher.getAppTransitionManager()
.getActivityLaunchOptions(hostView, (ItemInfo) hostView.getTag());
- if (Utilities.ATLEAST_S && !pendingIntent.isActivity()) {
+ if (!pendingIntent.isActivity()) {
// In the event this pending intent eventually launches an activity, i.e. a trampoline,
// use the Quickstep transition animation.
try {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index d6ea653..9d9f096 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -19,6 +19,7 @@
import static android.os.Trace.TRACE_TAG_APP;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEASURE;
import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
+import static android.window.flags.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY;
import static com.android.app.animation.Interpolators.EMPHASIZED;
import static com.android.internal.jank.Cuj.CUJ_LAUNCHER_LAUNCH_APP_PAIR_FROM_WORKSPACE;
@@ -59,12 +60,9 @@
import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN;
import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.QUICK_SWITCH_FROM_HOME_FAILED;
-import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.QUICK_SWITCH_FROM_HOME_FALLBACK;
import static com.android.quickstep.util.AnimUtils.completeRunnableListCallback;
import static com.android.quickstep.util.SplitAnimationTimings.TABLET_HOME_TO_SPLIT;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
-import static com.android.wm.shell.shared.desktopmode.DesktopModeFlags.WALLPAPER_ACTIVITY;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_50_50;
import android.animation.Animator;
@@ -172,7 +170,7 @@
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskUtils;
import com.android.quickstep.TouchInteractionService.TISBinder;
-import com.android.quickstep.util.ActiveGestureLog;
+import com.android.quickstep.util.ActiveGestureProtoLogProxy;
import com.android.quickstep.util.AsyncClockEventDelegate;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.util.LauncherUnfoldAnimationController;
@@ -271,7 +269,7 @@
RecentsView<?,?> overviewPanel = getOverviewPanel();
SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.get(this);
mSplitSelectStateController =
- new SplitSelectStateController(this, mHandler, getStateManager(),
+ new SplitSelectStateController(this, getStateManager(),
getDepthController(), getStatsLogManager(),
systemUiProxy, RecentsModel.INSTANCE.get(this),
() -> onStateBack());
@@ -595,11 +593,8 @@
TaskView taskToLaunch = currentPageTask;
if (currentPageTask == null) {
taskToLaunch = fallbackTask;
- ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
- "Quick switch from home fallback case: The TaskView at index ")
- .append(rv.getCurrentPage())
- .append(" is missing."),
- QUICK_SWITCH_FROM_HOME_FALLBACK);
+ ActiveGestureProtoLogProxy.logQuickSwitchFromHomeFallback(
+ rv.getCurrentPage());
}
taskToLaunch.launchWithoutAnimation(success -> {
if (!success) {
@@ -610,11 +605,7 @@
return Unit.INSTANCE;
});
} else {
- ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
- "Quick switch from home failed: TaskViews at indices ")
- .append(rv.getCurrentPage())
- .append(" and 0 are missing."),
- QUICK_SWITCH_FROM_HOME_FAILED);
+ ActiveGestureProtoLogProxy.logQuickSwitchFromHomeFailed(rv.getCurrentPage());
getStateManager().goToState(NORMAL);
}
break;
@@ -681,10 +672,6 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
- // Back dispatcher is registered in {@link BaseActivity#onCreate}. For predictive back to
- // work, we must opt-in BEFORE registering back dispatcher. So we need to call
- // setEnableOnBackInvokedCallback() before super.onCreate()
- getApplicationInfo().setEnableOnBackInvokedCallback(true);
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
mPendingSplitSelectInfo = ObjectWrapper.unwrap(
@@ -692,9 +679,7 @@
}
addMultiWindowModeChangedListener(mDepthController);
initUnfoldTransitionProgressProvider();
- if (FeatureFlags.CONTINUOUS_VIEW_TREE_CAPTURE.get()) {
- mViewCapture = ViewCaptureFactory.getInstance(this).startCapture(getWindow());
- }
+ mViewCapture = ViewCaptureFactory.getInstance(this).startCapture(getWindow());
getWindow().addPrivateFlags(PRIVATE_FLAG_OPTIMIZE_MEASURE);
QuickstepOnboardingPrefs.setup(this);
View.setTraceLayoutSteps(TRACE_LAYOUTS);
@@ -1005,7 +990,7 @@
@Override
public void setResumed() {
DesktopVisibilityController desktopVisibilityController = getDesktopVisibilityController();
- if (!WALLPAPER_ACTIVITY.isEnabled(this)
+ if (!ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()
&& desktopVisibilityController != null
&& desktopVisibilityController.areDesktopTasksVisible()
&& !desktopVisibilityController.isRecentsGestureInProgress()) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index 1ba784b..18d717f 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -50,9 +50,11 @@
return super.getVerticalProgress(launcher);
}
RecentsView recentsView = launcher.getOverviewPanel();
- int transitionLength = LayoutUtils.getShelfTrackingDistance(launcher,
+ int transitionLength = LayoutUtils.getShelfTrackingDistance(
+ launcher,
launcher.getDeviceProfile(),
- recentsView.getPagedOrientationHandler());
+ recentsView.getPagedOrientationHandler(),
+ recentsView.getSizeStrategy());
AllAppsTransitionController controller = launcher.getAllAppsController();
float scrollRange = Math.max(controller.getShiftRange(), 1);
float progressDelta = (transitionLength / scrollRange);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index 0da7b2d..9164405 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -129,7 +129,10 @@
mRecentsView = mLauncher.getOverviewPanel();
mXRange = mLauncher.getDeviceProfile().widthPx / 2f;
mYRange = LayoutUtils.getShelfTrackingDistance(
- mLauncher, mLauncher.getDeviceProfile(), mRecentsView.getPagedOrientationHandler());
+ mLauncher,
+ mLauncher.getDeviceProfile(),
+ mRecentsView.getPagedOrientationHandler(),
+ mRecentsView.getSizeStrategy());
mMaxYProgress = mLauncher.getDeviceProfile().heightPx / mYRange;
mMotionPauseDetector = new MotionPauseDetector(mLauncher);
mMotionPauseMinDisplacement = mLauncher.getResources().getDimension(
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index b6a0ab3..b562838 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -142,8 +142,11 @@
.createPlaybackController();
mLauncher.getStateManager().setCurrentUserControlledAnimation(mCurrentAnimation);
RecentsView recentsView = mLauncher.getOverviewPanel();
- totalShift = LayoutUtils.getShelfTrackingDistance(mLauncher,
- mLauncher.getDeviceProfile(), recentsView.getPagedOrientationHandler());
+ totalShift = LayoutUtils.getShelfTrackingDistance(
+ mLauncher,
+ mLauncher.getDeviceProfile(),
+ recentsView.getPagedOrientationHandler(),
+ recentsView.getSizeStrategy());
} else {
mCurrentAnimation = mLauncher.getStateManager()
.createAnimationToNewWorkspace(mToState, config);
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 240d6ad..864328f 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -45,6 +45,7 @@
import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
+import static com.android.quickstep.BaseContainerInterface.AnimationFactory;
import static com.android.quickstep.GestureState.GestureEndTarget.HOME;
import static com.android.quickstep.GestureState.GestureEndTarget.LAST_TASK;
import static com.android.quickstep.GestureState.GestureEndTarget.NEW_TASK;
@@ -55,11 +56,7 @@
import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_STARTED;
import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
-import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.CANCEL_RECENTS_ANIMATION;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.EXPECTING_TASK_APPEARED;
-import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.INVALID_VELOCITY_ON_SWIPE_UP;
-import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.LAUNCHER_DESTROYED;
-import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.ON_SETTLED_ON_END_TARGET;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
@@ -92,6 +89,7 @@
import android.view.animation.Interpolator;
import android.widget.Toast;
import android.window.PictureInPictureSurfaceTransaction;
+import android.window.flags.DesktopModeFlags;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -102,6 +100,7 @@
import com.android.internal.util.LatencyTracker;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Flags;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
@@ -111,6 +110,7 @@
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.logging.StatsLogManager.StatsLogger;
import com.android.launcher3.statemanager.BaseState;
+import com.android.launcher3.statemanager.StatefulContainer;
import com.android.launcher3.taskbar.TaskbarThresholdUtils;
import com.android.launcher3.taskbar.TaskbarUIController;
import com.android.launcher3.uioverrides.QuickstepLauncher;
@@ -119,11 +119,12 @@
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.util.VibratorWrapper;
import com.android.launcher3.util.WindowBounds;
-import com.android.quickstep.BaseActivityInterface.AnimationFactory;
import com.android.quickstep.GestureState.GestureEndTarget;
import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
+import com.android.quickstep.fallback.window.RecentsWindowManager;
import com.android.quickstep.util.ActiveGestureErrorDetector;
import com.android.quickstep.util.ActiveGestureLog;
+import com.android.quickstep.util.ActiveGestureProtoLogProxy;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.InputConsumerProxy;
@@ -152,12 +153,9 @@
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.wm.shell.shared.TransactionPool;
-import com.android.wm.shell.shared.desktopmode.DesktopModeFlags;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.shared.startingsurface.SplashScreenExitAnimationUtils;
-import kotlin.Unit;
-
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -167,15 +165,16 @@
import java.util.OptionalInt;
import java.util.function.Consumer;
+import kotlin.Unit;
+
/**
* Handles the navigation gestures when Launcher is the default home activity.
*/
public abstract class AbsSwipeUpHandler<
- RECENTS_CONTAINER extends Context & RecentsViewContainer,
- RECENTS_VIEW extends RecentsView<RECENTS_CONTAINER, STATE>,
- STATE extends BaseState<STATE>>
- extends SwipeUpAnimationLogic
- implements OnApplyWindowInsetsListener, RecentsAnimationCallbacks.RecentsAnimationListener {
+ RECENTS_CONTAINER extends Context & RecentsViewContainer & StatefulContainer<STATE>,
+ RECENTS_VIEW extends RecentsView<RECENTS_CONTAINER, STATE>, STATE extends BaseState<STATE>>
+ extends SwipeUpAnimationLogic implements OnApplyWindowInsetsListener,
+ RecentsAnimationCallbacks.RecentsAnimationListener {
private static final String TAG = "AbsSwipeUpHandler";
private static final ArrayList<String> STATE_NAMES = new ArrayList<>();
@@ -201,7 +200,7 @@
private boolean mRecentsViewScrollLinked = false;
private final Runnable mLauncherOnDestroyCallback = () -> {
- ActiveGestureLog.INSTANCE.addLog("Launcher destroyed", LAUNCHER_DESTROYED);
+ ActiveGestureProtoLogProxy.logLauncherDestroyed();
mRecentsView = null;
mContainer = null;
mStateCallback.clearState(STATE_LAUNCHER_PRESENT);
@@ -292,7 +291,7 @@
private static final int LOG_NO_OP_PAGE_INDEX = -1;
protected final TaskAnimationManager mTaskAnimationManager;
-
+ protected final RecentsWindowManager mRecentsWindowManager;
// Either RectFSpringAnim (if animating home) or ObjectAnimator (from mCurrentShift) otherwise
private RunningWindowAnim[] mRunningWindowAnim;
// Possible second animation running at the same time as mRunningWindowAnim
@@ -353,9 +352,12 @@
public AbsSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState,
TaskAnimationManager taskAnimationManager, GestureState gestureState,
long touchTimeMs, boolean continuingLastGesture,
- InputConsumerController inputConsumer) {
+ InputConsumerController inputConsumer, RecentsWindowManager recentsWindowManager) {
super(context, deviceState, gestureState);
mContainerInterface = gestureState.getContainerInterface();
+ if (recentsWindowManager != null && Flags.enableFallbackOverviewInWindow()) {
+ recentsWindowManager.registerInitListener(this::onActivityInit);
+ }
mActivityInitListener =
mContainerInterface.createActivityInitListener(this::onActivityInit);
mInputConsumerProxy =
@@ -369,6 +371,7 @@
endLauncherTransitionController();
}, new InputProxyHandlerFactory(mContainerInterface, mGestureState));
mTaskAnimationManager = taskAnimationManager;
+ mRecentsWindowManager = recentsWindowManager;
mTouchTimeMs = touchTimeMs;
mContinuingLastGesture = continuingLastGesture;
@@ -819,7 +822,7 @@
return;
}
initTransitionEndpoints(mContainer.getDeviceProfile());
- mAnimationFactory.createActivityInterface(mTransitionDragLength);
+ mAnimationFactory.createContainerInterface(mTransitionDragLength);
}
/**
@@ -863,10 +866,13 @@
}
}
+ public Intent getHomeIntent() {
+ return mGestureState.getHomeIntent();
+ }
+
public Intent getLaunchIntent() {
return mGestureState.getOverviewIntent();
}
-
/**
* Called when the value of {@link #mCurrentShift} changes
*/
@@ -975,9 +981,7 @@
@Override
public void onRecentsAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas) {
- ActiveGestureLog.INSTANCE.addLog(
- /* event= */ "cancelRecentsAnimation",
- /* gestureEvent= */ CANCEL_RECENTS_ANIMATION);
+ ActiveGestureProtoLogProxy.logAbsSwipeUpHandlerOnRecentsAnimationCanceled();
mActivityInitListener.unregister("AbsSwipeUpHandler.onRecentsAnimationCanceled");
mStateCallback.setStateOnUiThread(STATE_GESTURE_CANCELLED | STATE_HANDLER_INVALIDATED);
// Defer clearing the controller and the targets until after we've updated the state
@@ -1181,10 +1185,7 @@
// Resets this value as the gesture is now complete.
mContainerInterface.getTaskbarController().setUserIsNotGoingHome(false);
}
- ActiveGestureLog.INSTANCE.addLog(
- new ActiveGestureLog.CompoundString("onSettledOnEndTarget ")
- .append(endTarget.name()),
- /* gestureEvent= */ ON_SETTLED_ON_END_TARGET);
+ ActiveGestureProtoLogProxy.logOnSettledOnEndTarget(endTarget.name());
}
/** @return Whether this was the task we were waiting to appear, and thus handled it. */
@@ -1218,19 +1219,10 @@
private GestureEndTarget calculateEndTarget(
PointF velocityPxPerMs, float endVelocityPxPerMs, boolean isFlingY, boolean isCancel) {
-
- ActiveGestureErrorDetector.GestureEvent gestureEvent =
- velocityPxPerMs.x == 0 && velocityPxPerMs.y == 0
- ? INVALID_VELOCITY_ON_SWIPE_UP
- : null;
- ActiveGestureLog.INSTANCE.addLog(
- new ActiveGestureLog.CompoundString("calculateEndTarget: velocities=(x=")
- .append(dpiFromPx(velocityPxPerMs.x))
- .append("dp/ms, y=")
- .append(dpiFromPx(velocityPxPerMs.y))
- .append("dp/ms), angle=")
- .append(Math.toDegrees(Math.atan2(
- -velocityPxPerMs.y, velocityPxPerMs.x))), gestureEvent);
+ 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.
@@ -1256,8 +1248,8 @@
? mRecentsView.getCurrentPageTaskView() : null;
if (DesktopModeStatus.canEnterDesktopMode(mContext)
- && !(DesktopModeFlags.WALLPAPER_ACTIVITY.isEnabled(mContext)
- && DesktopModeFlags.QUICK_SWITCH.isEnabled(mContext))) {
+ && !(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()
+ && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_QUICK_SWITCH.isTrue())) {
if ((nextPageTaskView instanceof DesktopTaskView
|| currentPageTaskView instanceof DesktopTaskView)
&& endTarget == NEW_TASK) {
@@ -1423,8 +1415,8 @@
};
if (DesktopModeStatus.canEnterDesktopMode(mContext)
- && !(DesktopModeFlags.WALLPAPER_ACTIVITY.isEnabled(mContext)
- && DesktopModeFlags.QUICK_SWITCH.isEnabled(mContext))) {
+ && !(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()
+ && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_QUICK_SWITCH.isTrue())) {
if (mRecentsView != null && (mRecentsView.getCurrentPageTaskView() != null
&& !(mRecentsView.getCurrentPageTaskView() instanceof DesktopTaskView))) {
ActiveGestureLog.INSTANCE.trackEvent(ActiveGestureErrorDetector.GestureEvent
@@ -1981,9 +1973,7 @@
* handler (in case of quick switch).
*/
private void cancelCurrentAnimation() {
- ActiveGestureLog.INSTANCE.addLog(
- "AbsSwipeUpHandler.cancelCurrentAnimation",
- ActiveGestureErrorDetector.GestureEvent.CANCEL_CURRENT_ANIMATION);
+ ActiveGestureProtoLogProxy.logAbsSwipeUpHandlerCancelCurrentAnimation();
mCanceled = true;
mCurrentShift.cancelAnimation();
@@ -2249,8 +2239,8 @@
});
if (DesktopModeStatus.canEnterDesktopMode(mContext)
- && !(DesktopModeFlags.WALLPAPER_ACTIVITY.isEnabled(mContext)
- && DesktopModeFlags.QUICK_SWITCH.isEnabled(mContext))) {
+ && !(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()
+ && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_QUICK_SWITCH.isTrue())) {
if (mRecentsView.getNextPageTaskView() instanceof DesktopTaskView
|| mRecentsView.getCurrentPageTaskView() instanceof DesktopTaskView) {
mRecentsViewScrollLinked = false;
@@ -2304,7 +2294,7 @@
if (!hasTaskPreviouslyAppeared) {
ActiveGestureLog.INSTANCE.trackEvent(EXPECTING_TASK_APPEARED);
}
- ActiveGestureLog.INSTANCE.addLog(nextTaskLog);
+ ActiveGestureProtoLogProxy.logDynamicString(nextTaskLog.toString());
nextTask.launchWithoutAnimation(true, success -> {
resultCallback.accept(success);
if (success) {
@@ -2382,9 +2372,7 @@
return;
}
final Runnable onFinishComplete = () -> {
- ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
- "AbsSwipeUpHandler.onTasksAppeared: ")
- .append("force finish recents animation complete; clearing state callback."));
+ ActiveGestureProtoLogProxy.logAbsSwipeUpHandlerOnTasksAppeared();
mStateCallback.setStateOnUiThread(STATE_GESTURE_CANCELLED | STATE_HANDLER_INVALIDATED);
};
ActiveGestureLog.CompoundString forceFinishReason = new ActiveGestureLog.CompoundString(
@@ -2395,18 +2383,17 @@
// previous quickswitch task launch, then cancel the animation back to the app
RemoteAnimationTarget appearedTaskTarget = appearedTaskTargets[0];
TaskInfo taskInfo = appearedTaskTarget.taskInfo;
- ActiveGestureLog.INSTANCE.addLog(forceFinishReason
- .append("Unexpected task appeared id=")
- .append(taskInfo.taskId)
- .append(" pkg=")
- .append(taskInfo.baseIntent.getComponent().getPackageName()));
+ ActiveGestureProtoLogProxy.logUnexpectedTaskAppeared(
+ taskInfo.taskId,
+ taskInfo.baseIntent.getComponent().getPackageName());
finishRecentsAnimationOnTasksAppeared(onFinishComplete);
return;
}
ActiveGestureLog.CompoundString handleTaskFailureReason =
new ActiveGestureLog.CompoundString("handleTaskAppeared check failed: ");
if (!handleTaskAppeared(appearedTaskTargets, handleTaskFailureReason)) {
- ActiveGestureLog.INSTANCE.addLog(forceFinishReason.append(handleTaskFailureReason));
+ forceFinishReason.append(handleTaskFailureReason);
+ ActiveGestureProtoLogProxy.logDynamicString(forceFinishReason.toString());
finishRecentsAnimationOnTasksAppeared(onFinishComplete);
return;
}
@@ -2414,8 +2401,8 @@
.filter(mGestureState.mLastStartedTaskIdPredicate)
.toArray(RemoteAnimationTarget[]::new);
if (taskTargets.length == 0) {
- ActiveGestureLog.INSTANCE.addLog(
- forceFinishReason.append("No appeared task matching started task id"));
+ forceFinishReason.append("No appeared task matching started task id");
+ ActiveGestureProtoLogProxy.logDynamicString(forceFinishReason.toString());
finishRecentsAnimationOnTasksAppeared(onFinishComplete);
return;
}
@@ -2424,12 +2411,14 @@
? null : mRecentsView.getTaskViewByTaskId(taskTarget.taskId);
if (taskView == null || taskView.getTaskContainers().stream().noneMatch(
TaskContainer::getShouldShowSplashView)) {
- ActiveGestureLog.INSTANCE.addLog(forceFinishReason.append("Splash not needed"));
+ forceFinishReason.append("Splash not needed");
+ ActiveGestureProtoLogProxy.logDynamicString(forceFinishReason.toString());
finishRecentsAnimationOnTasksAppeared(onFinishComplete);
return;
}
if (mContainer == null) {
- ActiveGestureLog.INSTANCE.addLog(forceFinishReason.append("Activity destroyed"));
+ forceFinishReason.append("Activity destroyed");
+ ActiveGestureProtoLogProxy.logDynamicString(forceFinishReason.toString());
finishRecentsAnimationOnTasksAppeared(onFinishComplete);
return;
}
@@ -2485,7 +2474,7 @@
if (mRecentsAnimationController != null) {
mRecentsAnimationController.finish(false /* toRecents */, onFinishComplete);
}
- ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimationOnTasksAppeared");
+ ActiveGestureProtoLogProxy.logFinishRecentsAnimationOnTasksAppeared();
}
/**
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 8703843..1e755eb 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -36,7 +36,6 @@
import android.view.MotionEvent;
import androidx.annotation.Nullable;
-import androidx.annotation.UiThread;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
@@ -62,17 +61,14 @@
public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_TYPE>,
ACTIVITY_TYPE extends StatefulActivity<STATE_TYPE> & RecentsViewContainer> extends
BaseContainerInterface<STATE_TYPE, ACTIVITY_TYPE> {
- private final STATE_TYPE mBackgroundState;
private STATE_TYPE mTargetState;
- @Nullable private Runnable mOnInitBackgroundStateUICallback = null;
-
protected BaseActivityInterface(boolean rotationSupportedByActivity,
STATE_TYPE overviewState, STATE_TYPE backgroundState) {
+ super(backgroundState);
this.rotationSupportedByActivity = rotationSupportedByActivity;
mTargetState = overviewState;
- mBackgroundState = backgroundState;
}
/**
@@ -124,13 +120,6 @@
return activity != null && activity.isStarted();
}
- @UiThread
- @Nullable
- public abstract <T extends RecentsView> T getVisibleRecentsView();
-
- @UiThread
- public abstract boolean switchToRecentsIfVisible(Animator.AnimatorListener animatorListener);
-
public boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
TaskbarUIController controller = getTaskbarController();
boolean isEventOverBubbleBarStashHandle =
@@ -163,49 +152,6 @@
recentsView.switchToScreenshot(thumbnailDatas, runnable);
}
-
- protected void runOnInitBackgroundStateUI(Runnable callback) {
- ACTIVITY_TYPE activity = getCreatedContainer();
- if (activity != null && activity.getStateManager().getState() == mBackgroundState) {
- callback.run();
- onInitBackgroundStateUI();
- return;
- }
- mOnInitBackgroundStateUICallback = callback;
- }
-
- private void onInitBackgroundStateUI() {
- if (mOnInitBackgroundStateUICallback != null) {
- mOnInitBackgroundStateUICallback.run();
- mOnInitBackgroundStateUICallback = null;
- }
- }
-
- public interface AnimationFactory {
-
- void createActivityInterface(long transitionLength);
-
- /**
- * @param attached Whether to show RecentsView alongside the app window. If false, recents
- * will be hidden by some property we can animate, e.g. alpha.
- * @param animate Whether to animate recents to/from its new attached state.
- * @param updateRunningTaskAlpha Whether to update the running task's attached alpha
- */
- default void setRecentsAttachedToAppWindow(
- boolean attached, boolean animate, boolean updateRunningTaskAlpha) { }
-
- default boolean isRecentsAttachedToAppWindow() {
- return false;
- }
-
- default boolean hasRecentsEverAttachedToAppWindow() {
- return false;
- }
-
- /** Called when the gesture ends and we know what state it is going towards */
- default void setEndTarget(GestureState.GestureEndTarget endTarget) { }
- }
-
class DefaultAnimationFactory implements AnimationFactory {
protected final ACTIVITY_TYPE mActivity;
@@ -234,7 +180,7 @@
}
@Override
- public void createActivityInterface(long transitionLength) {
+ public void createContainerInterface(long transitionLength) {
PendingAnimation pa = new PendingAnimation(transitionLength * 2);
createBackgroundToOverviewAnim(mActivity, pa);
AnimatorPlaybackController controller = pa.createPlaybackController();
diff --git a/quickstep/src/com/android/quickstep/BaseContainerInterface.java b/quickstep/src/com/android/quickstep/BaseContainerInterface.java
index bf3a662..14c2cc4 100644
--- a/quickstep/src/com/android/quickstep/BaseContainerInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseContainerInterface.java
@@ -34,16 +34,19 @@
import android.view.View;
import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Flags;
import com.android.launcher3.R;
import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.statemanager.BaseState;
+import com.android.launcher3.statemanager.StatefulContainer;
import com.android.launcher3.taskbar.TaskbarUIController;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.WindowBounds;
import com.android.launcher3.views.ScrimView;
+import com.android.quickstep.fallback.window.RecentsWindowManager;
import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.AnimatorControllerWithResistance;
@@ -57,13 +60,28 @@
import java.util.function.Predicate;
public abstract class BaseContainerInterface<STATE_TYPE extends BaseState<STATE_TYPE>,
- CONTAINER_TYPE extends RecentsViewContainer> {
+ CONTAINER_TYPE extends RecentsViewContainer & StatefulContainer<STATE_TYPE>> {
public boolean rotationSupportedByActivity = false;
+ protected final STATE_TYPE mBackgroundState;
+
+ protected BaseContainerInterface(STATE_TYPE backgroundState) {
+ mBackgroundState = backgroundState;
+ }
+
+ @UiThread
+ @Nullable
+ public abstract <T extends RecentsView<?,?>> T getVisibleRecentsView();
+
+ @UiThread
+ public abstract boolean switchToRecentsIfVisible(Animator.AnimatorListener animatorListener);
@Nullable
public abstract CONTAINER_TYPE getCreatedContainer();
+ @Nullable
+ protected Runnable mOnInitBackgroundStateUICallback = null;
+
public abstract boolean isInLiveTileMode();
public abstract void onAssistantVisibilityChanged(float assistantVisibility);
@@ -88,7 +106,32 @@
@Nullable
public abstract TaskbarUIController getTaskbarController();
- public abstract BaseActivityInterface.AnimationFactory prepareRecentsUI(
+ public interface AnimationFactory {
+
+ void createContainerInterface(long transitionLength);
+
+ /**
+ * @param attached Whether to show RecentsView alongside the app window. If false, recents
+ * will be hidden by some property we can animate, e.g. alpha.
+ * @param animate Whether to animate recents to/from its new attached state.
+ * @param updateRunningTaskAlpha Whether to update the running task's attached alpha
+ */
+ default void setRecentsAttachedToAppWindow(
+ boolean attached, boolean animate, boolean updateRunningTaskAlpha) { }
+
+ default boolean isRecentsAttachedToAppWindow() {
+ return false;
+ }
+
+ default boolean hasRecentsEverAttachedToAppWindow() {
+ return false;
+ }
+
+ /** Called when the gesture ends and we know what state it is going towards */
+ default void setEndTarget(GestureState.GestureEndTarget endTarget) { }
+ }
+
+ public abstract BaseContainerInterface.AnimationFactory prepareRecentsUI(
RecentsAnimationDeviceState deviceState, boolean activityVisible,
Consumer<AnimatorControllerWithResistance> callback);
@@ -126,6 +169,17 @@
return false;
}
+ public void runOnInitBackgroundStateUI(Runnable callback) {
+ StatefulContainer container = getCreatedContainer();
+ if (container != null
+ && container.getStateManager().getState() == mBackgroundState) {
+ callback.run();
+ onInitBackgroundStateUI();
+ return;
+ }
+ mOnInitBackgroundStateUICallback = callback;
+ }
+
@Nullable
public DesktopVisibilityController getDesktopVisibilityController() {
CONTAINER_TYPE container = getCreatedContainer();
@@ -403,4 +457,11 @@
outRect,
orientationHandler);
}
+
+ protected void onInitBackgroundStateUI() {
+ if (mOnInitBackgroundStateUICallback != null) {
+ mOnInitBackgroundStateUICallback.run();
+ mOnInitBackgroundStateUICallback = null;
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/BaseWindowInterface.java b/quickstep/src/com/android/quickstep/BaseWindowInterface.java
new file mode 100644
index 0000000..9d6e61d
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/BaseWindowInterface.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep;
+
+import static com.android.app.animation.Interpolators.ACCELERATE_2;
+import static com.android.app.animation.Interpolators.INSTANT;
+import static com.android.app.animation.Interpolators.LINEAR;
+import static com.android.launcher3.MotionEventsUtils.isTrackpadMultiFingerSwipe;
+import static com.android.quickstep.AbsSwipeUpHandler.RECENTS_ATTACH_DURATION;
+import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_RECENTS_FADE_ANIM;
+import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_RECENTS_TRANSLATE_X_ANIM;
+import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
+import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
+import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
+import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.util.Log;
+import android.view.MotionEvent;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.statehandlers.DepthController;
+import com.android.launcher3.taskbar.TaskbarUIController;
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.NavigationMode;
+import com.android.quickstep.fallback.RecentsState;
+import com.android.quickstep.fallback.window.RecentsWindowManager;
+import com.android.quickstep.util.AnimatorControllerWithResistance;
+import com.android.quickstep.views.RecentsView;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+
+import java.util.HashMap;
+import java.util.Optional;
+import java.util.function.Consumer;
+
+/**
+ * Temporary utility class in place for differences needed between
+ * Recents in Window in Launcher vs Fallback
+ */
+public abstract class BaseWindowInterface extends
+ BaseContainerInterface<RecentsState, RecentsWindowManager> {
+
+ final String TAG = "BaseWindowInterface";
+ private RecentsState mTargetState;
+
+
+ protected BaseWindowInterface(RecentsState overviewState, RecentsState backgroundState) {
+ super(backgroundState);
+ mTargetState = overviewState;
+ }
+
+ @Nullable
+ public abstract RecentsWindowManager getCreatedContainer();
+
+ @Nullable
+ public DepthController getDepthController() {
+ return null;
+ }
+
+ public final boolean isResumed() {
+ return isStarted();
+ }
+
+ public final boolean isStarted() {
+ RecentsWindowManager windowManager = getCreatedContainer();
+ return windowManager != null && windowManager.isStarted();
+ }
+
+ public boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
+ TaskbarUIController controller = getTaskbarController();
+ boolean isEventOverBubbleBarStashHandle =
+ controller != null && controller.isEventOverBubbleBarViews(ev);
+ return deviceState.isInDeferredGestureRegion(ev) || deviceState.isImeRenderingNavButtons()
+ || isTrackpadMultiFingerSwipe(ev) || isEventOverBubbleBarStashHandle;
+ }
+
+ /**
+ * Closes any overlays.
+ */
+ public void closeOverlay() {
+ Optional.ofNullable(getTaskbarController()).ifPresent(
+ TaskbarUIController::hideOverlayWindow);
+ }
+
+ public void switchRunningTaskViewToScreenshot(HashMap<Integer, ThumbnailData> thumbnailDatas,
+ Runnable runnable) {
+ RecentsWindowManager windowManager = getCreatedContainer();
+ if (windowManager == null) {
+ return;
+ }
+ RecentsView recentsView = windowManager.getOverviewPanel();
+ if (recentsView == null) {
+ if (runnable != null) {
+ runnable.run();
+ }
+ return;
+ }
+ recentsView.switchToScreenshot(thumbnailDatas, runnable);
+ }
+
+ /**
+ * todo: Create an abstract animation factory to handle both activity and window implementations
+ * todo: move new factory into BaseContainerInterface and cleanup.
+ */
+
+ class DefaultAnimationFactory implements AnimationFactory {
+
+ protected final RecentsWindowManager mRecentsWindowManager;
+ private final RecentsState mStartState;
+ private final Consumer<AnimatorControllerWithResistance> mCallback;
+
+ private boolean mIsAttachedToWindow;
+ private boolean mHasEverAttachedToWindow;
+
+ DefaultAnimationFactory(Consumer<AnimatorControllerWithResistance> callback) {
+ mCallback = callback;
+
+ mRecentsWindowManager = getCreatedContainer();
+ mStartState = mRecentsWindowManager.getStateManager().getState();
+ }
+
+ protected RecentsWindowManager initBackgroundStateUI() {
+ RecentsState resetState = mStartState;
+ if (mStartState.shouldDisableRestore()) {
+ resetState = mRecentsWindowManager.getStateManager().getRestState();
+ }
+ mRecentsWindowManager.getStateManager().setRestState(resetState);
+ mRecentsWindowManager.getStateManager().goToState(mBackgroundState, false);
+ onInitBackgroundStateUI();
+ return mRecentsWindowManager;
+ }
+
+ @Override
+ public void createContainerInterface(long transitionLength) {
+ PendingAnimation pa = new PendingAnimation(transitionLength * 2);
+ createBackgroundToOverviewAnim(mRecentsWindowManager, pa);
+ AnimatorPlaybackController controller = pa.createPlaybackController();
+ mRecentsWindowManager.getStateManager().setCurrentUserControlledAnimation(controller);
+
+ // Since we are changing the start position of the UI, reapply the state, at the end
+ controller.setEndAction(() -> {
+ mRecentsWindowManager.getStateManager().goToState(
+ controller.getInterpolatedProgress() > 0.5 ? mTargetState
+ : mBackgroundState,
+ /* animated= */ false);
+ });
+
+ RecentsView recentsView = mRecentsWindowManager.getOverviewPanel();
+ AnimatorControllerWithResistance controllerWithResistance =
+ AnimatorControllerWithResistance.createForRecents(controller,
+ mRecentsWindowManager, recentsView.getPagedViewOrientedState(),
+ mRecentsWindowManager.getDeviceProfile(), recentsView,
+ RECENTS_SCALE_PROPERTY, recentsView, TASK_SECONDARY_TRANSLATION);
+ mCallback.accept(controllerWithResistance);
+
+ // Creating the activity controller animation sometimes reapplies the launcher state
+ // (because we set the animation as the current state animation), so we reapply the
+ // attached state here as well to ensure recents is shown/hidden appropriately.
+ if (DisplayController.getNavigationMode(mRecentsWindowManager)
+ == NavigationMode.NO_BUTTON) {
+ setRecentsAttachedToAppWindow(mIsAttachedToWindow, false, false);
+ }
+ }
+
+ @Override
+ public void setRecentsAttachedToAppWindow(boolean attached, boolean animate,
+ boolean updateRunningTaskAlpha) {
+
+ if (mIsAttachedToWindow == attached && animate) {
+ return;
+ }
+ mRecentsWindowManager.getStateManager()
+ .cancelStateElementAnimation(INDEX_RECENTS_FADE_ANIM);
+ mRecentsWindowManager.getStateManager()
+ .cancelStateElementAnimation(INDEX_RECENTS_TRANSLATE_X_ANIM);
+
+ AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
+ mIsAttachedToWindow = attached;
+ if (attached) {
+ mHasEverAttachedToWindow = true;
+ }
+ }});
+
+ long animationDuration = animate ? RECENTS_ATTACH_DURATION : 0;
+ Animator fadeAnim = mRecentsWindowManager.getStateManager()
+ .createStateElementAnimation(INDEX_RECENTS_FADE_ANIM, attached ? 1 : 0);
+ fadeAnim.setInterpolator(attached ? INSTANT : ACCELERATE_2);
+ fadeAnim.setDuration(animationDuration);
+ animatorSet.play(fadeAnim);
+
+ float fromTranslation = ADJACENT_PAGE_HORIZONTAL_OFFSET.get(
+ mRecentsWindowManager.getOverviewPanel());
+ float toTranslation = attached ? 0 : 1;
+
+ Animator translationAnimator =
+ mRecentsWindowManager.getStateManager().createStateElementAnimation(
+ INDEX_RECENTS_TRANSLATE_X_ANIM, fromTranslation, toTranslation);
+ translationAnimator.setDuration(animationDuration);
+ animatorSet.play(translationAnimator);
+ animatorSet.start();
+ }
+
+ @Override
+ public boolean isRecentsAttachedToAppWindow() {
+ return mIsAttachedToWindow;
+ }
+
+ @Override
+ public boolean hasRecentsEverAttachedToAppWindow() {
+ return mHasEverAttachedToWindow;
+ }
+
+ @Override
+ public void setEndTarget(GestureState.GestureEndTarget endTarget) {
+ mTargetState = stateFromGestureEndTarget(endTarget);
+ }
+
+ protected void createBackgroundToOverviewAnim(RecentsWindowManager container,
+ PendingAnimation pa) {
+ // Scale down recents from being full screen to being in overview.
+ RecentsView recentsView = container.getOverviewPanel();
+ pa.addFloat(recentsView, RECENTS_SCALE_PROPERTY,
+ recentsView.getMaxScaleForFullScreen(), 1, LINEAR);
+ pa.addFloat(recentsView, FULLSCREEN_PROGRESS, 1, 0, LINEAR);
+
+ pa.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ TaskbarUIController taskbarUIController = getTaskbarController();
+ if (taskbarUIController != null) {
+ taskbarUIController.setSystemGestureInProgress(true);
+ }
+ }
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
index 9b66154..7abcfb8 100644
--- a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -64,6 +64,7 @@
import com.android.launcher3.util.DisplayController;
import com.android.quickstep.fallback.FallbackRecentsView;
import com.android.quickstep.fallback.RecentsState;
+import com.android.quickstep.fallback.window.RecentsWindowManager;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
@@ -82,7 +83,7 @@
* Handles the navigation gestures when a 3rd party launcher is the default home activity.
*/
public class FallbackSwipeHandler extends
- AbsSwipeUpHandler<RecentsActivity, FallbackRecentsView, RecentsState> {
+ AbsSwipeUpHandler<RecentsActivity, FallbackRecentsView<RecentsActivity>, RecentsState> {
private static final String TAG = "FallbackSwipeHandler";
@@ -105,7 +106,7 @@
TaskAnimationManager taskAnimationManager, GestureState gestureState, long touchTimeMs,
boolean continuingLastGesture, InputConsumerController inputConsumer) {
super(context, deviceState, taskAnimationManager, gestureState, touchTimeMs,
- continuingLastGesture, inputConsumer);
+ continuingLastGesture, inputConsumer, null);
mRunningOverHome = mGestureState.getRunningTask() != null
&& mGestureState.getRunningTask().isHomeTask();
diff --git a/quickstep/src/com/android/quickstep/FallbackWindowInterface.java b/quickstep/src/com/android/quickstep/FallbackWindowInterface.java
new file mode 100644
index 0000000..ea478dd
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/FallbackWindowInterface.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep;
+
+import static com.android.launcher3.util.NavigationMode.NO_BUTTON;
+import static com.android.quickstep.fallback.RecentsState.BACKGROUND_APP;
+import static com.android.quickstep.fallback.RecentsState.DEFAULT;
+import static com.android.quickstep.fallback.RecentsState.HOME;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.content.Context;
+import android.graphics.Rect;
+import android.view.MotionEvent;
+import android.view.RemoteAnimationTarget;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.taskbar.FallbackTaskbarUIController;
+import com.android.launcher3.util.DisplayController;
+import com.android.quickstep.GestureState.GestureEndTarget;
+import com.android.quickstep.fallback.RecentsState;
+import com.android.quickstep.fallback.window.RecentsWindowManager;
+import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
+import com.android.quickstep.util.ActivityInitListener;
+import com.android.quickstep.util.AnimatorControllerWithResistance;
+import com.android.quickstep.views.RecentsView;
+
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+/**
+ * {@link BaseWindowInterface} for recents when the default launcher is different than the
+ * currently running one and apps should interact with the {@link RecentsWindowManager} as opposed
+ * to the in-launcher one.
+ */
+public final class FallbackWindowInterface extends BaseWindowInterface{
+
+ private static FallbackWindowInterface INSTANCE;
+
+ private final RecentsWindowManager mRecentsWindowManager;
+
+ /**
+ * This is only null before init() or after destroy()
+ */
+ @Nullable
+ public static FallbackWindowInterface getInstance(){
+ return INSTANCE;
+ }
+
+ public static void init(RecentsWindowManager recentsWindowManager) {
+ if (INSTANCE == null) {
+ INSTANCE = new FallbackWindowInterface(recentsWindowManager);
+ }
+ }
+
+ private FallbackWindowInterface(RecentsWindowManager recentsWindowManager) {
+ super(DEFAULT, BACKGROUND_APP);
+ mRecentsWindowManager = recentsWindowManager;
+ }
+
+ public void destroy() {
+ INSTANCE = null;
+ }
+
+ /** 2 */
+ @Override
+ public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect,
+ RecentsPagedOrientationHandler orientationHandler) {
+ calculateTaskSize(context, dp, outRect, orientationHandler);
+ if (dp.isVerticalBarLayout() && DisplayController.getNavigationMode(context) != NO_BUTTON) {
+ return dp.isSeascape() ? outRect.left : (dp.widthPx - outRect.right);
+ } else {
+ return dp.heightPx - outRect.bottom;
+ }
+ }
+
+ /** 5 */
+ @Override
+ public void onAssistantVisibilityChanged(float visibility) {
+ // This class becomes active when the screen is locked.
+ // Rather than having it handle assistant visibility changes, the assistant visibility is
+ // set to zero prior to this class becoming active.
+ }
+
+ /** 6 */
+ @Override
+ public BaseWindowInterface.AnimationFactory prepareRecentsUI(RecentsAnimationDeviceState
+ deviceState, boolean activityVisible,
+ Consumer<AnimatorControllerWithResistance> callback) {
+ notifyRecentsOfOrientation(deviceState.getRotationTouchHelper());
+ BaseWindowInterface.DefaultAnimationFactory factory =
+ new BaseWindowInterface.DefaultAnimationFactory(callback);
+ factory.initBackgroundStateUI();
+ return factory;
+ }
+
+ @Override
+ public ActivityInitListener createActivityInitListener(
+ Predicate<Boolean> onInitListener) {
+ //todo figure out how to properly replace this
+ return new ActivityInitListener<>((activity, alreadyOnHome) ->
+ onInitListener.test(alreadyOnHome), RecentsActivity.ACTIVITY_TRACKER);
+ }
+
+ @Nullable
+ @Override
+ public RecentsWindowManager getCreatedContainer() {
+ return mRecentsWindowManager;
+ }
+
+ @Override
+ public FallbackTaskbarUIController getTaskbarController() {
+ RecentsWindowManager manager = getCreatedContainer();
+ if (manager == null) {
+ return null;
+ }
+ return null;
+ // todo b/365775636: pass a taskbar implementation
+ // return manager.getTaskbarUIController();
+ }
+
+ @Override
+ public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTarget target) {
+ // TODO: Remove this once b/77875376 is fixed
+ return target.screenSpaceBounds;
+ }
+
+ @Nullable
+ @Override
+ public <T extends RecentsView<?, ?>> T getVisibleRecentsView() {
+ RecentsWindowManager manager = getCreatedContainer();
+ if(manager.isStarted() || isInLiveTileMode()){
+ return getCreatedContainer().getOverviewPanel();
+ }
+ return null;
+ }
+
+ @Override
+ public boolean switchToRecentsIfVisible(Animator.AnimatorListener animatorListener) {
+ return false;
+ }
+
+ @Override
+ protected int getOverviewScrimColorForState(RecentsWindowManager container,
+ RecentsState state) {
+ return state.getScrimColor(container.asContext());
+ }
+
+ @Override
+ public boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
+ // In non-gesture mode, user might be clicking on the home button which would directly
+ // start the home activity instead of going through recents. In that case, defer starting
+ // recents until we are sure it is a gesture.
+ return false;
+// return !deviceState.isFullyGesturalNavMode();
+// || super.deferStartingActivity(deviceState, ev);
+ }
+
+ @Override
+ public void onExitOverview(RotationTouchHelper deviceState, Runnable exitRunnable) {
+ final StateManager<RecentsState, RecentsWindowManager> stateManager =
+ getCreatedContainer().getStateManager();
+ if (stateManager.getState() == HOME) {
+ exitRunnable.run();
+ notifyRecentsOfOrientation(deviceState);
+ return;
+ }
+
+ stateManager.addStateListener(
+ new StateManager.StateListener<RecentsState>() {
+ @Override
+ public void onStateTransitionComplete(RecentsState toState) {
+ // Are we going from Recents to Workspace?
+ if (toState == HOME) {
+ exitRunnable.run();
+ notifyRecentsOfOrientation(deviceState);
+ stateManager.removeStateListener(this);
+ }
+ }
+ });
+ }
+
+ @Override
+ public boolean isInLiveTileMode() {
+ RecentsWindowManager windowManager = getCreatedContainer();
+ return windowManager != null && windowManager.getStateManager().getState() == DEFAULT &&
+ windowManager.isStarted();
+ }
+
+ @Override
+ public void onLaunchTaskFailed() {
+ // TODO: probably go back to overview instead.
+ RecentsWindowManager manager = getCreatedContainer();
+ if (manager == null) {
+ return;
+ }
+ manager.<RecentsView>getOverviewPanel().startHome();
+ }
+
+ @Override
+ public RecentsState stateFromGestureEndTarget(GestureEndTarget endTarget) {
+ switch (endTarget) {
+ case RECENTS:
+ return DEFAULT;
+ case NEW_TASK:
+ case LAST_TASK:
+ return BACKGROUND_APP;
+ case HOME:
+ case ALL_APPS:
+ default:
+ return HOME;
+ }
+ }
+
+ private void notifyRecentsOfOrientation(RotationTouchHelper rotationTouchHelper) {
+ // reset layout on swipe to home
+ RecentsView recentsView = getCreatedContainer().getOverviewPanel();
+ recentsView.setLayoutRotation(rotationTouchHelper.getCurrentActiveRotation(),
+ rotationTouchHelper.getDisplayRotation());
+ }
+
+ @Override
+ public @Nullable Animator getParallelAnimationToLauncher(GestureEndTarget endTarget,
+ long duration, RecentsAnimationCallbacks callbacks) {
+ FallbackTaskbarUIController uiController = getTaskbarController();
+ Animator superAnimator = super.getParallelAnimationToLauncher(
+ endTarget, duration, callbacks);
+ if (uiController == null) {
+ return superAnimator;
+ }
+ RecentsState toState = stateFromGestureEndTarget(endTarget);
+ Animator taskbarAnimator = uiController.createAnimToRecentsState(toState, duration);
+ if (taskbarAnimator == null) {
+ return superAnimator;
+ }
+ if (superAnimator == null) {
+ return taskbarAnimator;
+ }
+ AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet.playTogether(superAnimator, taskbarAnimator);
+ return animatorSet;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index 9cc463a..2892d2c 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -24,7 +24,6 @@
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
-import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET_ALL_APPS;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET_HOME;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET_NEW_TASK;
@@ -38,9 +37,11 @@
import androidx.annotation.Nullable;
import com.android.launcher3.statemanager.BaseState;
+import com.android.launcher3.statemanager.StatefulContainer;
import com.android.quickstep.TopTaskTracker.CachedTaskInfo;
import com.android.quickstep.util.ActiveGestureErrorDetector;
import com.android.quickstep.util.ActiveGestureLog;
+import com.android.quickstep.util.ActiveGestureProtoLogProxy;
import com.android.quickstep.views.RecentsViewContainer;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -190,7 +191,7 @@
public GestureState(OverviewComponentObserver componentObserver, int gestureId) {
mHomeIntent = componentObserver.getHomeIntent();
mOverviewIntent = componentObserver.getOverviewIntent();
- mContainerInterface = componentObserver.getActivityInterface();
+ mContainerInterface = componentObserver.getContainerInterface();
mStateCallback = new MultiStateCallback(
STATE_NAMES.toArray(new String[0]), GestureState::getTrackedEventForState);
mGestureId = gestureId;
@@ -269,8 +270,8 @@
/**
* @return the interface to the activity handing the UI updates for this gesture.
*/
- public <S extends BaseState<S>, T extends RecentsViewContainer>
- BaseContainerInterface<S, T> getContainerInterface() {
+ public <S extends BaseState<S>, T extends RecentsViewContainer & StatefulContainer<S>>
+ BaseContainerInterface getContainerInterface() {
return mContainerInterface;
}
@@ -410,10 +411,7 @@
public void setEndTarget(GestureEndTarget target, boolean isAtomic) {
mEndTarget = target;
mStateCallback.setState(STATE_END_TARGET_SET);
- ActiveGestureLog.INSTANCE.addLog(
- new ActiveGestureLog.CompoundString("setEndTarget ")
- .append(mEndTarget.name()),
- /* gestureEvent= */ SET_END_TARGET);
+ ActiveGestureProtoLogProxy.logSetEndTarget(mEndTarget.name());
switch (mEndTarget) {
case HOME:
ActiveGestureLog.INSTANCE.trackEvent(SET_END_TARGET_HOME);
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index 5308436..85312e4 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -76,7 +76,7 @@
&& DisplayController.getNavigationMode(context) != NavigationMode.NO_BUTTON) {
return dp.isSeascape() ? outRect.left : (dp.widthPx - outRect.right);
} else {
- return LayoutUtils.getShelfTrackingDistance(context, dp, orientationHandler);
+ return LayoutUtils.getShelfTrackingDistance(context, dp, orientationHandler, this);
}
}
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
index 360c216..1124aac 100644
--- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -27,7 +27,6 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.content.ComponentCallbacks;
import android.content.res.Configuration;
@@ -38,7 +37,6 @@
import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
-import android.util.Pair;
import android.view.Choreographer;
import android.view.IRemoteAnimationFinishedCallback;
import android.view.IRemoteAnimationRunner;
@@ -63,7 +61,7 @@
import com.android.launcher3.taskbar.LauncherTaskbarUIController;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
-import com.android.quickstep.util.RectFSpringAnim;
+import com.android.quickstep.util.BackAnimState;
import com.android.systemui.shared.system.QuickStepContract;
import java.lang.ref.WeakReference;
@@ -109,8 +107,6 @@
private RemoteAnimationTarget mLauncherTarget;
private View mLauncherTargetView;
private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
- private boolean mSpringAnimationInProgress = false;
- private boolean mAnimatorSetInProgress = false;
private float mBackProgress = 0;
private boolean mBackInProgress = false;
private OnBackInvokedCallbackStub mBackCallback;
@@ -448,14 +444,15 @@
mQuickstepTransitionManager.transferRectToTargetCoordinate(
mBackTarget, mCurrentRect, true, resolveRectF);
- Pair<RectFSpringAnim, AnimatorSet> pair =
+ BackAnimState backAnim =
mQuickstepTransitionManager.createWallpaperOpenAnimations(
new RemoteAnimationTarget[]{mBackTarget},
new RemoteAnimationTarget[0],
+ new RemoteAnimationTarget[0],
resolveRectF,
cornerRadius,
mBackInProgress /* fromPredictiveBack */);
- startTransitionAnimations(pair.first, pair.second);
+ startTransitionAnimations(backAnim);
mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL);
customizeStatusBarAppearance(true);
}
@@ -470,8 +467,6 @@
mCurrentRect.setEmpty();
mStartRect.setEmpty();
mInitialTouchPos.set(0, 0);
- mAnimatorSetInProgress = false;
- mSpringAnimationInProgress = false;
setLauncherTargetViewVisible(true);
mLauncherTargetView = null;
// We don't call customizeStatusBarAppearance here to prevent the status bar update with
@@ -494,27 +489,8 @@
}
}
- private void startTransitionAnimations(RectFSpringAnim springAnim, AnimatorSet anim) {
- mAnimatorSetInProgress = anim != null;
- mSpringAnimationInProgress = springAnim != null;
- if (springAnim != null) {
- springAnim.addAnimatorListener(
- new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mSpringAnimationInProgress = false;
- tryFinishBackAnimation();
- }
- }
- );
- }
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mAnimatorSetInProgress = false;
- tryFinishBackAnimation();
- }
- });
+ private void startTransitionAnimations(BackAnimState backAnim) {
+ backAnim.addOnAnimCompleteCallback(this::finishAnimation);
if (mScrimLayer == null) {
// Scrim hasn't been attached yet. Let's attach it.
addScrimLayer();
@@ -534,7 +510,7 @@
}
});
mScrimAlphaAnimator.setDuration(SCRIM_FADE_DURATION).start();
- anim.start();
+ backAnim.start();
}
private void loadResources() {
@@ -567,12 +543,6 @@
mScrimAlpha = 0;
}
- private void tryFinishBackAnimation() {
- if (!mSpringAnimationInProgress && !mAnimatorSetInProgress) {
- finishAnimation();
- }
- }
-
private void customizeStatusBarAppearance(boolean overridingStatusBarFlags) {
if (mOverridingStatusBarFlags == overridingStatusBarFlags) {
return;
diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index d2dcd7b..dacafd4 100644
--- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -46,6 +46,7 @@
import com.android.launcher3.views.FloatingIconView;
import com.android.launcher3.views.FloatingView;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
+import com.android.quickstep.fallback.window.RecentsWindowManager;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.ScalingWorkspaceRevealAnim;
import com.android.quickstep.util.StaggeredWorkspaceAnim;
@@ -68,7 +69,7 @@
TaskAnimationManager taskAnimationManager, GestureState gestureState, long touchTimeMs,
boolean continuingLastGesture, InputConsumerController inputConsumer) {
super(context, deviceState, taskAnimationManager, gestureState, touchTimeMs,
- continuingLastGesture, inputConsumer);
+ continuingLastGesture, inputConsumer, null);
}
diff --git a/quickstep/src/com/android/quickstep/MultiStateCallback.java b/quickstep/src/com/android/quickstep/MultiStateCallback.java
index df42efc..a9f196d 100644
--- a/quickstep/src/com/android/quickstep/MultiStateCallback.java
+++ b/quickstep/src/com/android/quickstep/MultiStateCallback.java
@@ -28,6 +28,7 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.quickstep.util.ActiveGestureErrorDetector;
import com.android.quickstep.util.ActiveGestureLog;
+import com.android.quickstep.util.ActiveGestureProtoLogProxy;
import java.util.ArrayList;
import java.util.LinkedList;
@@ -114,10 +115,9 @@
if (gestureEvent == null) {
continue;
}
- if (gestureEvent.mLogEvent && gestureEvent.mTrackEvent) {
- ActiveGestureLog.INSTANCE.addLog(gestureEvent.name(), gestureEvent);
- } else if (gestureEvent.mLogEvent) {
- ActiveGestureLog.INSTANCE.addLog(gestureEvent.name());
+ if (gestureEvent.mLogEvent) {
+ ActiveGestureProtoLogProxy.logDynamicString(
+ gestureEvent.name(), gestureEvent.mTrackEvent ? gestureEvent : null);
} else if (gestureEvent.mTrackEvent) {
ActiveGestureLog.INSTANCE.trackEvent(gestureEvent);
}
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
index c1f9963..a33e5c0 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
@@ -29,7 +29,6 @@
import com.android.internal.jank.Cuj
import com.android.launcher3.Flags.enableOverviewCommandHelperTimeout
import com.android.launcher3.PagedView
-import com.android.launcher3.config.FeatureFlags
import com.android.launcher3.logger.LauncherAtom
import com.android.launcher3.logging.StatsLogManager
import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_3_BUTTON
@@ -46,8 +45,8 @@
import com.android.quickstep.OverviewCommandHelper.CommandType.SHOW
import com.android.quickstep.OverviewCommandHelper.CommandType.TOGGLE
import com.android.quickstep.util.ActiveGestureLog
+import com.android.quickstep.util.ActiveGestureProtoLogProxy
import com.android.quickstep.views.RecentsView
-import com.android.quickstep.views.RecentsViewContainer
import com.android.quickstep.views.TaskView
import com.android.systemui.shared.recents.model.ThumbnailData
import com.android.systemui.shared.system.InteractionJankMonitorWrapper
@@ -81,19 +80,11 @@
*/
private var keyboardTaskFocusIndex = -1
- /**
- * Whether we should incoming toggle commands while a previous toggle command is still ongoing.
- * This serves as a rate-limiter to prevent overlapping animations that can clobber each other
- * and prevent clean-up callbacks from running. This thus prevents a recurring set of bugs with
- * janky recents animations and unresponsive home and overview buttons.
- */
- private var waitForToggleCommandComplete = false
-
- private val activityInterface: BaseActivityInterface<*, *>
- get() = overviewComponentObserver.activityInterface
+ private val containerInterface: BaseContainerInterface<*, *>
+ get() = overviewComponentObserver.containerInterface
private val visibleRecentsView: RecentsView<*, *>?
- get() = activityInterface.getVisibleRecentsView<RecentsView<*, *>>()
+ get() = containerInterface.getVisibleRecentsView<RecentsView<*, *>>()
/**
* Adds a command to be executed next, after all pending tasks are completed. Max commands that
@@ -175,12 +166,6 @@
*/
@VisibleForTesting
fun executeCommand(command: CommandInfo, onCallbackResult: () -> Unit): Boolean {
- // This shouldn't happen if we execute 1 command per time.
- if (waitForToggleCommandComplete && command.type == TOGGLE) {
- Log.d(TAG, "executeCommand: $command - waiting for toggle command complete")
- return true
- }
-
val recentsView = visibleRecentsView
Log.d(TAG, "executeCommand: $command - visibleRecentsView: $recentsView")
return if (recentsView != null) {
@@ -248,11 +233,10 @@
recents: RecentsView<*, *>,
taskView: TaskView?,
command: CommandInfo,
- onCallbackResult: () -> Unit
+ onCallbackResult: () -> Unit,
): Boolean {
var callbackList: RunnableList? = null
if (taskView != null) {
- waitForToggleCommandComplete = true
taskView.isEndQuickSwitchCuj = true
callbackList = taskView.launchWithAnimation()
}
@@ -261,28 +245,25 @@
callbackList.add {
Log.d(TAG, "launching task callback: $command")
onCallbackResult()
- waitForToggleCommandComplete = false
}
Log.d(TAG, "launching task - waiting for callback: $command")
return false
} else {
recents.startHome()
- waitForToggleCommandComplete = false
return true
}
}
private fun executeWhenRecentsIsNotVisible(
command: CommandInfo,
- onCallbackResult: () -> Unit
+ onCallbackResult: () -> Unit,
): Boolean {
- val recentsViewContainer = activityInterface.getCreatedContainer() as? RecentsViewContainer
+ val recentsViewContainer = containerInterface.getCreatedContainer()
val recentsView: RecentsView<*, *>? = recentsViewContainer?.getOverviewPanel()
val deviceProfile = recentsViewContainer?.getDeviceProfile()
- val uiController = activityInterface.getTaskbarController()
+ val uiController = containerInterface.getTaskbarController()
val allowQuickSwitch =
- FeatureFlags.ENABLE_KEYBOARD_QUICK_SWITCH.get() &&
- uiController != null &&
+ uiController != null &&
deviceProfile != null &&
(deviceProfile.isTablet || deviceProfile.isTwoPanels)
@@ -300,7 +281,7 @@
keyboardTaskFocusIndex = 0
}
HOME -> {
- ActiveGestureLog.INSTANCE.addLog("OverviewCommandHelper.executeCommand(HOME)")
+ ActiveGestureProtoLogProxy.logExecuteHomeCommand()
// Although IActivityTaskManager$Stub$Proxy.startActivity is a slow binder call,
// we should still call it on main thread because launcher is waiting for
// ActivityTaskManager to resume it. Also calling startActivity() on bg thread
@@ -335,13 +316,13 @@
onCallbackResult()
}
}
- if (activityInterface.switchToRecentsIfVisible(animatorListener)) {
+ if (containerInterface.switchToRecentsIfVisible(animatorListener)) {
Log.d(TAG, "switching to Overview state - waiting: $command")
// If successfully switched, wait until animation finishes
return false
}
- val activity = activityInterface.getCreatedContainer()
+ val activity = containerInterface.getCreatedContainer()
if (activity != null) {
InteractionJankMonitorWrapper.begin(activity.rootView, Cuj.CUJ_LAUNCHER_QUICK_SWITCH)
}
@@ -349,13 +330,13 @@
val gestureState =
touchInteractionService.createGestureState(
GestureState.DEFAULT_STATE,
- GestureState.TrackpadGestureType.NONE
+ GestureState.TrackpadGestureType.NONE,
)
gestureState.isHandlingAtomicEvent = true
val interactionHandler =
touchInteractionService.swipeUpHandlerFactory.newHandler(
gestureState,
- command.createTime
+ command.createTime,
)
interactionHandler.setGestureEndCallback {
onTransitionComplete(command, interactionHandler, onCallbackResult)
@@ -366,12 +347,12 @@
object : RecentsAnimationCallbacks.RecentsAnimationListener {
override fun onRecentsAnimationStart(
controller: RecentsAnimationController,
- targets: RecentsAnimationTargets
+ targets: RecentsAnimationTargets,
) {
Log.d(TAG, "recents animation started: $command")
updateRecentsViewFocus(command)
logShowOverviewFrom(command.type)
- activityInterface.runOnInitBackgroundStateUI {
+ containerInterface.runOnInitBackgroundStateUI {
Log.d(TAG, "recents animation started - onInitBackgroundStateUI: $command")
interactionHandler.onGestureEnded(0f, PointF())
}
@@ -385,7 +366,7 @@
interactionHandler.onGestureCancelled()
command.removeListener(this)
- activityInterface.getCreatedContainer() ?: return
+ containerInterface.getCreatedContainer() ?: return
recentsView?.onRecentsAnimationComplete()
}
}
@@ -418,7 +399,7 @@
private fun onTransitionComplete(
command: CommandInfo,
handler: AbsSwipeUpHandler<*, *, *>,
- onCommandResult: () -> Unit
+ onCommandResult: () -> Unit,
) {
Log.d(TAG, "switching via recents animation - onTransitionComplete: $command")
command.removeListener(handler)
@@ -434,7 +415,7 @@
Log.d(
TAG,
"next task not scheduled. First pending command type " +
- "is ${commandQueue.firstOrNull()} - command type is: $command"
+ "is ${commandQueue.firstOrNull()} - command type is: $command",
)
return
}
@@ -492,7 +473,7 @@
}
private fun logShowOverviewFrom(commandType: CommandType) {
- val container = activityInterface.getCreatedContainer() as? RecentsViewContainer ?: return
+ val container = containerInterface.getCreatedContainer() ?: return
val event =
when (commandType) {
SHOW -> LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_SHORTCUT
@@ -519,7 +500,6 @@
pw.println(" pendingCommandType=${commandQueue.first().type}")
}
pw.println(" keyboardTaskFocusIndex=$keyboardTaskFocusIndex")
- pw.println(" waitForToggleCommandComplete=$waitForToggleCommandComplete")
}
@VisibleForTesting
@@ -527,7 +507,7 @@
val type: CommandType,
var status: CommandStatus = CommandStatus.IDLE,
val createTime: Long = SystemClock.elapsedRealtime(),
- private var animationCallbacks: RecentsAnimationCallbacks? = null
+ private var animationCallbacks: RecentsAnimationCallbacks? = null,
) {
fun setAnimationCallbacks(recentsAnimationCallbacks: RecentsAnimationCallbacks) {
this.animationCallbacks = recentsAnimationCallbacks
@@ -545,7 +525,7 @@
IDLE,
PROCESSING,
COMPLETED,
- CANCELED
+ CANCELED,
}
}
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index ca19480..66112c1 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -39,9 +39,10 @@
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
+import com.android.launcher3.Flags;
import com.android.launcher3.R;
import com.android.launcher3.util.SimpleBroadcastReceiver;
-import com.android.quickstep.util.ActiveGestureLog;
+import com.android.quickstep.util.ActiveGestureProtoLogProxy;
import com.android.systemui.shared.system.PackageManagerWrapper;
import java.io.PrintWriter;
@@ -73,7 +74,7 @@
private Consumer<Boolean> mOverviewChangeListener = b -> { };
private String mUpdateRegisteredPackage;
- private BaseActivityInterface mActivityInterface;
+ private BaseContainerInterface mContainerInterface;
private Intent mOverviewIntent;
private boolean mIsHomeAndOverviewSame;
private boolean mIsDefaultHome;
@@ -150,8 +151,8 @@
// 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 (mActivityInterface != null) {
- mActivityInterface.onAssistantVisibilityChanged(0.f);
+ if (mContainerInterface != null) {
+ mContainerInterface.onAssistantVisibilityChanged(0.f);
}
if (SEPARATE_RECENTS_ACTIVITY.get()) {
@@ -168,7 +169,7 @@
if (!mIsHomeDisabled && (defaultHome == null || mIsDefaultHome)) {
// User default home is same as out home app. Use Overview integrated in Launcher.
- mActivityInterface = LauncherActivityInterface.INSTANCE;
+ mContainerInterface = LauncherActivityInterface.INSTANCE;
mIsHomeAndOverviewSame = true;
mOverviewIntent = mMyHomeIntent;
mCurrentHomeIntent.setComponent(mMyHomeIntent.getComponent());
@@ -178,7 +179,11 @@
} else {
// The default home app is a different launcher. Use the fallback Overview instead.
- mActivityInterface = FallbackActivityInterface.INSTANCE;
+ if (Flags.enableFallbackOverviewInWindow()) {
+ mContainerInterface = FallbackWindowInterface.getInstance();
+ } else {
+ mContainerInterface = FallbackActivityInterface.INSTANCE;
+ }
mIsHomeAndOverviewSame = false;
mOverviewIntent = mFallbackIntent;
mCurrentHomeIntent.setComponent(defaultHome);
@@ -266,21 +271,12 @@
}
/**
- * Get the current activity control helper for managing interactions to the overview activity.
+ * Get the current control helper for managing interactions to the overview container.
*
- * @return the current activity control helper
+ * @return the current control helper
*/
- public BaseActivityInterface getActivityInterface() {
- return mActivityInterface;
- }
-
- /**
- * Get the current container control helper for managing interactions to the overview activity.
- *
- * @return the current container control helper
- */
- public BaseContainerInterface<?, ?> getContainerInterface() {
- return mActivityInterface;
+ public BaseContainerInterface<?,?> getContainerInterface() {
+ return mContainerInterface;
}
public void dump(PrintWriter pw) {
@@ -309,10 +305,11 @@
* Starts the intent for the current home activity.
*/
public static void startHomeIntentSafely(
- @NonNull Context context, @NonNull Intent homeIntent, @Nullable Bundle options,
+ @NonNull Context context,
+ @NonNull Intent homeIntent,
+ @Nullable Bundle options,
@NonNull String reason) {
- ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
- "OverviewComponentObserver.startHomeIntent: ").append(reason));
+ ActiveGestureProtoLogProxy.logStartHomeIntent(reason);
try {
context.startActivity(homeIntent, options);
} catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
diff --git a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
index f4e68dc..334bead 100644
--- a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
+++ b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
@@ -25,6 +25,7 @@
import com.android.launcher3.BuildConfig;
import com.android.launcher3.MainProcessInitializer;
+import com.android.quickstep.util.QuickstepProtoLogGroup;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
@SuppressWarnings("unused")
@@ -69,5 +70,7 @@
call.descriptor + " called on main thread under " + call.activeTrace
+ " stackTrace: " + call.stackTrace));
}
+
+ QuickstepProtoLogGroup.initProtoLog();
}
}
diff --git a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
index 49b6f57..c3b9736 100644
--- a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -7,6 +7,7 @@
import android.content.Context;
import android.content.res.Resources;
import android.os.Bundle;
+import android.view.WindowInsets;
import androidx.annotation.Nullable;
@@ -203,11 +204,12 @@
}
@Override
- protected Activity getCurrentActivity() {
+ protected WindowInsets getWindowInsets() {
RecentsAnimationDeviceState rads = new RecentsAnimationDeviceState(mContext);
OverviewComponentObserver observer = new OverviewComponentObserver(mContext, rads);
try {
- return observer.getActivityInterface().getCreatedContainer();
+ return observer.getContainerInterface()
+ .getCreatedContainer().getRootView().getRootWindowInsets();
} finally {
observer.onDestroy();
rads.destroy();
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 9c60693..9ac4141 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -122,9 +122,6 @@
// Strong refs to runners which are cleared when the activity is destroyed
private RemoteAnimationFactory mActivityLaunchAnimationRunner;
- // For handling degenerate cases where starting an activity doesn't actually trigger the remote
- // animation callback
- private final Handler mHandler = new Handler();
private final Runnable mAnimationStartTimeoutRunnable = this::onAnimationStartTimeout;
private SplitSelectStateController mSplitSelectStateController;
@Nullable
@@ -137,7 +134,7 @@
SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.get(this);
// SplitSelectStateController needs to be created before setContentView()
mSplitSelectStateController =
- new SplitSelectStateController(this, mHandler, getStateManager(),
+ new SplitSelectStateController(this, getStateManager(),
null /* depthController */, getStatsLogManager(),
systemUiProxy, RecentsModel.INSTANCE.get(this),
null /*activityBackCallback*/);
@@ -198,7 +195,7 @@
}
@Override
- protected void onHandleConfigurationChanged() {
+ public void onHandleConfigurationChanged() {
initDeviceProfile();
AbstractFloatingView.closeOpenViews(this, true,
@@ -352,7 +349,6 @@
@Override
protected void onStop() {
super.onStop();
-
// Workaround for b/78520668, explicitly trim memory once UI is hidden
onTrimMemory(TRIM_MEMORY_UI_HIDDEN);
mFallbackRecentsView.updateLocusId();
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
index 0c5806b..fc11812 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -19,9 +19,6 @@
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.ON_CANCEL_RECENTS_ANIMATION;
-import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.ON_FINISH_RECENTS_ANIMATION;
-import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.ON_START_RECENTS_ANIMATION;
import android.graphics.Rect;
import android.os.Bundle;
@@ -34,8 +31,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.util.Preconditions;
-import com.android.quickstep.util.ActiveGestureErrorDetector;
-import com.android.quickstep.util.ActiveGestureLog;
+import com.android.quickstep.util.ActiveGestureProtoLogProxy;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
@@ -107,11 +103,8 @@
.filter(app -> app.mode == MODE_CLOSING)
.count();
if (appCount == 0) {
+ ActiveGestureProtoLogProxy.logOnRecentsAnimationStartCancelled();
// Edge case, if there are no closing app targets, then Launcher has nothing to handle
- ActiveGestureLog.INSTANCE.addLog(
- /* event= */ "RecentsAnimationCallbacks.onAnimationStart (canceled)",
- /* extras= */ 0,
- /* gestureEvent= */ ON_START_RECENTS_ANIMATION);
notifyAnimationCanceled();
animationController.finish(false /* toHome */, false /* sendUserLeaveHint */,
null /* finishCb */);
@@ -138,10 +131,7 @@
extras);
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
- ActiveGestureLog.INSTANCE.addLog(
- /* event= */ "RecentsAnimationCallbacks.onAnimationStart",
- /* extras= */ targets.apps.length,
- /* gestureEvent= */ ON_START_RECENTS_ANIMATION);
+ ActiveGestureProtoLogProxy.logOnRecentsAnimationStart(targets.apps.length);
for (RecentsAnimationListener listener : getListeners()) {
listener.onRecentsAnimationStart(mController, targets);
}
@@ -153,9 +143,7 @@
@Override
public final void onAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas) {
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
- ActiveGestureLog.INSTANCE.addLog(
- /* event= */ "RecentsAnimationCallbacks.onAnimationCanceled",
- /* gestureEvent= */ ON_CANCEL_RECENTS_ANIMATION);
+ ActiveGestureProtoLogProxy.logRecentsAnimationCallbacksOnAnimationCancelled();
for (RecentsAnimationListener listener : getListeners()) {
listener.onRecentsAnimationCanceled(thumbnailDatas);
}
@@ -166,8 +154,7 @@
@Override
public void onTasksAppeared(RemoteAnimationTarget[] apps) {
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
- ActiveGestureLog.INSTANCE.addLog("RecentsAnimationCallbacks.onTasksAppeared",
- ActiveGestureErrorDetector.GestureEvent.TASK_APPEARED);
+ ActiveGestureProtoLogProxy.logRecentsAnimationCallbacksOnTasksAppeared();
for (RecentsAnimationListener listener : getListeners()) {
listener.onTasksAppeared(apps);
}
@@ -176,9 +163,7 @@
private void onAnimationFinished(RecentsAnimationController controller) {
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
- ActiveGestureLog.INSTANCE.addLog(
- /* event= */ "RecentsAnimationCallbacks.onAnimationFinished",
- ON_FINISH_RECENTS_ANIMATION);
+ ActiveGestureProtoLogProxy.logAbsSwipeUpHandlerOnRecentsAnimationFinished();
for (RecentsAnimationListener listener : getListeners()) {
listener.onRecentsAnimationFinished(controller);
}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
index 190d526..dcb0108 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -17,7 +17,6 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.FINISH_RECENTS_ANIMATION;
import android.os.Bundle;
import android.os.RemoteException;
@@ -32,7 +31,7 @@
import com.android.internal.os.IResultReceiver;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.RunnableList;
-import com.android.quickstep.util.ActiveGestureLog;
+import com.android.quickstep.util.ActiveGestureProtoLogProxy;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
@@ -132,10 +131,7 @@
// trigger the callback to be called immediately
return;
}
- ActiveGestureLog.INSTANCE.addLog(
- /* event= */ "finishRecentsAnimation",
- /* extras= */ toRecents,
- /* gestureEvent= */ FINISH_RECENTS_ANIMATION);
+ ActiveGestureProtoLogProxy.logFinishRecentsAnimation(toRecents);
// Finish not yet requested
mFinishRequested = true;
mFinishTargetIsLauncher = toRecents;
@@ -144,7 +140,7 @@
mController.finish(toRecents, sendUserLeaveHint, new IResultReceiver.Stub() {
@Override
public void send(int i, Bundle bundle) throws RemoteException {
- ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation-callback");
+ ActiveGestureProtoLogProxy.logFinishRecentsAnimationCallback();
MAIN_EXECUTOR.execute(() -> {
mPendingFinishCallbacks.executeAllAndDestroy();
});
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index a01ceb2..d073580 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -56,6 +56,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.function.Consumer;
@@ -76,7 +77,8 @@
private static final Executor RECENTS_MODEL_EXECUTOR = Executors.newSingleThreadExecutor(
new SimpleThreadFactory("TaskThumbnailIconCache-", THREAD_PRIORITY_BACKGROUND));
- private final List<TaskVisualsChangeListener> mThumbnailChangeListeners = new ArrayList<>();
+ private final ConcurrentLinkedQueue<TaskVisualsChangeListener> mThumbnailChangeListeners =
+ new ConcurrentLinkedQueue<>();
private final Context mContext;
private final RecentTasksList mTaskList;
@@ -239,8 +241,8 @@
public boolean onTaskSnapshotChanged(int taskId, ThumbnailData snapshot) {
mThumbnailCache.updateTaskSnapShot(taskId, snapshot);
- for (int i = mThumbnailChangeListeners.size() - 1; i >= 0; i--) {
- Task task = mThumbnailChangeListeners.get(i).onTaskThumbnailChanged(taskId, snapshot);
+ for (TaskVisualsChangeListener listener : mThumbnailChangeListeners) {
+ Task task = listener.onTaskThumbnailChanged(taskId, snapshot);
if (task != null) {
task.thumbnail = snapshot;
}
@@ -269,8 +271,8 @@
@Override
public void onAppIconChanged(String packageName, UserHandle user) {
mIconCache.invalidateCacheEntries(packageName, user);
- for (int i = mThumbnailChangeListeners.size() - 1; i >= 0; i--) {
- mThumbnailChangeListeners.get(i).onTaskIconChanged(packageName, user);
+ for (TaskVisualsChangeListener listener : mThumbnailChangeListeners) {
+ listener.onTaskIconChanged(packageName, user);
}
}
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index b4bd3e3..34435d5 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -21,7 +21,6 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
-import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.RECENT_TASKS_MISSING;
import static com.android.quickstep.util.LogUtils.splitFailureMessage;
import android.app.ActivityManager;
@@ -51,9 +50,11 @@
import android.window.RemoteTransition;
import android.window.TaskSnapshot;
import android.window.TransitionFilter;
+import android.window.flags.DesktopModeFlags;
import androidx.annotation.MainThread;
import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
import androidx.annotation.WorkerThread;
import com.android.internal.logging.InstanceId;
@@ -62,7 +63,7 @@
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.SafeCloseable;
-import com.android.quickstep.util.ActiveGestureLog;
+import com.android.quickstep.util.ActiveGestureProtoLogProxy;
import com.android.quickstep.util.AssistUtils;
import com.android.quickstep.util.unfold.ProxyUnfoldTransitionProvider;
import com.android.systemui.shared.recents.ISystemUiProxy;
@@ -85,14 +86,13 @@
import com.android.wm.shell.desktopmode.IDesktopTaskListener;
import com.android.wm.shell.draganddrop.IDragAndDrop;
import com.android.wm.shell.onehanded.IOneHanded;
-import com.android.wm.shell.recents.IRecentsAnimationController;
-import com.android.wm.shell.recents.IRecentsAnimationRunner;
import com.android.wm.shell.recents.IRecentTasks;
import com.android.wm.shell.recents.IRecentTasksListener;
+import com.android.wm.shell.recents.IRecentsAnimationController;
+import com.android.wm.shell.recents.IRecentsAnimationRunner;
import com.android.wm.shell.shared.GroupedRecentTaskInfo;
import com.android.wm.shell.shared.IShellTransitions;
import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
-import com.android.wm.shell.shared.desktopmode.DesktopModeFlags;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource;
import com.android.wm.shell.shared.split.SplitBounds;
@@ -187,7 +187,8 @@
@Nullable
private final ProxyUnfoldTransitionProvider mUnfoldTransitionProvider;
- private SystemUiProxy(Context context) {
+ @VisibleForTesting
+ protected SystemUiProxy(Context context) {
mContext = context;
mAsyncHandler = new Handler(UI_HELPER_EXECUTOR.getLooper(), this::handleMessageAsync);
final Intent baseIntent = new Intent().setPackage(mContext.getPackageName());
@@ -1395,7 +1396,7 @@
private boolean shouldEnableRunningTasksForDesktopMode() {
return DesktopModeStatus.canEnterDesktopMode(mContext)
- && DesktopModeFlags.TASKBAR_RUNNING_APPS.isEnabled(mContext);
+ && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TASKBAR_RUNNING_APPS.isTrue();
}
private boolean handleMessageAsync(Message msg) {
@@ -1518,7 +1519,7 @@
public boolean startRecentsActivity(Intent intent, ActivityOptions options,
RecentsAnimationListener listener) {
if (mRecentTasks == null) {
- ActiveGestureLog.INSTANCE.addLog("Null mRecentTasks", RECENT_TASKS_MISSING);
+ ActiveGestureProtoLogProxy.logRecentTasksMissing();
return false;
}
final IRecentsAnimationRunner runner = new IRecentsAnimationRunner.Stub() {
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index 289a2c1..98d7628 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -26,7 +26,6 @@
import static com.android.quickstep.GestureState.STATE_END_TARGET_ANIMATION_FINISHED;
import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_INITIALIZED;
import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_STARTED;
-import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.START_RECENTS_ANIMATION;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
@@ -43,11 +42,13 @@
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.util.ActiveGestureLog;
+import com.android.quickstep.fallback.window.RecentsWindowManager;
+import com.android.quickstep.util.ActiveGestureProtoLogProxy;
import com.android.quickstep.util.SystemUiFlagUtils;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -64,6 +65,7 @@
SystemProperties.getBoolean("persist.wm.debug.shell_transit_rotate", false);
private final Context mCtx;
+ private RecentsWindowManager mRecentsWindowsManager;
private RecentsAnimationController mController;
private RecentsAnimationCallbacks mCallbacks;
private RecentsAnimationTargets mTargets;
@@ -98,10 +100,10 @@
}
};
- TaskAnimationManager(Context ctx) {
+ TaskAnimationManager(Context ctx, RecentsWindowManager manager) {
mCtx = ctx;
+ mRecentsWindowsManager = manager;
}
-
SystemUiProxy getSystemUiProxy() {
return SystemUiProxy.INSTANCE.get(mCtx);
}
@@ -134,9 +136,7 @@
@UiThread
public RecentsAnimationCallbacks startRecentsAnimation(@NonNull GestureState gestureState,
Intent intent, RecentsAnimationCallbacks.RecentsAnimationListener listener) {
- ActiveGestureLog.INSTANCE.addLog(
- /* event= */ "startRecentsAnimation",
- /* gestureEvent= */ START_RECENTS_ANIMATION);
+ ActiveGestureProtoLogProxy.logStartRecentsAnimation();
// Notify if recents animation is still running
if (mController != null) {
String msg = "New recents animation started before old animation completed";
@@ -166,9 +166,8 @@
public void onRecentsAnimationStart(RecentsAnimationController controller,
RecentsAnimationTargets targets) {
if (enableHandleDelayedGestureCallbacks() && mRecentsAnimationStartPending) {
- ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
- "TaskAnimationManager.startRecentsAnimation(onRecentsAnimationStart): ")
- .append("Setting mRecentsAnimationStartPending = false"));
+ ActiveGestureProtoLogProxy.logStartRecentsAnimationCallback(
+ "onRecentsAnimationStart");
mRecentsAnimationStartPending = false;
}
if (mCallbacks == null) {
@@ -212,10 +211,8 @@
@Override
public void onRecentsAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas) {
if (enableHandleDelayedGestureCallbacks() && mRecentsAnimationStartPending) {
- ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
- "TaskAnimationManager.startRecentsAnimation")
- .append("(onRecentsAnimationCanceled): ")
- .append("Setting mRecentsAnimationStartPending = false"));
+ ActiveGestureProtoLogProxy.logStartRecentsAnimationCallback(
+ "onRecentsAnimationCanceled");
mRecentsAnimationStartPending = false;
}
cleanUpRecentsAnimation(newCallbacks);
@@ -224,10 +221,8 @@
@Override
public void onRecentsAnimationFinished(RecentsAnimationController controller) {
if (enableHandleDelayedGestureCallbacks() && mRecentsAnimationStartPending) {
- ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
- "TaskAnimationManager.startRecentsAnimation")
- .append("(onRecentsAnimationFinished): ")
- .append("Setting mRecentsAnimationStartPending = false"));
+ ActiveGestureProtoLogProxy.logStartRecentsAnimationCallback(
+ "onRecentsAnimationFinished");
mRecentsAnimationStartPending = false;
}
cleanUpRecentsAnimation(newCallbacks);
@@ -273,16 +268,14 @@
RecentsView recentsView =
containerInterface.getCreatedContainer().getOverviewPanel();
if (recentsView != null) {
- ActiveGestureLog.INSTANCE.addLog(
- new ActiveGestureLog.CompoundString("Launching side task id=")
- .append(appearedTaskTarget.taskId));
+ ActiveGestureProtoLogProxy.logLaunchingSideTask(appearedTaskTarget.taskId);
recentsView.launchSideTaskInLiveTileMode(appearedTaskTarget.taskId,
appearedTaskTargets,
new RemoteAnimationTarget[0] /* wallpaper */,
nonAppTargets /* nonApps */);
return;
} else {
- ActiveGestureLog.INSTANCE.addLog("Unable to launch side task (no recents)");
+ ActiveGestureProtoLogProxy.logLaunchingSideTaskFailed();
}
} else if (nonAppTargets.length > 0) {
TaskViewUtils.createSplitAuxiliarySurfacesAnimator(nonAppTargets /* nonApps */,
@@ -299,39 +292,46 @@
mCallbacks.addListener(listener);
final ActivityOptions options = ActivityOptions.makeBasic();
- options.setPendingIntentBackgroundActivityStartMode(
- ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS);
- options.setTransientLaunch();
- options.setSourceInfo(ActivityOptions.SourceInfo.TYPE_RECENTS_ANIMATION, eventTime);
- // Notify taskbar that we should skip reacting to launcher visibility change to
- // avoid a jumping taskbar.
- TaskbarUIController taskbarUIController = containerInterface.getTaskbarController();
- if (enableScalingRevealHomeAnimation() && taskbarUIController != null) {
- taskbarUIController.setSkipLauncherVisibilityChange(true);
+ // TODO:(b/365777482) if flag is enabled, but on launcher it will crash.
+ if(containerInterface.getCreatedContainer() instanceof RecentsWindowManager
+ && Flags.enableFallbackOverviewInWindow()){
+ mRecentsAnimationStartPending =
+ getSystemUiProxy().startRecentsActivity(intent, options, mCallbacks);
+ mRecentsWindowsManager.startRecentsWindow();
+ } else {
+ options.setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS);
+ options.setTransientLaunch();
+ options.setSourceInfo(ActivityOptions.SourceInfo.TYPE_RECENTS_ANIMATION, eventTime);
- mCallbacks.addListener(new RecentsAnimationCallbacks.RecentsAnimationListener() {
- @Override
- public void onRecentsAnimationCanceled(
- @NonNull HashMap<Integer, ThumbnailData> thumbnailDatas) {
- taskbarUIController.setSkipLauncherVisibilityChange(false);
- }
+ // Notify taskbar that we should skip reacting to launcher visibility change to
+ // avoid a jumping taskbar.
+ TaskbarUIController taskbarUIController = containerInterface.getTaskbarController();
+ if (enableScalingRevealHomeAnimation() && taskbarUIController != null) {
+ taskbarUIController.setSkipLauncherVisibilityChange(true);
- @Override
- public void onRecentsAnimationFinished(
- @NonNull RecentsAnimationController controller) {
- taskbarUIController.setSkipLauncherVisibilityChange(false);
- }
- });
+ mCallbacks.addListener(new RecentsAnimationCallbacks.RecentsAnimationListener() {
+ @Override
+ public void onRecentsAnimationCanceled(
+ @NonNull HashMap<Integer, ThumbnailData> thumbnailDatas) {
+ taskbarUIController.setSkipLauncherVisibilityChange(false);
+ }
+
+ @Override
+ public void onRecentsAnimationFinished(
+ @NonNull RecentsAnimationController controller) {
+ taskbarUIController.setSkipLauncherVisibilityChange(false);
+ }
+ });
+ }
+
+ mRecentsAnimationStartPending = getSystemUiProxy()
+ .startRecentsActivity(intent, options, mCallbacks);
}
-
- mRecentsAnimationStartPending = getSystemUiProxy()
- .startRecentsActivity(intent, options, mCallbacks);
if (enableHandleDelayedGestureCallbacks()) {
- ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
- "TaskAnimationManager.startRecentsAnimation: ")
- .append("Setting mRecentsAnimationStartPending = ")
- .append(mRecentsAnimationStartPending));
+ ActiveGestureProtoLogProxy.logSettingRecentsAnimationStartPending(
+ mRecentsAnimationStartPending);
}
gestureState.setState(STATE_RECENTS_ANIMATION_INITIALIZED);
return mCallbacks;
@@ -341,7 +341,7 @@
* Continues the existing running recents animation for a new gesture.
*/
public RecentsAnimationCallbacks continueRecentsAnimation(GestureState gestureState) {
- ActiveGestureLog.INSTANCE.addLog(/* event= */ "continueRecentsAnimation");
+ ActiveGestureProtoLogProxy.logContinueRecentsAnimation();
mCallbacks.removeListener(mLastGestureState);
mLastGestureState = gestureState;
mCallbacks.addListener(gestureState);
@@ -423,8 +423,7 @@
public void finishRunningRecentsAnimation(boolean toHome, boolean forceFinish,
Runnable forceFinishCb) {
if (mController != null) {
- ActiveGestureLog.INSTANCE.addLog(
- /* event= */ "finishRunningRecentsAnimation", toHome);
+ ActiveGestureProtoLogProxy.logFinishRunningRecentsAnimation(toHome);
if (forceFinish) {
mController.finishController(toHome, forceFinishCb, false /* sendUserLeaveHint */,
true /* forceFinish */);
@@ -461,11 +460,10 @@
*/
private void cleanUpRecentsAnimation(RecentsAnimationCallbacks targetCallbacks) {
if (mCallbacks != targetCallbacks) {
- ActiveGestureLog.INSTANCE.addLog(
- /* event= */ "cleanUpRecentsAnimation skipped due to wrong callbacks");
+ ActiveGestureProtoLogProxy.logCleanUpRecentsAnimationSkipped();
return;
}
- ActiveGestureLog.INSTANCE.addLog(/* event= */ "cleanUpRecentsAnimation");
+ ActiveGestureProtoLogProxy.logCleanUpRecentsAnimation();
if (mLiveTileCleanUpHandler != null) {
mLiveTileCleanUpHandler.run();
mLiveTileCleanUpHandler = null;
@@ -487,6 +485,10 @@
mTargets = null;
mLastGestureState = null;
mLastAppearedTaskTargets = null;
+
+ if(Flags.enableFallbackOverviewInWindow()) {
+ mRecentsWindowsManager.cleanup();
+ }
}
@Nullable
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index f4ff4b2..8e45767 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -173,9 +173,8 @@
protected T getActionsView() {
if (mActionsView == null) {
- mActionsView = BaseActivity.fromContext(
- mTaskContainer.getTaskView().getContext()).findViewById(
- R.id.overview_actions_view);
+ mActionsView = (T) RecentsViewContainer.containerFromContext(
+ mTaskContainer.getTaskView().getContext()).getActionsView();
}
return mActionsView;
}
diff --git a/quickstep/src/com/android/quickstep/TaskUtils.java b/quickstep/src/com/android/quickstep/TaskUtils.java
index 63e536a..49f7daf 100644
--- a/quickstep/src/com/android/quickstep/TaskUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskUtils.java
@@ -31,8 +31,8 @@
import androidx.annotation.Nullable;
import com.android.launcher3.pm.UserCache;
+import com.android.launcher3.util.ApplicationInfoWrapper;
import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.TraceHelper;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -70,8 +70,8 @@
return "";
}
UserHandle user = UserHandle.of(userId);
- ApplicationInfo applicationInfo = PackageManagerHelper.INSTANCE.get(context)
- .getApplicationInfo(packageName, user, 0);
+ ApplicationInfo applicationInfo =
+ new ApplicationInfoWrapper(context, packageName, user).getInfo();
if (applicationInfo == null) {
Log.e(TAG, "Failed to get title for userId=" + userId + ", packageName=" + packageName);
return "";
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index d8063ba..07ee479 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -59,7 +59,6 @@
import com.android.app.animation.Interpolators;
import com.android.internal.jank.Cuj;
-import com.android.launcher3.BaseActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimationSuccessListener;
@@ -68,6 +67,7 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.statemanager.StatefulContainer;
import com.android.launcher3.taskbar.TaskbarUIController;
import com.android.launcher3.util.DisplayController;
import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
@@ -80,6 +80,7 @@
import com.android.quickstep.views.DesktopTaskView;
import com.android.quickstep.views.GroupedTaskView;
import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.RecentsViewContainer;
import com.android.quickstep.views.TaskView;
import com.android.systemui.animation.RemoteAnimationTargetCompat;
import com.android.systemui.shared.recents.model.Task;
@@ -155,8 +156,9 @@
return taskView;
}
- public static void createRecentsWindowAnimator(
- @NonNull RecentsView recentsView,
+ public static <T extends Context & RecentsViewContainer & StatefulContainer<?>>
+ void createRecentsWindowAnimator(
+ @NonNull RecentsView<T, ?> recentsView,
@NonNull TaskView v,
boolean skipViewChanges,
@NonNull RemoteAnimationTarget[] appTargets,
@@ -204,8 +206,9 @@
int taskIndex = recentsView.indexOfChild(v);
Context context = v.getContext();
- BaseActivity baseActivity = BaseActivity.fromContext(context);
- DeviceProfile dp = baseActivity.getDeviceProfile();
+
+ T container = RecentsViewContainer.containerFromContext(context);
+ DeviceProfile dp = container.getDeviceProfile();
boolean showAsGrid = dp.isTablet;
boolean parallaxCenterAndAdjacentTask =
!showAsGrid && taskIndex != recentsView.getCurrentPage();
@@ -417,7 +420,8 @@
if (depthController != null) {
out.setFloat(depthController.stateDepth, MULTI_PROPERTY_VALUE,
- BACKGROUND_APP.getDepth(baseActivity), TOUCH_RESPONSE);
+ BACKGROUND_APP.getDepth(container),
+ TOUCH_RESPONSE);
}
}
diff --git a/quickstep/src/com/android/quickstep/TopTaskTracker.java b/quickstep/src/com/android/quickstep/TopTaskTracker.java
index 3cf0542..23a1ec7 100644
--- a/quickstep/src/com/android/quickstep/TopTaskTracker.java
+++ b/quickstep/src/com/android/quickstep/TopTaskTracker.java
@@ -206,12 +206,17 @@
Collections.addAll(mOrderedTaskList, tasks);
}
- // Strip the pinned task
ArrayList<RunningTaskInfo> tasks = new ArrayList<>(mOrderedTaskList);
- tasks.removeIf(t -> t.taskId == mPinnedTaskId);
+ // Strip the pinned task and recents task
+ tasks.removeIf(t -> t.taskId == mPinnedTaskId || isRecentsTask(t));
return new CachedTaskInfo(tasks);
}
+ private static boolean isRecentsTask(RunningTaskInfo task) {
+ return task != null && task.configuration.windowConfiguration
+ .getActivityType() == ACTIVITY_TYPE_RECENTS;
+ }
+
/**
* Class to provide information about a task which can be safely cached and do not change
* during the lifecycle of the task.
@@ -267,8 +272,7 @@
}
public boolean isRecentsTask() {
- return mTopTask != null && mTopTask.configuration.windowConfiguration
- .getActivityType() == ACTIVITY_TYPE_RECENTS;
+ return TopTaskTracker.isRecentsTask(mTopTask);
}
/**
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index ce66b04..18b7678 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -15,7 +15,6 @@
*/
package com.android.quickstep;
-import static android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS;
import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_MOVE;
@@ -26,11 +25,9 @@
import static com.android.launcher3.Flags.enableCursorHoverStates;
import static com.android.launcher3.Flags.enableHandleDelayedGestureCallbacks;
import static com.android.launcher3.Flags.useActivityOverlay;
-import static com.android.launcher3.Launcher.INTENT_ACTION_ALL_APPS_TOGGLE;
import static com.android.launcher3.LauncherPrefs.backedUpItem;
import static com.android.launcher3.MotionEventsUtils.isTrackpadMotionEvent;
import static com.android.launcher3.MotionEventsUtils.isTrackpadMultiFingerSwipe;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_TRACKPAD_GESTURE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.OnboardingPrefs.HOME_BOUNCE_SEEN;
@@ -39,11 +36,6 @@
import static com.android.quickstep.GestureState.TrackpadGestureType.getTrackpadGestureType;
import static com.android.quickstep.InputConsumer.TYPE_CURSOR_HOVER;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER;
-import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_DOWN;
-import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_MOVE;
-import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_UP;
-import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.NAVIGATION_MODE_SWITCHED;
-import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.RECENTS_ANIMATION_START_PENDING;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNFOLD_ANIMATION_FORWARDER;
@@ -79,6 +71,7 @@
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.MotionEvent;
+import android.view.View;
import androidx.annotation.BinderThread;
import androidx.annotation.NonNull;
@@ -86,13 +79,11 @@
import androidx.annotation.UiThread;
import androidx.annotation.VisibleForTesting;
-import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.ConstantItem;
import com.android.launcher3.EncryptionType;
import com.android.launcher3.Flags;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.anim.AnimatedFloat;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.provider.RestoreDbTask;
import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.statemanager.StatefulActivity;
@@ -111,6 +102,8 @@
import com.android.launcher3.util.ScreenOnTracker;
import com.android.launcher3.util.TraceHelper;
import com.android.quickstep.OverviewCommandHelper.CommandType;
+import com.android.quickstep.fallback.window.RecentsWindowManager;
+import com.android.quickstep.fallback.window.RecentsWindowSwipeHandler;
import com.android.quickstep.inputconsumers.AccessibilityInputConsumer;
import com.android.quickstep.inputconsumers.AssistantInputConsumer;
import com.android.quickstep.inputconsumers.BubbleBarInputConsumer;
@@ -128,6 +121,7 @@
import com.android.quickstep.inputconsumers.TrackpadStatusBarInputConsumer;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.ActiveGestureLog.CompoundString;
+import com.android.quickstep.util.ActiveGestureProtoLogProxy;
import com.android.quickstep.util.AssistStateManager;
import com.android.quickstep.util.AssistUtils;
import com.android.quickstep.views.RecentsViewContainer;
@@ -228,7 +222,6 @@
@BinderThread
@Override
public void onTaskbarToggled() {
- if (!FeatureFlags.ENABLE_KEYBOARD_TASKBAR_TOGGLE.get()) return;
MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis -> {
TaskbarActivityContext activityContext =
tis.mTaskbarManager.getCurrentActivityContext();
@@ -329,10 +322,10 @@
@Override
public void enterStageSplitFromRunningApp(boolean leftOrTop) {
executeForTouchInteractionService(tis -> {
- StatefulActivity activity =
- tis.mOverviewComponentObserver.getActivityInterface().getCreatedContainer();
- if (activity != null) {
- activity.enterStageSplitFromRunningApp(leftOrTop);
+ RecentsViewContainer container = tis.mOverviewComponentObserver
+ .getContainerInterface().getCreatedContainer();
+ if (container != null) {
+ container.enterStageSplitFromRunningApp(leftOrTop);
}
});
}
@@ -611,6 +604,8 @@
this::createLauncherSwipeHandler;
private final AbsSwipeUpHandler.Factory mFallbackSwipeHandlerFactory =
this::createFallbackSwipeHandler;
+ private final AbsSwipeUpHandler.Factory mRecentsWindowSwipeHandlerFactory =
+ this::createRecentsWindowSwipeHandler;
private final ScreenOnTracker.ScreenOnListener mScreenOnListener = this::onScreenOnChanged;
@@ -643,6 +638,7 @@
private InputEventReceiver mInputEventReceiver;
private TaskbarManager mTaskbarManager;
+ private RecentsWindowManager mRecentsWindowManager;
private Function<GestureState, AnimatedFloat> mSwipeUpProxyProvider = i -> null;
private AllAppsActionManager mAllAppsActionManager;
private InputManager mInputManager;
@@ -655,6 +651,8 @@
@Override
public void onCreate() {
super.onCreate();
+ Log.d(TAG, "onCreate: user=" + getUserId()
+ + " instance=" + System.identityHashCode(this));
// Initialize anything here that is needed in direct boot mode.
// Everything else should be initialized in onUserUnlocked() below.
mMainChoreographer = Choreographer.getInstance();
@@ -664,17 +662,18 @@
mAllAppsActionManager = new AllAppsActionManager(
this, UI_HELPER_EXECUTOR, this::createAllAppsPendingIntent);
mInputManager = getSystemService(InputManager.class);
- if (ENABLE_TRACKPAD_GESTURE.get()) {
- mInputManager.registerInputDeviceListener(mInputDeviceListener,
- UI_HELPER_EXECUTOR.getHandler());
- int [] inputDevices = mInputManager.getInputDeviceIds();
- for (int inputDeviceId : inputDevices) {
- mInputDeviceListener.onInputDeviceAdded(inputDeviceId);
- }
+ mInputManager.registerInputDeviceListener(mInputDeviceListener,
+ UI_HELPER_EXECUTOR.getHandler());
+ int [] inputDevices = mInputManager.getInputDeviceIds();
+ for (int inputDeviceId : inputDevices) {
+ mInputDeviceListener.onInputDeviceAdded(inputDeviceId);
}
mDesktopVisibilityController = new DesktopVisibilityController(this);
mTaskbarManager = new TaskbarManager(
this, mAllAppsActionManager, mNavCallbacks, mDesktopVisibilityController);
+ if(Flags.enableFallbackOverviewInWindow()) {
+ mRecentsWindowManager = new RecentsWindowManager(this);
+ }
mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer();
// Call runOnUserUnlocked() before any other callbacks to ensure everything is initialized.
@@ -687,7 +686,8 @@
}
private void disposeEventHandlers(String reason) {
- Log.d(TAG, "disposeEventHandlers: Reason: " + reason);
+ Log.d(TAG, "disposeEventHandlers: Reason: " + reason
+ + " instance=" + System.identityHashCode(this));
if (mInputEventReceiver != null) {
mInputEventReceiver.dispose();
mInputEventReceiver = null;
@@ -703,7 +703,7 @@
if (mDeviceState.isButtonNavMode()
&& !mDeviceState.supportsAssistantGestureInButtonNav()
- && (!ENABLE_TRACKPAD_GESTURE.get() || mTrackpadsConnected.isEmpty())) {
+ && (mTrackpadsConnected.isEmpty())) {
return;
}
@@ -724,8 +724,9 @@
@UiThread
public void onUserUnlocked() {
- Log.d(TAG, "onUserUnlocked: userId=" + getUserId());
- mTaskAnimationManager = new TaskAnimationManager(this);
+ Log.d(TAG, "onUserUnlocked: userId=" + getUserId()
+ + " instance=" + System.identityHashCode(this));
+ mTaskAnimationManager = new TaskAnimationManager(this, mRecentsWindowManager);
mOverviewComponentObserver = new OverviewComponentObserver(this, mDeviceState);
mOverviewCommandHelper = new OverviewCommandHelper(this,
mOverviewComponentObserver, mTaskAnimationManager);
@@ -768,33 +769,25 @@
private void onOverviewTargetChange(boolean isHomeAndOverviewSame) {
mAllAppsActionManager.setHomeAndOverviewSame(isHomeAndOverviewSame);
-
- StatefulActivity<?> newOverviewActivity =
- mOverviewComponentObserver.getActivityInterface().getCreatedContainer();
- if (newOverviewActivity != null) {
- mTaskbarManager.setActivity(newOverviewActivity);
+ RecentsViewContainer newOverviewContainer =
+ mOverviewComponentObserver.getContainerInterface().getCreatedContainer();
+ if (newOverviewContainer != null
+ && newOverviewContainer instanceof StatefulActivity activity) {
+ //TODO(b/368030750) refactor taskbarManager to accept RecentsViewContainer
+ mTaskbarManager.setActivity(activity);
}
mTISBinder.onOverviewTargetChange();
}
private PendingIntent createAllAppsPendingIntent() {
- if (FeatureFlags.ENABLE_ALL_APPS_SEARCH_IN_TASKBAR.get()) {
- return new PendingIntent(new IIntentSender.Stub() {
- @Override
- public void send(int code, Intent intent, String resolvedType,
- IBinder allowlistToken, IIntentReceiver finishedReceiver,
- String requiredPermission, Bundle options) {
- MAIN_EXECUTOR.execute(() -> mTaskbarManager.toggleAllApps());
- }
- });
- } else {
- return PendingIntent.getActivity(
- this,
- GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS,
- new Intent(mOverviewComponentObserver.getHomeIntent())
- .setAction(INTENT_ACTION_ALL_APPS_TOGGLE),
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
- }
+ return new PendingIntent(new IIntentSender.Stub() {
+ @Override
+ public void send(int code, Intent intent, String resolvedType,
+ IBinder allowlistToken, IIntentReceiver finishedReceiver,
+ String requiredPermission, Bundle options) {
+ MAIN_EXECUTOR.execute(() -> mTaskbarManager.toggleAllApps());
+ }
+ });
}
@UiThread
@@ -811,14 +804,15 @@
@UiThread
private void onAssistantVisibilityChanged() {
if (LockedUserState.get(this).isUserUnlocked()) {
- mOverviewComponentObserver.getActivityInterface().onAssistantVisibilityChanged(
+ mOverviewComponentObserver.getContainerInterface().onAssistantVisibilityChanged(
mDeviceState.getAssistantVisibility());
}
}
@Override
public void onDestroy() {
- Log.d(TAG, "Touch service destroyed: user=" + getUserId());
+ Log.d(TAG, "onDestroy: user=" + getUserId()
+ + " instance=" + System.identityHashCode(this));
sIsInitialized = false;
if (LockedUserState.get(this).isUserUnlocked()) {
mInputConsumer.unregisterInputConsumer();
@@ -834,6 +828,10 @@
mTrackpadsConnected.clear();
mTaskbarManager.destroy();
+
+ if (mRecentsWindowManager != null) {
+ mRecentsWindowManager.destroy();
+ }
mDesktopVisibilityController.onDestroy();
sConnected = false;
@@ -843,7 +841,8 @@
@Override
public IBinder onBind(Intent intent) {
- Log.d(TAG, "Touch service connected: user=" + getUserId());
+ Log.d(TAG, "onBind: user=" + getUserId()
+ + " instance=" + System.identityHashCode(this));
return mTISBinder;
}
@@ -860,9 +859,7 @@
private void onInputEvent(InputEvent ev) {
if (!(ev instanceof MotionEvent)) {
- ActiveGestureLog.INSTANCE.addLog(new CompoundString("TIS.onInputEvent: ")
- .append("Cannot process input event: received unknown event ")
- .append(ev.toString()));
+ ActiveGestureProtoLogProxy.logUnknownInputEvent(ev.toString());
return;
}
MotionEvent event = (MotionEvent) ev;
@@ -871,27 +868,19 @@
TestProtocol.SEQUENCE_TIS, "TouchInteractionService.onInputEvent", event);
if (!LockedUserState.get(this).isUserUnlocked()) {
- ActiveGestureLog.INSTANCE.addLog(new CompoundString("TIS.onInputEvent: ")
- .append("Cannot process input event: user is locked"));
+ ActiveGestureProtoLogProxy.logOnInputEventUserLocked();
return;
}
NavigationMode currentNavMode = mDeviceState.getMode();
if (mGestureStartNavMode != null && mGestureStartNavMode != currentNavMode) {
- ActiveGestureLog.INSTANCE.addLog(new CompoundString("TIS.onInputEvent: ")
- .append("Navigation mode switched mid-gesture (")
- .append(mGestureStartNavMode.name())
- .append(" -> ")
- .append(currentNavMode.name())
- .append("); cancelling gesture."),
- NAVIGATION_MODE_SWITCHED);
+ ActiveGestureProtoLogProxy.logOnInputEventNavModeSwitched(
+ mGestureStartNavMode.name(), currentNavMode.name());
event.setAction(ACTION_CANCEL);
} else if (mDeviceState.isButtonNavMode()
&& !mDeviceState.supportsAssistantGestureInButtonNav()
&& !isTrackpadMotionEvent(event)) {
- ActiveGestureLog.INSTANCE.addLog(new CompoundString("TIS.onInputEvent: ")
- .append("Cannot process input event: ")
- .append("using 3-button nav and event is not a trackpad event"));
+ ActiveGestureProtoLogProxy.logOnInputEventThreeButtonNav();
return;
}
@@ -907,12 +896,7 @@
}
if (mTaskAnimationManager.shouldIgnoreMotionEvents()) {
if (action == ACTION_DOWN || isHoverActionWithoutConsumer) {
- ActiveGestureLog.INSTANCE.addLog(
- new CompoundString("TIS.onMotionEvent: A new gesture has been ")
- .append("started, but a previously-requested recents ")
- .append("animation hasn't started. Ignoring all following ")
- .append("motion events."),
- RECENTS_ANIMATION_START_PENDING);
+ ActiveGestureProtoLogProxy.logOnInputIgnoringFollowingEvents();
}
return;
}
@@ -1002,41 +986,25 @@
if (mUncheckedConsumer != InputConsumer.NO_OP) {
switch (action) {
case ACTION_DOWN:
- ActiveGestureLog.INSTANCE.addLog(reasonString);
+ ActiveGestureProtoLogProxy.logDynamicString(reasonString.toString());
// fall through
case ACTION_UP:
- ActiveGestureLog.INSTANCE.addLog(
- new CompoundString("onMotionEvent(")
- .append((int) event.getRawX())
- .append(", ")
- .append((int) event.getRawY())
- .append("): ")
- .append(MotionEvent.actionToString(action))
- .append(", ")
- .append(MotionEvent.classificationToString(
- event.getClassification())),
- /* gestureEvent= */ action == ACTION_DOWN
- ? MOTION_DOWN
- : MOTION_UP);
+ ActiveGestureProtoLogProxy.logOnInputEventActionUp(
+ (int) event.getRawX(),
+ (int) event.getRawY(),
+ action,
+ MotionEvent.classificationToString(event.getClassification()));
break;
case ACTION_MOVE:
- ActiveGestureLog.INSTANCE.addLog(
- new CompoundString("onMotionEvent: ")
- .append(MotionEvent.actionToString(action))
- .append(",")
- .append(MotionEvent.classificationToString(
- event.getClassification()))
- .append(", pointerCount: ")
- .append(event.getPointerCount()),
- MOTION_MOVE);
+ ActiveGestureProtoLogProxy.logOnInputEventActionMove(
+ MotionEvent.actionToString(action),
+ MotionEvent.classificationToString(event.getClassification()),
+ event.getPointerCount());
break;
default: {
- ActiveGestureLog.INSTANCE.addLog(
- new CompoundString("onMotionEvent: ")
- .append(MotionEvent.actionToString(action))
- .append(",")
- .append(MotionEvent.classificationToString(
- event.getClassification())));
+ ActiveGestureProtoLogProxy.logOnInputEventGenericAction(
+ MotionEvent.actionToString(action),
+ MotionEvent.classificationToString(event.getClassification()));
}
}
}
@@ -1126,10 +1094,8 @@
gestureState.setTrackpadGestureType(trackpadGestureType);
// Log initial state for the gesture.
- ActiveGestureLog.INSTANCE.addLog(new CompoundString("Current running task package name=")
- .append(taskInfo.getPackageName()));
- ActiveGestureLog.INSTANCE.addLog(new CompoundString("Current SystemUi state flags=")
- .append(mDeviceState.getSystemUiStateString()));
+ ActiveGestureProtoLogProxy.logRunningTaskPackage(taskInfo.getPackageName());
+ ActiveGestureProtoLogProxy.logSysuiStateFlags(mDeviceState.getSystemUiStateString());
return gestureState;
}
@@ -1271,7 +1237,7 @@
getBaseContext(), mDeviceState, mInputMonitorCompat);
}
- if (ENABLE_TRACKPAD_GESTURE.get() && mGestureState.isTrackpadGesture()
+ if (mGestureState.isTrackpadGesture()
&& canStartSystemGesture && !previousGestureState.isRecentsAnimationRunning()) {
reasonString = newCompoundString(reasonPrefix)
.append(SUBSTRING_PREFIX)
@@ -1342,10 +1308,7 @@
private void logInputConsumerSelectionReason(
InputConsumer consumer, CompoundString reasonString) {
- ActiveGestureLog.INSTANCE.addLog(new CompoundString("setInputConsumer: ")
- .append(consumer.getName())
- .append(". reason(s):")
- .append(reasonString));
+ ActiveGestureProtoLogProxy.logSetInputConsumer(consumer.getName(), reasonString.toString());
if ((consumer.getType() & InputConsumer.TYPE_OTHER_ACTIVITY) != 0) {
ActiveGestureLog.INSTANCE.trackEvent(FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER);
}
@@ -1383,11 +1346,8 @@
? null
: runningTask.getVisibleNonExcludedTask();
if (otherVisibleTask != null) {
- ActiveGestureLog.INSTANCE.addLog(new CompoundString("Changing active task to ")
- .append(otherVisibleTask.getPackageName())
- .append(" because the previous task running on top of this one (")
- .append(runningTask.getPackageName())
- .append(") was excluded from recents"));
+ ActiveGestureProtoLogProxy.logUpdateGestureStateRunningTask(
+ otherVisibleTask.getPackageName(), runningTask.getPackageName());
gestureState.updateRunningTask(otherVisibleTask);
}
@@ -1446,8 +1406,9 @@
}
public AbsSwipeUpHandler.Factory getSwipeUpHandlerFactory() {
- return !mOverviewComponentObserver.isHomeAndOverviewSame()
- ? mFallbackSwipeHandlerFactory : mLauncherSwipeHandlerFactory;
+ return mOverviewComponentObserver.isHomeAndOverviewSame()
+ ? mLauncherSwipeHandlerFactory : (Flags.enableFallbackOverviewInWindow()
+ ? mRecentsWindowSwipeHandlerFactory : mFallbackSwipeHandlerFactory);
}
private InputConsumer createOtherActivityInputConsumer(GestureState gestureState,
@@ -1496,7 +1457,8 @@
.append("activity == null, trying to use default input consumer"));
}
- boolean hasWindowFocus = container.getRootView().hasWindowFocus();
+ View rootview = container.getRootView();
+ boolean hasWindowFocus = rootview != null && rootview.hasWindowFocus();
boolean isPreviousGestureAnimatingToLauncher =
previousGestureState.isRunningAnimationToLauncher()
|| mDeviceState.isPredictiveBackToHomeInProgress();
@@ -1591,11 +1553,11 @@
return;
}
- final BaseActivityInterface activityInterface =
- mOverviewComponentObserver.getActivityInterface();
+ final BaseContainerInterface containerInterface =
+ mOverviewComponentObserver.getContainerInterface();
final Intent overviewIntent = new Intent(
mOverviewComponentObserver.getOverviewIntentIgnoreSysUiState());
- if (activityInterface.getCreatedContainer() != null && fromInit) {
+ if (containerInterface.getCreatedContainer() != null && fromInit) {
// The activity has been created before the initialization of overview service. It is
// usually happens when booting or launcher is the top activity, so we should already
// have the latest state.
@@ -1605,8 +1567,7 @@
// TODO(b/258022658): Remove temporary logging.
Log.i(TAG, "preloadOverview: forSUWAllSet=" + forSUWAllSet
+ ", isHomeAndOverviewSame=" + mOverviewComponentObserver.isHomeAndOverviewSame());
-
- ActiveGestureLog.INSTANCE.addLog("preloadRecentsAnimation");
+ ActiveGestureProtoLogProxy.logPreloadRecentsAnimation();
mTaskAnimationManager.preloadRecentsAnimation(overviewIntent);
}
@@ -1615,18 +1576,18 @@
if (!LockedUserState.get(this).isUserUnlocked()) {
return;
}
- final BaseActivityInterface activityInterface =
- mOverviewComponentObserver.getActivityInterface();
- final BaseDraggingActivity activity = activityInterface.getCreatedContainer();
- if (activity == null || activity.isStarted()) {
+ final BaseContainerInterface containerInterface =
+ mOverviewComponentObserver.getContainerInterface();
+ final RecentsViewContainer container = containerInterface.getCreatedContainer();
+ if (container == null || container.isStarted()) {
// We only care about the existing background activity.
return;
}
- Configuration oldConfig = activity.getResources().getConfiguration();
+ Configuration oldConfig = container.asContext().getResources().getConfiguration();
boolean isFoldUnfold = isTablet(oldConfig) != isTablet(newConfig);
if (!isFoldUnfold && mOverviewComponentObserver.canHandleConfigChanges(
- activity.getComponentName(),
- activity.getResources().getConfiguration().diff(newConfig))) {
+ container.getComponentName(),
+ container.asContext().getResources().getConfiguration().diff(newConfig))) {
// Since navBar gestural height are different between portrait and landscape,
// can handle orientation changes and refresh navigation gestural region through
// onOneHandedModeChanged()
@@ -1665,11 +1626,11 @@
pw.println("\tmInputEventReceiver=" + mInputEventReceiver);
DisplayController.INSTANCE.get(this).dump(pw);
pw.println("TouchState:");
- BaseDraggingActivity createdOverviewActivity = mOverviewComponentObserver == null ? null
- : mOverviewComponentObserver.getActivityInterface().getCreatedContainer();
+ RecentsViewContainer createdOverviewContainer = mOverviewComponentObserver == null ? null
+ : mOverviewComponentObserver.getContainerInterface().getCreatedContainer();
boolean resumed = mOverviewComponentObserver != null
- && mOverviewComponentObserver.getActivityInterface().isResumed();
- pw.println("\tcreatedOverviewActivity=" + createdOverviewActivity);
+ && mOverviewComponentObserver.getContainerInterface().isResumed();
+ pw.println("\tcreatedOverviewActivity=" + createdOverviewContainer);
pw.println("\tresumed=" + resumed);
pw.println("\tmConsumer=" + mConsumer.getName());
ActiveGestureLog.INSTANCE.dump("", pw);
@@ -1677,8 +1638,8 @@
if (mTaskAnimationManager != null) {
mTaskAnimationManager.dump("", pw);
}
- if (createdOverviewActivity != null) {
- createdOverviewActivity.getDeviceProfile().dump(this, "", pw);
+ if (createdOverviewContainer != null) {
+ createdOverviewContainer.getDeviceProfile().dump(this, "", pw);
}
mTaskbarManager.dumpLogs("", pw);
mDesktopVisibilityController.dumpLogs("", pw);
@@ -1701,4 +1662,11 @@
gestureState, touchTimeMs, mTaskAnimationManager.isRecentsAnimationRunning(),
mInputConsumer);
}
+
+ private AbsSwipeUpHandler createRecentsWindowSwipeHandler(
+ GestureState gestureState, long touchTimeMs) {
+ return new RecentsWindowSwipeHandler(this, mDeviceState, mTaskAnimationManager,
+ gestureState, touchTimeMs, mTaskAnimationManager.isRecentsAnimationRunning(),
+ mInputConsumer, mRecentsWindowManager);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/dagger/QuickStepModule.java b/quickstep/src/com/android/quickstep/dagger/QuickStepModule.java
new file mode 100644
index 0000000..08345b8
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/dagger/QuickStepModule.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.dagger;
+
+import dagger.Module;
+
+@Module
+public class QuickStepModule {
+}
diff --git a/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java b/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java
new file mode 100644
index 0000000..f2d5715
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.dagger;
+
+import com.android.launcher3.dagger.LauncherAppComponent;
+import com.android.launcher3.dagger.LauncherBaseAppComponent;
+import com.android.quickstep.logging.SettingsChangeLogger;
+
+/**
+ * Launcher Quickstep base component for Dagger injection.
+ *
+ * This class is not actually annotated as a Dagger component, since it is not used directly as one.
+ * Doing so generates unnecessary code bloat.
+ *
+ * See {@link LauncherAppComponent} for the one actually used.
+ */
+public interface QuickstepBaseAppComponent extends LauncherBaseAppComponent {
+ SettingsChangeLogger getSettingsChangeLogger();
+}
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
index 94764a5..83794fb 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
@@ -47,9 +47,9 @@
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.states.StateAnimationConfig;
-import com.android.quickstep.RecentsActivity;
import com.android.quickstep.views.ClearAllButton;
import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.RecentsViewContainer;
/**
* State controller for fallback recents activity
@@ -57,12 +57,12 @@
public class FallbackRecentsStateController implements StateHandler<RecentsState> {
private final StateAnimationConfig mNoConfig = new StateAnimationConfig();
- private final RecentsActivity mActivity;
+ private final RecentsViewContainer mRecentsViewContainer;
private final FallbackRecentsView mRecentsView;
- public FallbackRecentsStateController(RecentsActivity activity) {
- mActivity = activity;
- mRecentsView = activity.getOverviewPanel();
+ public FallbackRecentsStateController(RecentsViewContainer container) {
+ mRecentsViewContainer = container;
+ mRecentsView = container.getOverviewPanel();
}
@Override
@@ -81,7 +81,7 @@
// While animating into recents, update the visible task data as needed
setter.addOnFrameCallback(() -> mRecentsView.loadVisibleTaskData(FLAG_UPDATE_ALL));
setter.addEndListener(success -> {
- if (!success) {
+ if (!success && !toState.isRecentsViewVisible()) {
mRecentsView.reset();
}
});
@@ -96,10 +96,10 @@
setter.setFloat(mRecentsView.getClearAllButton(), ClearAllButton.VISIBILITY_ALPHA,
clearAllButtonAlpha, LINEAR);
float overviewButtonAlpha = state.hasOverviewActions() ? 1 : 0;
- setter.setFloat(mActivity.getActionsView().getVisibilityAlpha(),
+ setter.setFloat(mRecentsViewContainer.getActionsView().getVisibilityAlpha(),
AnimatedFloat.VALUE, overviewButtonAlpha, LINEAR);
- float[] scaleAndOffset = state.getOverviewScaleAndOffset(mActivity);
+ float[] scaleAndOffset = state.getOverviewScaleAndOffset(mRecentsViewContainer);
setter.setFloat(mRecentsView, RECENTS_SCALE_PROPERTY, scaleAndOffset[0],
config.getInterpolator(ANIM_OVERVIEW_SCALE, LINEAR));
setter.setFloat(mRecentsView, ADJACENT_PAGE_HORIZONTAL_OFFSET, scaleAndOffset[1],
@@ -110,16 +110,19 @@
setter.setFloat(mRecentsView, TASK_MODALNESS, state.getOverviewModalness(),
config.getInterpolator(ANIM_OVERVIEW_MODAL, LINEAR));
setter.setFloat(mRecentsView, FULLSCREEN_PROGRESS, state.isFullScreen() ? 1 : 0, LINEAR);
- boolean showAsGrid = state.displayOverviewTasksAsGrid(mActivity.getDeviceProfile());
+ boolean showAsGrid =
+ state.displayOverviewTasksAsGrid(mRecentsViewContainer.getDeviceProfile());
setter.setFloat(mRecentsView, RECENTS_GRID_PROGRESS, showAsGrid ? 1f : 0f,
getOverviewInterpolator(state));
setter.setFloat(mRecentsView, TASK_THUMBNAIL_SPLASH_ALPHA,
state.showTaskThumbnailSplash() ? 1f : 0f, getOverviewInterpolator(state));
- setter.setViewBackgroundColor(mActivity.getScrimView(), state.getScrimColor(mActivity),
+ setter.setViewBackgroundColor(mRecentsViewContainer.getScrimView(),
+ state.getScrimColor(mRecentsViewContainer.asContext()),
config.getInterpolator(ANIM_SCRIM_FADE, LINEAR));
if (isSplitSelectionState(state)) {
- int duration = state.getTransitionDuration(mActivity, true /* isToState */);
+ int duration =
+ state.getTransitionDuration(mRecentsViewContainer.asContext(), true);
// TODO (b/246851887): Pass in setter as a NO_ANIM PendingAnimation instead
PendingAnimation pa = new PendingAnimation(duration);
mRecentsView.createSplitSelectInitAnimation(pa, duration);
@@ -129,7 +132,7 @@
Pair<FloatProperty<RecentsView>, FloatProperty<RecentsView>> taskViewsFloat =
mRecentsView.getPagedOrientationHandler().getSplitSelectTaskOffset(
TASK_PRIMARY_SPLIT_TRANSLATION, TASK_SECONDARY_SPLIT_TRANSLATION,
- mActivity.getDeviceProfile());
+ mRecentsViewContainer.getDeviceProfile());
setter.setFloat(mRecentsView, taskViewsFloat.first, isSplitSelectionState(state)
? mRecentsView.getSplitSelectTranslation() : 0, LINEAR);
setter.setFloat(mRecentsView, taskViewsFloat.second, 0, LINEAR);
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index e67a9bc..7f88090 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -19,7 +19,6 @@
import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
import static com.android.quickstep.fallback.RecentsState.DEFAULT;
-import static com.android.quickstep.fallback.RecentsState.HOME;
import static com.android.quickstep.fallback.RecentsState.MODAL_TASK;
import static com.android.quickstep.fallback.RecentsState.OVERVIEW_SPLIT_SELECT;
@@ -31,6 +30,7 @@
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.config.FeatureFlags;
@@ -38,17 +38,20 @@
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.statemanager.StateManager.StateListener;
+import com.android.launcher3.statemanager.StatefulContainer;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource;
+import com.android.quickstep.BaseContainerInterface;
import com.android.quickstep.FallbackActivityInterface;
+import com.android.quickstep.FallbackWindowInterface;
import com.android.quickstep.GestureState;
-import com.android.quickstep.RecentsActivity;
import com.android.quickstep.RotationTouchHelper;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.util.SplitSelectStateController;
import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.views.OverviewActionsView;
import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.RecentsViewContainer;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.Task;
@@ -56,7 +59,8 @@
import java.util.Arrays;
import java.util.List;
-public class FallbackRecentsView extends RecentsView<RecentsActivity, RecentsState>
+public class FallbackRecentsView<CONTAINER_TYPE extends Context & RecentsViewContainer
+ & StatefulContainer<RecentsState>> extends RecentsView<CONTAINER_TYPE, RecentsState>
implements StateListener<RecentsState> {
private static final int TASK_DISMISS_DURATION = 150;
@@ -69,10 +73,16 @@
}
public FallbackRecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr, FallbackActivityInterface.INSTANCE);
+ super(context, attrs, defStyleAttr, getContainerInterface());
mContainer.getStateManager().addStateListener(this);
}
+ private static BaseContainerInterface<RecentsState, ?> getContainerInterface() {
+ return Flags.enableFallbackOverviewInWindow()
+ ? FallbackWindowInterface.getInstance()
+ : FallbackActivityInterface.INSTANCE;
+ }
+
@Override
public void init(OverviewActionsView actionsView, SplitSelectStateController splitController,
@Nullable DesktopRecentsTransitionController desktopRecentsTransitionController) {
@@ -93,7 +103,7 @@
}
@Override
- public StateManager<RecentsState, RecentsActivity> getStateManager() {
+ public StateManager<RecentsState, ?> getStateManager() {
return mContainer.getStateManager();
}
@@ -260,7 +270,7 @@
@Override
public void onStateTransitionComplete(RecentsState finalState) {
- if (finalState == HOME) {
+ if (!finalState.isRecentsViewVisible()) {
// Clean-up logic that occurs when recents is no longer in use/visible.
reset();
}
@@ -283,7 +293,8 @@
}
}
- if (isOverlayEnabled) {
+ // disabling this so app icons aren't drawn on top of recent tasks.
+ if (isOverlayEnabled && !Flags.enableFallbackOverviewInWindow()) {
runActionOnRemoteHandles(remoteTargetHandle ->
remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true));
}
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsDragLayer.java b/quickstep/src/com/android/quickstep/fallback/RecentsDragLayer.java
index 29c3dc8..a2884b6 100644
--- a/quickstep/src/com/android/quickstep/fallback/RecentsDragLayer.java
+++ b/quickstep/src/com/android/quickstep/fallback/RecentsDragLayer.java
@@ -20,13 +20,12 @@
import com.android.launcher3.util.TouchController;
import com.android.launcher3.views.BaseDragLayer;
-import com.android.quickstep.RecentsActivity;
+import com.android.quickstep.views.RecentsViewContainer;
/**
* Drag layer for fallback recents activity
*/
-public class RecentsDragLayer extends BaseDragLayer<RecentsActivity> {
-
+public class RecentsDragLayer<T extends Context & RecentsViewContainer> extends BaseDragLayer<T> {
public RecentsDragLayer(Context context, AttributeSet attrs) {
super(context, attrs, 1 /* alphaChannelCount */);
}
@@ -34,8 +33,8 @@
@Override
public void recreateControllers() {
mControllers = new TouchController[] {
- new RecentsTaskController(mActivity),
- new FallbackNavBarTouchController(mActivity),
+ new RecentsTaskController(mContainer),
+ new FallbackNavBarTouchController(mContainer),
};
}
}
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java b/quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java
index 2cb398c..07da379 100644
--- a/quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java
+++ b/quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java
@@ -15,18 +15,22 @@
*/
package com.android.quickstep.fallback;
+import android.content.Context;
+
+import com.android.launcher3.statemanager.StatefulContainer;
import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController;
-import com.android.quickstep.RecentsActivity;
+import com.android.quickstep.views.RecentsViewContainer;
-public class RecentsTaskController extends TaskViewTouchController<RecentsActivity> {
-
- public RecentsTaskController(RecentsActivity activity) {
- super(activity);
+public class RecentsTaskController<T extends Context & RecentsViewContainer &
+ StatefulContainer<RecentsState>> extends TaskViewTouchController<T> {
+ public RecentsTaskController(T container) {
+ super(container);
}
@Override
protected boolean isRecentsInteractive() {
- return mContainer.hasWindowFocus() || mContainer.getStateManager().getState().hasLiveTile();
+ return mContainer.getRootView().hasWindowFocus()
+ || mContainer.getStateManager().getState().hasLiveTile();
}
@Override
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowContext.kt b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowContext.kt
new file mode 100644
index 0000000..52a7682
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowContext.kt
@@ -0,0 +1,99 @@
+/*
+ * 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.fallback.window
+
+import android.content.Context
+import android.graphics.PixelFormat
+import android.view.ContextThemeWrapper
+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.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.
+ * <p>
+ * Overlays have their own window and need a window context.
+ */
+open class RecentsWindowContext(windowContext: Context) :
+ ContextThemeWrapper(windowContext, Themes.getActivityThemeRes(windowContext)), ActivityContext {
+
+ 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"
+
+ protected var windowLayoutParams: WindowManager.LayoutParams? =
+ createDefaultWindowLayoutParams(
+ WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, windowTitle)
+
+ override fun getDragLayer(): BaseDragLayer<RecentsWindowManager> {
+ return dragLayer
+ }
+
+ override fun getDeviceProfile(): DeviceProfile {
+ if (deviceProfile == null) {
+ deviceProfile = InvariantDeviceProfile.INSTANCE[this].getDeviceProfile(this)
+ .copy(this)
+ }
+ return deviceProfile!!
+ }
+
+ override fun getOnDeviceProfileChangeListeners():
+ List<DeviceProfile.OnDeviceProfileChangeListener> {
+ return deviceProfileChangeListeners
+ }
+
+ /**
+ * Creates LayoutParams for adding a view directly to WindowManager as a new window.
+ *
+ * @param type The window type to pass to the created WindowManager.LayoutParams.
+ * @param title The window title to pass to the created WindowManager.LayoutParams.
+ */
+ fun createDefaultWindowLayoutParams(type: Int, title: String): WindowManager.LayoutParams {
+ var windowFlags =
+ (WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS or
+ WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS or
+ WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED)
+
+ val windowLayoutParams =
+ WindowManager.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ type,
+ windowFlags,
+ PixelFormat.TRANSLUCENT,
+ )
+
+ windowLayoutParams.title = title
+ windowLayoutParams.fitInsetsTypes = 0
+ windowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+ windowLayoutParams.isSystemApplicationOverlay = true
+ windowLayoutParams.privateFlags = PRIVATE_FLAG_CONSUME_IME_INSETS
+
+ return windowLayoutParams
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt
new file mode 100644
index 0000000..8ce61f5
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt
@@ -0,0 +1,370 @@
+/*
+ * 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.fallback.window
+
+import android.animation.AnimatorSet
+import android.app.ActivityOptions
+import android.content.ComponentName
+import android.content.Context
+import android.content.LocusId
+import android.os.Bundle
+import android.util.Log
+import android.view.KeyEvent
+import android.view.LayoutInflater
+import android.view.MotionEvent
+import android.view.RemoteAnimationAdapter
+import android.view.RemoteAnimationTarget
+import android.view.SurfaceControl
+import android.view.View
+import android.view.Window
+import android.view.WindowManager
+import android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
+import android.window.RemoteTransition
+import com.android.launcher3.BaseActivity
+import com.android.launcher3.LauncherAnimationRunner
+import com.android.launcher3.LauncherAnimationRunner.RemoteAnimationFactory
+import com.android.launcher3.R
+import com.android.launcher3.statehandlers.DesktopVisibilityController
+import com.android.launcher3.statemanager.StateManager
+import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory
+import com.android.launcher3.statemanager.StatefulContainer
+import com.android.launcher3.util.DisplayController
+import com.android.launcher3.util.RunnableList
+import com.android.launcher3.util.SystemUiController
+import com.android.launcher3.views.BaseDragLayer
+import com.android.launcher3.views.ScrimView
+import com.android.quickstep.FallbackWindowInterface
+import com.android.quickstep.OverviewComponentObserver
+import com.android.quickstep.RecentsModel
+import com.android.quickstep.RemoteAnimationTargets
+import com.android.quickstep.SystemUiProxy
+import com.android.quickstep.fallback.FallbackRecentsStateController
+import com.android.quickstep.fallback.FallbackRecentsView
+import com.android.quickstep.fallback.RecentsDragLayer
+import com.android.quickstep.fallback.RecentsState
+import com.android.quickstep.fallback.RecentsState.BACKGROUND_APP
+import com.android.quickstep.fallback.RecentsState.BG_LAUNCHER
+import com.android.quickstep.fallback.RecentsState.DEFAULT
+import com.android.quickstep.fallback.RecentsState.HOME
+import com.android.quickstep.fallback.RecentsState.MODAL_TASK
+import com.android.quickstep.fallback.RecentsState.OVERVIEW_SPLIT_SELECT
+import com.android.quickstep.util.RecentsAtomicAnimationFactory
+import com.android.quickstep.util.SplitSelectStateController
+import com.android.quickstep.util.TISBindHelper
+import com.android.quickstep.views.OverviewActionsView
+import com.android.quickstep.views.RecentsView
+import com.android.quickstep.views.RecentsViewContainer
+import java.util.function.Predicate
+
+/**
+ * Class that will manage RecentsView lifecycle within a window and interface correctly
+ * where needed. This allows us to run RecentsView in a window where needed.
+ * todo: b/365776320, b/365777482
+ */
+class RecentsWindowManager(context: Context) :
+ RecentsWindowContext(context), RecentsViewContainer, StatefulContainer<RecentsState> {
+
+ companion object {
+ private const val HOME_APPEAR_DURATION: Long = 250
+ private const val TAG = "RecentsWindowManager"
+ private const val DEBUG = false
+ }
+
+ protected var recentsView: FallbackRecentsView<RecentsWindowManager>? = null
+ private val windowContext: Context = createWindowContext(TYPE_APPLICATION_OVERLAY, null)
+ private val windowManager: WindowManager = windowContext.getSystemService(WindowManager::class.java)!!
+ private var layoutInflater: LayoutInflater = LayoutInflater.from(this).cloneInContext(this)
+ private var stateManager: StateManager<RecentsState, RecentsWindowManager> =
+ StateManager<RecentsState, RecentsWindowManager>(this, RecentsState.BG_LAUNCHER)
+ private var mSystemUiController: SystemUiController? = null
+
+ private var dragLayer: RecentsDragLayer<RecentsWindowManager>? = null
+ private var windowView: View? = null
+ private var actionsView: OverviewActionsView<*>? = null
+ private var scrimView: ScrimView? = null
+
+ private var isShown = false
+
+ private var tisBindHelper: TISBindHelper = TISBindHelper(this) {}
+
+ // Callback array that corresponds to events defined in @ActivityEvent
+ private val mEventCallbacks =
+ arrayOf(RunnableList(), RunnableList(), RunnableList(), RunnableList())
+ private var onInitListener: Predicate<Boolean>? = null
+
+ init {
+ FallbackWindowInterface.init(this)
+ }
+
+ override fun destroy() {
+ super.destroy()
+ FallbackWindowInterface.getInstance()?.destroy()
+ }
+
+ override fun startHome() {
+ val recentsView: RecentsView<*, *> = getOverviewPanel()
+ recentsView.switchToScreenshot {
+ recentsView.finishRecentsAnimation(true) { startHomeInternal() }
+ }
+ }
+
+ private fun startHomeInternal() {
+ val runner = LauncherAnimationRunner(mainThreadHandler, mAnimationToHomeFactory, true)
+ val options =
+ ActivityOptions.makeRemoteAnimation(
+ RemoteAnimationAdapter(runner, HOME_APPEAR_DURATION, 0),
+ RemoteTransition(
+ runner.toRemoteTransition(),
+ iApplicationThread,
+ "StartHomeFromRecents",
+ ),
+ )
+ OverviewComponentObserver.startHomeIntentSafely(this, options.toBundle(), TAG)
+ }
+
+ private val mAnimationToHomeFactory =
+ RemoteAnimationFactory {
+ _: Int,
+ appTargets: Array<RemoteAnimationTarget>?,
+ wallpaperTargets: Array<RemoteAnimationTarget>?,
+ nonAppTargets: Array<RemoteAnimationTarget>?,
+ result: LauncherAnimationRunner.AnimationResult? ->
+ val controller =
+ getStateManager().createAnimationToNewWorkspace(BG_LAUNCHER, HOME_APPEAR_DURATION)
+ controller.dispatchOnStart()
+ val targets =
+ RemoteAnimationTargets(
+ appTargets,
+ wallpaperTargets,
+ nonAppTargets,
+ RemoteAnimationTarget.MODE_OPENING,
+ )
+ for (app in targets.apps) {
+ SurfaceControl.Transaction().setAlpha(app.leash, 1f).apply()
+ }
+ val anim = AnimatorSet()
+ anim.play(controller.animationPlayer)
+ anim.setDuration(HOME_APPEAR_DURATION)
+ result!!.setAnimation(
+ anim,
+ this@RecentsWindowManager,
+ {
+ getStateManager().goToState(HOME, false)
+ cleanup()
+ },
+ true, /* skipFirstFrame */
+ )
+ }
+
+ fun cleanup() {
+ if (isShown) {
+ windowManager.removeViewImmediate(windowView)
+ isShown = false
+ }
+ }
+
+ fun startRecentsWindow() {
+ if (isShown) return
+ if (windowView == null) {
+ windowView = layoutInflater.inflate(R.layout.fallback_recents_activity, null)
+ }
+ windowManager.addView(windowView, windowLayoutParams)
+ isShown = true
+
+ windowView?.systemUiVisibility =
+ (View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
+ View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)
+
+ recentsView = windowView?.findViewById(R.id.overview_panel)
+ actionsView = windowView?.findViewById(R.id.overview_actions_view)
+ scrimView = windowView?.findViewById(R.id.scrim_view)
+ val systemUiProxy = SystemUiProxy.INSTANCE[this]
+ val splitSelectStateController =
+ SplitSelectStateController(
+ this,
+ getStateManager(),
+ null, /* depthController */
+ statsLogManager,
+ systemUiProxy,
+ RecentsModel.INSTANCE[this],
+ null, /*activityBackCallback*/
+ )
+ recentsView?.init(actionsView, splitSelectStateController, null)
+ dragLayer = windowView?.findViewById(R.id.drag_layer)
+
+ actionsView?.updateDimension(getDeviceProfile(), recentsView?.lastComputedTaskSize)
+ actionsView?.updateVerticalMargin(DisplayController.getNavigationMode(this))
+
+ mSystemUiController = SystemUiController(windowView)
+ onInitListener?.test(true)
+ }
+
+ override fun canStartHomeSafely(): Boolean {
+ val overviewCommandHelper = tisBindHelper.overviewCommandHelper
+ return overviewCommandHelper == null || overviewCommandHelper.canStartHomeSafely()
+ }
+
+ override fun getDesktopVisibilityController(): DesktopVisibilityController? {
+ return tisBindHelper.desktopVisibilityController
+ }
+
+ fun registerInitListener(onInitListener: Predicate<Boolean>) {
+ this.onInitListener = onInitListener
+ }
+
+ override fun collectStateHandlers(out: MutableList<StateManager.StateHandler<RecentsState?>>?) {
+ out!!.add(FallbackRecentsStateController(this))
+ }
+
+ override fun getStateManager(): StateManager<RecentsState, RecentsWindowManager> {
+ return this.stateManager
+ }
+
+ override fun shouldAnimateStateChange(): Boolean {
+ return true
+ }
+
+ override fun isInState(state: RecentsState?): Boolean {
+ return stateManager.state == state
+ }
+
+ override fun onStateSetStart(state: RecentsState?) {
+ super.onStateSetStart(state)
+ logState(state, "state started:")
+ }
+
+ override fun onStateSetEnd(state: RecentsState?) {
+ super.onStateSetEnd(state)
+ logState(state, "state ended:")
+ }
+
+ private fun logState(state: RecentsState?, prefix: String) {
+ if (!DEBUG) {
+ return
+ }
+ if (state != null) {
+ when (state) {
+ DEFAULT -> Log.d(TAG, prefix + "default")
+ MODAL_TASK -> {
+ Log.d(TAG, prefix + "MODAL_TASK")
+ }
+ BACKGROUND_APP -> {
+ Log.d(TAG, prefix + "BACKGROUND_APP")
+ }
+ HOME -> {
+ Log.d(TAG, prefix + "HOME")
+ }
+ BG_LAUNCHER -> {
+ Log.d(TAG, prefix + "BG_LAUNCHER")
+ }
+ OVERVIEW_SPLIT_SELECT -> {
+ Log.d(TAG, prefix + "OVERVIEW_SPLIT_SELECT")
+ }
+ }
+ }
+ }
+
+ override fun getSystemUiController(): SystemUiController? {
+ if (mSystemUiController == null) {
+ mSystemUiController = SystemUiController(rootView)
+ }
+ return mSystemUiController
+ }
+
+ override fun getContext(): Context {
+ return this
+ }
+
+ override fun getScrimView(): ScrimView? {
+ return scrimView
+ }
+
+ override fun <T : View?> getOverviewPanel(): T {
+ return recentsView as T
+ }
+
+ override fun getRootView(): View? {
+ return windowView
+ }
+
+ override fun getDragLayer(): BaseDragLayer<RecentsWindowManager> {
+ return dragLayer!!
+ }
+
+ override fun dispatchGenericMotionEvent(ev: MotionEvent?): Boolean {
+ // TODO(b/368610710)
+ return false
+ }
+
+ override fun dispatchKeyEvent(ev: KeyEvent?): Boolean {
+ // TODO(b/368610710)
+ return false
+ }
+
+ override fun getActionsView(): OverviewActionsView<*>? {
+ return actionsView
+ }
+
+ override fun addForceInvisibleFlag(flag: Int) {}
+
+ override fun clearForceInvisibleFlag(flag: Int) {}
+
+ override fun setLocusContext(id: LocusId?, bundle: Bundle?) {
+ // no op
+ }
+
+ override fun isStarted(): Boolean {
+ return isShown
+ }
+
+ /** Adds a callback for the provided activity event */
+ override fun addEventCallback(@BaseActivity.ActivityEvent event: Int, callback: Runnable?) {
+ mEventCallbacks[event].add(callback)
+ }
+
+ /** Removes a previously added callback */
+ override fun removeEventCallback(@BaseActivity.ActivityEvent event: Int, callback: Runnable?) {
+ mEventCallbacks[event].remove(callback)
+ }
+
+ override fun runOnBindToTouchInteractionService(r: Runnable?) {
+ tisBindHelper.runOnBindToTouchInteractionService(r)
+ }
+
+ override fun addMultiWindowModeChangedListener(
+ listener: BaseActivity.MultiWindowModeChangedListener?
+ ) {
+ // TODO(b/368408838)
+ }
+
+ override fun removeMultiWindowModeChangedListener(
+ listener: BaseActivity.MultiWindowModeChangedListener?
+ ) {}
+
+ override fun returnToHomescreen() {
+ startHome()
+ }
+
+ override fun isRecentsViewVisible(): Boolean {
+ return getStateManager().state!!.isRecentsViewVisible
+ }
+
+ override fun createAtomicAnimationFactory(): AtomicAnimationFactory<RecentsState?>? {
+ return RecentsAtomicAnimationFactory<RecentsWindowManager, RecentsState>(this)
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowSwipeHandler.java b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowSwipeHandler.java
new file mode 100644
index 0000000..34b3d74
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowSwipeHandler.java
@@ -0,0 +1,452 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.fallback.window;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.content.Intent.EXTRA_COMPONENT_NAME;
+import static android.content.Intent.EXTRA_USER;
+
+import static com.android.app.animation.Interpolators.ACCELERATE;
+import static com.android.launcher3.GestureNavContract.EXTRA_GESTURE_CONTRACT;
+import static com.android.launcher3.GestureNavContract.EXTRA_ICON_POSITION;
+import static com.android.launcher3.GestureNavContract.EXTRA_ICON_SURFACE;
+import static com.android.launcher3.GestureNavContract.EXTRA_ON_FINISH_CALLBACK;
+import static com.android.launcher3.GestureNavContract.EXTRA_REMOTE_CALLBACK;
+import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
+
+import android.animation.ObjectAnimator;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.ParcelUuid;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+import android.view.RemoteAnimationTarget;
+import android.view.Surface;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatedFloat;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.anim.SpringAnimationBuilder;
+import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.util.DisplayController;
+import com.android.quickstep.AbsSwipeUpHandler;
+import com.android.quickstep.GestureState;
+import com.android.quickstep.RecentsAnimationDeviceState;
+import com.android.quickstep.RemoteAnimationTargets;
+import com.android.quickstep.TaskAnimationManager;
+import com.android.quickstep.fallback.FallbackRecentsView;
+import com.android.quickstep.fallback.RecentsState;
+import com.android.quickstep.util.ActiveGestureLog;
+import com.android.quickstep.util.RectFSpringAnim;
+import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
+import com.android.quickstep.util.TransformParams;
+import com.android.quickstep.util.TransformParams.BuilderProxy;
+import com.android.quickstep.views.TaskView;
+import com.android.systemui.shared.recents.model.Task.TaskKey;
+import com.android.systemui.shared.system.InputConsumerController;
+
+import java.lang.ref.WeakReference;
+import java.util.List;
+import java.util.UUID;
+import java.util.function.Consumer;
+
+/**
+ * Handles the navigation gestures when a 3rd party launcher is the default home activity.
+ *
+ * Bugs: b/365775417
+ */
+public class RecentsWindowSwipeHandler extends AbsSwipeUpHandler<RecentsWindowManager,
+ FallbackRecentsView<RecentsWindowManager>, RecentsState> {
+
+ private static final String TAG = "RecentsWindowSwipeHandler";
+
+ /**
+ * Message used for receiving gesture nav contract information. We use a static messenger to
+ * avoid leaking too make binders in case the receiving launcher does not handle the contract
+ * properly.
+ */
+ private static StaticMessageReceiver sMessageReceiver = null;
+
+ private FallbackHomeAnimationFactory mActiveAnimationFactory;
+ private final boolean mRunningOverHome;
+
+ private final Matrix mTmpMatrix = new Matrix();
+ private float mMaxLauncherScale = 1;
+
+ private boolean mAppCanEnterPip;
+
+ public RecentsWindowSwipeHandler(Context context, RecentsAnimationDeviceState deviceState,
+ TaskAnimationManager taskAnimationManager, GestureState gestureState, long touchTimeMs,
+ boolean continuingLastGesture, InputConsumerController inputConsumer,
+ RecentsWindowManager recentsWindowManager) {
+ super(context, deviceState, taskAnimationManager, gestureState, touchTimeMs,
+ continuingLastGesture, inputConsumer, recentsWindowManager);
+
+ mRunningOverHome = mGestureState.getRunningTask() != null
+ && mGestureState.getRunningTask().isHomeTask();
+ if (mRunningOverHome) {
+ runActionOnRemoteHandles(remoteTargetHandle ->
+ remoteTargetHandle.getTransformParams().setHomeBuilderProxy(
+ RecentsWindowSwipeHandler.
+ this::updateHomeActivityTransformDuringSwipeUp));
+ }
+ }
+
+ @Override
+ protected void initTransitionEndpoints(DeviceProfile dp) {
+ super.initTransitionEndpoints(dp);
+ if (mRunningOverHome) {
+ // Full screen scale should be independent of remote target handle
+ mMaxLauncherScale = 1 / mRemoteTargetHandles[0].getTaskViewSimulator()
+ .getFullScreenScale();
+ }
+ }
+
+ private void updateHomeActivityTransformDuringSwipeUp(SurfaceProperties builder,
+ RemoteAnimationTarget app, TransformParams params) {
+ setHomeScaleAndAlpha(builder, app, mCurrentShift.value,
+ Utilities.boundToRange(1 - mCurrentShift.value, 0, 1));
+ }
+
+ private void setHomeScaleAndAlpha(SurfaceProperties builder,
+ RemoteAnimationTarget app, float verticalShift, float alpha) {
+ float scale = Utilities.mapRange(verticalShift, 1, mMaxLauncherScale);
+ mTmpMatrix.setScale(scale, scale,
+ app.localBounds.exactCenterX(), app.localBounds.exactCenterY());
+ builder.setMatrix(mTmpMatrix).setAlpha(alpha);
+ builder.setShow();
+ }
+
+ @Override
+ protected HomeAnimationFactory createHomeAnimationFactory(
+ List<IBinder> launchCookies,
+ long duration,
+ boolean isTargetTranslucent,
+ boolean appCanEnterPip,
+ RemoteAnimationTarget runningTaskTarget,
+ @Nullable TaskView targetTaskView) {
+ mAppCanEnterPip = appCanEnterPip;
+ if (appCanEnterPip) {
+ return new FallbackPipToHomeAnimationFactory();
+ }
+ mActiveAnimationFactory = new FallbackHomeAnimationFactory(duration);
+ //todo: b/368410893 follow up on this as its intent focused and seems to cut immediately
+ Intent intent = new Intent(mGestureState.getHomeIntent());
+ if (mActiveAnimationFactory != null && runningTaskTarget != null) {
+ mActiveAnimationFactory.addGestureContract(intent, runningTaskTarget.taskInfo);
+ }
+ return mActiveAnimationFactory;
+ }
+
+ @Override
+ protected boolean handleTaskAppeared(@NonNull RemoteAnimationTarget[] appearedTaskTargets,
+ @NonNull ActiveGestureLog.CompoundString failureReason) {
+ if (mActiveAnimationFactory != null
+ && mActiveAnimationFactory.handleHomeTaskAppeared(appearedTaskTargets)) {
+ mActiveAnimationFactory = null;
+ return false;
+ }
+
+ return super.handleTaskAppeared(appearedTaskTargets, failureReason);
+ }
+
+ @Override
+ protected void finishRecentsControllerToHome(Runnable callback) {
+ final Runnable recentsCallback;
+ if (mAppCanEnterPip) {
+ // Make sure Launcher is resumed after auto-enter-pip transition to actually trigger
+ // the PiP task appearing.
+ recentsCallback = () -> {
+ callback.run();
+ mRecentsWindowManager.startHome();
+ };
+ } else {
+ recentsCallback = callback;
+ }
+ mRecentsView.cleanupRemoteTargets();
+ mRecentsAnimationController.finish(
+ mAppCanEnterPip /* toRecents */, recentsCallback, true /* sendUserLeaveHint */);
+ }
+
+ @Override
+ protected void switchToScreenshot() {
+ if (mRunningOverHome) {
+ // When the current task is home, then we don't need to capture anything
+ mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
+ } else {
+ super.switchToScreenshot();
+ }
+ }
+
+ @Override
+ protected void notifyGestureAnimationStartToRecents() {
+ if (mRunningOverHome) {
+ if (DisplayController.getNavigationMode(mContext).hasGestures) {
+ mRecentsView.onGestureAnimationStartOnHome(
+ mGestureState.getRunningTask().getPlaceholderTasks(),
+ mDeviceState.getRotationTouchHelper());
+ }
+ } else {
+ super.notifyGestureAnimationStartToRecents();
+ }
+ }
+
+ private class FallbackPipToHomeAnimationFactory extends HomeAnimationFactory {
+ @NonNull
+ @Override
+ public AnimatorPlaybackController createActivityAnimationToHome() {
+ // copied from {@link LauncherSwipeHandlerV2.LauncherHomeAnimationFactory}
+ long accuracy = 2 * Math.max(mDp.widthPx, mDp.heightPx);
+ return mContainer.getStateManager().createAnimationToNewWorkspace(
+ RecentsState.HOME, accuracy, StateAnimationConfig.SKIP_ALL_ANIMATIONS);
+ }
+ }
+
+ private class FallbackHomeAnimationFactory extends HomeAnimationFactory
+ implements Consumer<Message> {
+ private final Rect mTempRect = new Rect();
+ private final TransformParams mHomeAlphaParams = new TransformParams();
+ private final AnimatedFloat mHomeAlpha;
+
+ private final AnimatedFloat mVerticalShiftForScale = new AnimatedFloat();
+ private final AnimatedFloat mRecentsAlpha = new AnimatedFloat();
+
+ private final RectF mTargetRect = new RectF();
+ private SurfaceControl mSurfaceControl;
+
+ private boolean mAnimationFinished;
+ private Message mOnFinishCallback;
+
+ private final long mDuration;
+
+ private RectFSpringAnim mSpringAnim;
+ FallbackHomeAnimationFactory(long duration) {
+ mDuration = duration;
+
+ if (mRunningOverHome) {
+ mHomeAlpha = new AnimatedFloat();
+ mHomeAlpha.value = Utilities.boundToRange(1 - mCurrentShift.value, 0, 1);
+ mVerticalShiftForScale.value = mCurrentShift.value;
+ runActionOnRemoteHandles(remoteTargetHandle ->
+ remoteTargetHandle.getTransformParams().setHomeBuilderProxy(
+ FallbackHomeAnimationFactory.this
+ ::updateHomeActivityTransformDuringHomeAnim));
+ } else {
+ mHomeAlpha = new AnimatedFloat(this::updateHomeAlpha);
+ mHomeAlpha.value = 0;
+ mHomeAlphaParams.setHomeBuilderProxy(
+ this::updateHomeActivityTransformDuringHomeAnim);
+ }
+
+ mRecentsAlpha.value = 1;
+ runActionOnRemoteHandles(remoteTargetHandle ->
+ remoteTargetHandle.getTransformParams().setBaseBuilderProxy(
+ FallbackHomeAnimationFactory.this
+ ::updateRecentsActivityTransformDuringHomeAnim));
+ }
+
+ @NonNull
+ @Override
+ public RectF getWindowTargetRect() {
+ if (mTargetRect.isEmpty()) {
+ mTargetRect.set(super.getWindowTargetRect());
+ }
+ return mTargetRect;
+ }
+
+ private void updateRecentsActivityTransformDuringHomeAnim(SurfaceProperties builder,
+ RemoteAnimationTarget app, TransformParams params) {
+ builder.setAlpha(mRecentsAlpha.value);
+ }
+
+ private void updateHomeActivityTransformDuringHomeAnim(SurfaceProperties builder,
+ RemoteAnimationTarget app, TransformParams params) {
+ setHomeScaleAndAlpha(builder, app, mVerticalShiftForScale.value, mHomeAlpha.value);
+ }
+
+ @NonNull
+ @Override
+ public AnimatorPlaybackController createActivityAnimationToHome() {
+ PendingAnimation pa = new PendingAnimation(mDuration);
+ pa.setFloat(mRecentsAlpha, AnimatedFloat.VALUE, 0, ACCELERATE);
+ return pa.createPlaybackController();
+ }
+
+ private void updateHomeAlpha() {
+ if (mHomeAlphaParams.getTargetSet() != null) {
+ mHomeAlphaParams.applySurfaceParams(
+ mHomeAlphaParams.createSurfaceParams(BuilderProxy.NO_OP));
+ }
+ }
+
+ public boolean handleHomeTaskAppeared(RemoteAnimationTarget[] appearedTaskTargets) {
+ RemoteAnimationTarget appearedTaskTarget = appearedTaskTargets[0];
+ if (appearedTaskTarget.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME) {
+ RemoteAnimationTargets targets = new RemoteAnimationTargets(
+ new RemoteAnimationTarget[] {appearedTaskTarget},
+ new RemoteAnimationTarget[0], new RemoteAnimationTarget[0],
+ appearedTaskTarget.mode);
+ mHomeAlphaParams.setTargetSet(targets);
+ updateHomeAlpha();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void playAtomicAnimation(float velocity) {
+ ObjectAnimator alphaAnim = mHomeAlpha.animateToValue(mHomeAlpha.value, 1);
+ alphaAnim.setDuration(mDuration).setInterpolator(ACCELERATE);
+ alphaAnim.start();
+
+ if (mRunningOverHome) {
+ // Spring back launcher scale
+ new SpringAnimationBuilder(mContext)
+ .setStartValue(mVerticalShiftForScale.value)
+ .setEndValue(0)
+ .setStartVelocity(-velocity / mTransitionDragLength)
+ .setMinimumVisibleChange(1f / mDp.heightPx)
+ .setDampingRatio(0.6f)
+ .setStiffness(800)
+ .build(mVerticalShiftForScale, AnimatedFloat.VALUE)
+ .start();
+ }
+ }
+
+ @Override
+ public void setAnimation(RectFSpringAnim anim) {
+ mSpringAnim = anim;
+ mSpringAnim.addAnimatorListener(forEndCallback(this::onRectAnimationEnd));
+ }
+
+ private void onRectAnimationEnd() {
+ mAnimationFinished = true;
+ maybeSendEndMessage();
+ }
+
+ private void maybeSendEndMessage() {
+ if (mAnimationFinished && mOnFinishCallback != null) {
+ try {
+ mOnFinishCallback.replyTo.send(mOnFinishCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error sending icon position", e);
+ }
+ }
+ }
+
+ @Override
+ public void accept(Message msg) {
+ try {
+ Bundle data = msg.getData();
+ RectF position = data.getParcelable(EXTRA_ICON_POSITION);
+ if (!position.isEmpty()) {
+ mSurfaceControl = data.getParcelable(EXTRA_ICON_SURFACE);
+ mTargetRect.set(position);
+ if (mSpringAnim != null) {
+ mSpringAnim.onTargetPositionChanged();
+ }
+ }
+ mOnFinishCallback = data.getParcelable(EXTRA_ON_FINISH_CALLBACK);
+ maybeSendEndMessage();
+ } catch (Exception e) {
+ // Ignore
+ }
+ }
+
+ @Override
+ public void update(RectF currentRect, float progress, float radius, int overlayAlpha) {
+ if (mSurfaceControl != null) {
+ currentRect.roundOut(mTempRect);
+ Transaction t = new Transaction();
+ try {
+ t.setGeometry(mSurfaceControl, null, mTempRect, Surface.ROTATION_0);
+ t.apply();
+ } catch (RuntimeException e) {
+ // Ignore
+ }
+ }
+ }
+
+ private void addGestureContract(Intent intent, RunningTaskInfo runningTaskInfo) {
+ if (mRunningOverHome || runningTaskInfo == null) {
+ return;
+ }
+
+ TaskKey key = new TaskKey(runningTaskInfo);
+ if (key.getComponent() != null) {
+ if (sMessageReceiver == null) {
+ sMessageReceiver = new StaticMessageReceiver();
+ }
+
+ Bundle gestureNavContract = new Bundle();
+ gestureNavContract.putParcelable(EXTRA_COMPONENT_NAME, key.getComponent());
+ gestureNavContract.putParcelable(EXTRA_USER, UserHandle.of(key.userId));
+ gestureNavContract.putParcelable(
+ EXTRA_REMOTE_CALLBACK, sMessageReceiver.newCallback(this));
+ intent.putExtra(EXTRA_GESTURE_CONTRACT, gestureNavContract);
+ }
+ }
+ }
+
+ private static class StaticMessageReceiver implements Handler.Callback {
+
+ private final Messenger mMessenger =
+ new Messenger(new Handler(Looper.getMainLooper(), this));
+
+ private ParcelUuid mCurrentUID = new ParcelUuid(UUID.randomUUID());
+ private WeakReference<Consumer<Message>> mCurrentCallback = new WeakReference<>(null);
+
+ public Message newCallback(Consumer<Message> callback) {
+ mCurrentUID = new ParcelUuid(UUID.randomUUID());
+ mCurrentCallback = new WeakReference<>(callback);
+
+ Message msg = Message.obtain();
+ msg.replyTo = mMessenger;
+ msg.obj = mCurrentUID;
+ return msg;
+ }
+
+ @Override
+ public boolean handleMessage(@NonNull Message message) {
+ if (mCurrentUID.equals(message.obj)) {
+ Consumer<Message> consumer = mCurrentCallback.get();
+ if (consumer != null) {
+ consumer.accept(message);
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/BubbleBarInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/BubbleBarInputConsumer.java
index 92031c5..778c231 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/BubbleBarInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/BubbleBarInputConsumer.java
@@ -23,10 +23,12 @@
import android.view.MotionEvent;
import android.view.ViewConfiguration;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.taskbar.TaskbarActivityContext;
+import com.android.launcher3.taskbar.bubbles.BubbleBarSwipeController;
import com.android.launcher3.taskbar.bubbles.BubbleBarViewController;
import com.android.launcher3.taskbar.bubbles.BubbleControllers;
-import com.android.launcher3.taskbar.bubbles.BubbleDragController;
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
@@ -40,10 +42,11 @@
private final BubbleStashController mBubbleStashController;
private final BubbleBarViewController mBubbleBarViewController;
- private final BubbleDragController mBubbleDragController;
+ @Nullable
+ private final BubbleBarSwipeController mBubbleBarSwipeController;
private final InputMonitorCompat mInputMonitorCompat;
- private boolean mSwipeUpOnBubbleHandle;
+ private boolean mPilfered;
private boolean mPassedTouchSlop;
private boolean mStashedOrCollapsedOnDown;
@@ -57,7 +60,8 @@
InputMonitorCompat inputMonitorCompat) {
mBubbleStashController = bubbleControllers.bubbleStashController;
mBubbleBarViewController = bubbleControllers.bubbleBarViewController;
- mBubbleDragController = bubbleControllers.bubbleDragController;
+ mBubbleBarSwipeController = bubbleControllers.bubbleBarSwipeController.orElse(null);
+
mInputMonitorCompat = inputMonitorCompat;
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
mTimeForTap = ViewConfiguration.getTapTimeout();
@@ -77,6 +81,9 @@
mDownPos.set(ev.getX(), ev.getY());
mLastPos.set(mDownPos);
mStashedOrCollapsedOnDown = mBubbleStashController.isStashed() || isCollapsed();
+ if (mBubbleBarSwipeController != null) {
+ mBubbleBarSwipeController.start();
+ }
break;
case MotionEvent.ACTION_MOVE:
int pointerIndex = ev.findPointerIndex(mActivePointerId);
@@ -90,11 +97,10 @@
if (!mPassedTouchSlop) {
mPassedTouchSlop = Math.abs(dY) > mTouchSlop || Math.abs(dX) > mTouchSlop;
}
- if (mStashedOrCollapsedOnDown && !mSwipeUpOnBubbleHandle && mPassedTouchSlop) {
- boolean verticalGesture = Math.abs(dY) > Math.abs(dX);
- if (verticalGesture && !mBubbleDragController.isDragging()) {
- mSwipeUpOnBubbleHandle = true;
- mBubbleStashController.showBubbleBar(/* expandBubbles= */ true);
+ if (mBubbleBarSwipeController != null) {
+ mBubbleBarSwipeController.swipeTo(dY);
+ if (!mPilfered && mBubbleBarSwipeController.isSwipeGesture()) {
+ mPilfered = true;
// Bubbles is handling the swipe so make sure no one else gets it.
TestLogging.recordEvent(TestProtocol.SEQUENCE_PILFER, "pilferPointers");
mInputMonitorCompat.pilferPointers();
@@ -102,8 +108,10 @@
}
break;
case MotionEvent.ACTION_UP:
+ boolean swipeUpOnBubbleHandle = mBubbleBarSwipeController != null
+ && mBubbleBarSwipeController.isSwipeGesture();
boolean isWithinTapTime = ev.getEventTime() - ev.getDownTime() <= mTimeForTap;
- if (isWithinTapTime && !mSwipeUpOnBubbleHandle && !mPassedTouchSlop
+ if (isWithinTapTime && !swipeUpOnBubbleHandle && !mPassedTouchSlop
&& mStashedOrCollapsedOnDown) {
// Taps on the handle / collapsed state should open the bar
mBubbleStashController.showBubbleBar(/* expandBubbles= */ true);
@@ -116,8 +124,11 @@
}
private void cleanupAfterMotionEvent() {
+ if (mBubbleBarSwipeController != null) {
+ mBubbleBarSwipeController.finish();
+ }
mPassedTouchSlop = false;
- mSwipeUpOnBubbleHandle = false;
+ mPilfered = false;
}
private boolean isCollapsed() {
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
index 5557639..4afd92a 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
@@ -5,7 +5,7 @@
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.quickstep.InputConsumer;
-import com.android.quickstep.util.ActiveGestureLog;
+import com.android.quickstep.util.ActiveGestureProtoLogProxy;
import com.android.systemui.shared.system.InputMonitorCompat;
public abstract class DelegateInputConsumer implements InputConsumer {
@@ -57,8 +57,7 @@
}
protected void setActive(MotionEvent ev) {
- ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(getDelegatorName())
- .append(" became active"));
+ ActiveGestureProtoLogProxy.logInputConsumerBecameActive(getDelegatorName());
mState = STATE_ACTIVE;
TestLogging.recordEvent(TestProtocol.SEQUENCE_PILFER, "pilferPointers");
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 69d3bc9..e19b338 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -41,6 +41,7 @@
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;
@@ -424,7 +425,10 @@
mTaskAnimationManager.notifyRecentsAnimationState(mInteractionHandler);
notifyGestureStarted(true /*isLikelyToStartNewTask*/);
} else {
- Intent intent = new Intent(mInteractionHandler.getLaunchIntent());
+ // todo differentiate intent based on if we are on home or in app for overview in window
+ Intent intent = new Intent(Flags.enableFallbackOverviewInWindow()
+ ? mInteractionHandler.getHomeIntent()
+ : mInteractionHandler.getLaunchIntent());
intent.putExtra(INTENT_EXTRA_LOG_TRACE_ID, mGestureState.getGestureId());
mActiveCallbacks = mTaskAnimationManager.startRecentsAnimation(mGestureState, intent,
mInteractionHandler);
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
index c61f71d..a236eca 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
@@ -28,6 +28,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.statemanager.BaseState;
+import com.android.launcher3.statemanager.StatefulContainer;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.views.BaseDragLayer;
@@ -41,7 +42,8 @@
/**
* Input consumer for handling touch on the recents/Launcher activity.
*/
-public class OverviewInputConsumer<S extends BaseState<S>, T extends RecentsViewContainer>
+public class OverviewInputConsumer<S extends BaseState<S>,
+ T extends RecentsViewContainer & StatefulContainer<S>>
implements InputConsumer {
private final T mContainer;
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
index 9284e13..5ad55ae 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
@@ -184,7 +184,7 @@
if (!mHasPassedTaskbarNavThreshold && passedTaskbarNavThreshold
&& !mGestureState.isInExtendedSlopRegion()) {
mHasPassedTaskbarNavThreshold = true;
- mTaskbarActivityContext.onSwipeToUnstashTaskbar();
+ mTaskbarActivityContext.onSwipeToUnstashTaskbar(true);
}
if (dY < 0) {
@@ -287,7 +287,7 @@
// start a single unstash timeout if hovering bottom edge under the hinted taskbar.
if (!sUnstashHandler.hasMessagesOrCallbacks()) {
sUnstashHandler.postDelayed(() -> {
- mTaskbarActivityContext.onSwipeToUnstashTaskbar();
+ mTaskbarActivityContext.onSwipeToUnstashTaskbar(false);
mIsStashedTaskbarHovered = false;
}, HOVER_TASKBAR_UNSTASH_TIMEOUT);
}
@@ -315,7 +315,7 @@
startStashedTaskbarHover(/* isHovered = */ true);
} else if (mBottomEdgeBounds.contains(x, y)) {
// If hover screen's bottom edge not below the stashed taskbar, unstash it.
- mTaskbarActivityContext.onSwipeToUnstashTaskbar();
+ mTaskbarActivityContext.onSwipeToUnstashTaskbar(false);
}
}
diff --git a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
index 1bec970..f7f3157 100644
--- a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
@@ -62,6 +62,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatedFloat;
@@ -103,6 +104,9 @@
private final AnimatedFloat mSwipeProgress = new AnimatedFloat(this::onSwipeProgressUpdate);
+ private final InvariantDeviceProfile.OnIDPChangeListener mOnIDPChangeListener =
+ modelPropertiesChanged -> updateHint();
+
private TISBindHelper mTISBindHelper;
private BgDrawable mBackground;
@@ -115,6 +119,8 @@
private AnimatorPlaybackController mLauncherStartAnim = null;
+ private TextView mHintView;
+
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -167,12 +173,9 @@
}
});
- TextView hint = findViewById(R.id.hint);
- DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(this).getDeviceProfile(this);
- if (!dp.isGestureMode) {
- hint.setText(R.string.allset_button_hint);
- }
- hint.setAccessibilityDelegate(new SkipButtonAccessibilityDelegate());
+ mHintView = findViewById(R.id.hint);
+ mHintView.setAccessibilityDelegate(new SkipButtonAccessibilityDelegate());
+ updateHint();
mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
@@ -190,7 +193,21 @@
LOTTIE_TERTIARY_COLOR_TOKEN, R.color.all_set_bg_tertiary),
getTheme());
- startBackgroundAnimation(dp.isTablet);
+ startBackgroundAnimation(getDP().isTablet);
+ getIDP().addOnChangeListener(mOnIDPChangeListener);
+ }
+
+ private InvariantDeviceProfile getIDP() {
+ return LauncherAppState.getInstance(this).getInvariantDeviceProfile();
+ }
+
+ private DeviceProfile getDP() {
+ return getIDP().getDeviceProfile(this);
+ }
+
+ private void updateHint() {
+ mHintView.setText(
+ getDP().isGestureMode ? R.string.allset_hint : R.string.allset_button_hint);
}
private void runOnUiHelperThread(Runnable runnable) {
@@ -202,7 +219,7 @@
}
private void startBackgroundAnimation(boolean forTablet) {
- if (!Utilities.ATLEAST_S || mVibrator == null) {
+ if (mVibrator == null) {
return;
}
boolean supportsThud = mVibrator.areAllPrimitivesSupported(
@@ -311,6 +328,7 @@
@Override
protected void onDestroy() {
super.onDestroy();
+ getIDP().removeOnChangeListener(mOnIDPChangeListener);
mTISBindHelper.onDestroy();
clearBinderOverride();
if (mBackgroundAnimatorListener != null) {
diff --git a/quickstep/src/com/android/quickstep/interaction/AnimatedTaskView.java b/quickstep/src/com/android/quickstep/interaction/AnimatedTaskView.java
index 742b0fc..7a86db3 100644
--- a/quickstep/src/com/android/quickstep/interaction/AnimatedTaskView.java
+++ b/quickstep/src/com/android/quickstep/interaction/AnimatedTaskView.java
@@ -15,8 +15,6 @@
*/
package com.android.quickstep.interaction;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_NEW_GESTURE_NAV_TUTORIAL;
-
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
@@ -175,11 +173,8 @@
void setFakeTaskViewFillColor(@ColorInt int colorResId) {
mFullTaskView.setBackgroundColor(colorResId);
-
- if (ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()){
- mTopTaskView.getBackground().setTint(colorResId);
- mBottomTaskView.getBackground().setTint(colorResId);
- }
+ mTopTaskView.getBackground().setTint(colorResId);
+ mBottomTaskView.getBackground().setTint(colorResId);
}
@Override
diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
index 6757cd8..be7f8e5 100644
--- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
@@ -15,7 +15,6 @@
*/
package com.android.quickstep.interaction;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_NEW_GESTURE_NAV_TUTORIAL;
import static com.android.quickstep.interaction.TutorialController.TutorialType.BACK_NAVIGATION;
import static com.android.quickstep.interaction.TutorialController.TutorialType.BACK_NAVIGATION_COMPLETE;
@@ -40,35 +39,29 @@
BackGestureTutorialController(BackGestureTutorialFragment fragment, TutorialType tutorialType) {
super(fragment, tutorialType);
// Set the Lottie animation colors specifically for the Back gesture
- if (ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()) {
- LottieAnimationColorUtils.updateToArgbColors(
- mAnimatedGestureDemonstration,
- Map.of(".onSurfaceBack", fragment.mRootView.mColorOnSurfaceBack,
- ".surfaceBack", fragment.mRootView.mColorSurfaceBack,
- ".secondaryBack", fragment.mRootView.mColorSecondaryBack));
+ LottieAnimationColorUtils.updateToArgbColors(
+ mAnimatedGestureDemonstration,
+ Map.of(".onSurfaceBack", fragment.mRootView.mColorOnSurfaceBack,
+ ".surfaceBack", fragment.mRootView.mColorSurfaceBack,
+ ".secondaryBack", fragment.mRootView.mColorSecondaryBack));
- LottieAnimationColorUtils.updateToArgbColors(
- mCheckmarkAnimation,
- Map.of(".checkmark",
- Utilities.isDarkTheme(mContext)
- ? fragment.mRootView.mColorOnSurfaceBack
- : fragment.mRootView.mColorSecondaryBack,
- ".checkmarkBackground", fragment.mRootView.mColorSurfaceBack));
- }
+ LottieAnimationColorUtils.updateToArgbColors(
+ mCheckmarkAnimation,
+ Map.of(".checkmark",
+ Utilities.isDarkTheme(mContext)
+ ? fragment.mRootView.mColorOnSurfaceBack
+ : fragment.mRootView.mColorSecondaryBack,
+ ".checkmarkBackground", fragment.mRootView.mColorSurfaceBack));
}
@Override
public int getIntroductionTitle() {
- return ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()
- ? R.string.back_gesture_tutorial_title
- : R.string.back_gesture_intro_title;
+ return R.string.back_gesture_tutorial_title;
}
@Override
public int getIntroductionSubtitle() {
- return ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()
- ? R.string.back_gesture_tutorial_subtitle
- : R.string.back_gesture_intro_subtitle;
+ return R.string.back_gesture_tutorial_subtitle;
}
@Override
@@ -85,9 +78,7 @@
public int getSuccessFeedbackSubtitle() {
return mTutorialFragment.isAtFinalStep()
? R.string.back_gesture_feedback_complete_without_follow_up
- : ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()
- ? R.string.back_gesture_feedback_complete_with_follow_up
- : R.string.back_gesture_feedback_complete_with_overview_follow_up;
+ : R.string.back_gesture_feedback_complete_with_follow_up;
}
@Override
@@ -128,20 +119,12 @@
@LayoutRes
int getMockAppTaskCurrentPageLayoutResId() {
- return ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()
- ? R.layout.back_gesture_tutorial_background
- : mTutorialFragment.isLargeScreen()
- ? R.layout.gesture_tutorial_tablet_mock_conversation
- : R.layout.gesture_tutorial_mock_conversation;
+ return R.layout.back_gesture_tutorial_background;
}
@LayoutRes
int getMockAppTaskPreviousPageLayoutResId() {
- return ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()
- ? R.layout.back_gesture_tutorial_background
- : mTutorialFragment.isLargeScreen()
- ? R.layout.gesture_tutorial_tablet_mock_conversation_list
- : R.layout.gesture_tutorial_mock_conversation_list;
+ return R.layout.back_gesture_tutorial_background;
}
@Override
@@ -214,17 +197,13 @@
}
private void handleBackAttempt(BackGestureResult result) {
- if (ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()) {
- resetViewsForBackGesture();
- }
+ resetViewsForBackGesture();
switch (result) {
case BACK_COMPLETED_FROM_LEFT:
case BACK_COMPLETED_FROM_RIGHT:
mTutorialFragment.releaseFeedbackAnimation();
- if (ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()) {
- mExitingAppView.setVisibility(View.GONE);
- }
+ mExitingAppView.setVisibility(View.GONE);
updateFakeAppTaskViewLayout(getMockAppTaskPreviousPageLayoutResId());
showSuccessFeedback();
break;
diff --git a/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java b/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java
index 1b12be8..700fbf8 100644
--- a/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java
+++ b/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java
@@ -15,8 +15,6 @@
*/
package com.android.quickstep.interaction;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_NEW_GESTURE_NAV_TUTORIAL;
-
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Point;
@@ -211,10 +209,8 @@
}
}
- if (ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()) {
- mGestureCallback.onBackGestureProgress(ev.getX() - mDownPoint.x,
- ev.getY() - mDownPoint.y, mEdgeBackPanel.getIsLeftPanel());
- }
+ mGestureCallback.onBackGestureProgress(ev.getX() - mDownPoint.x,
+ ev.getY() - mDownPoint.y, mEdgeBackPanel.getIsLeftPanel());
// forward touch
mEdgeBackPanel.onMotionEvent(ev);
diff --git a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
index acc9959..bc5cc15 100644
--- a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
@@ -37,7 +37,6 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.R;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.StatsLogManager;
import com.android.quickstep.TouchInteractionService.TISBinder;
import com.android.quickstep.interaction.TutorialController.TutorialType;
@@ -79,9 +78,7 @@
Bundle args = savedInstanceState == null ? getIntent().getExtras() : savedInstanceState;
boolean gestureComplete = args != null && args.getBoolean(KEY_GESTURE_COMPLETE, false);
- if (FeatureFlags.ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()
- && args != null
- && args.getBoolean(KEY_USE_TUTORIAL_MENU, false)) {
+ if (args != null && args.getBoolean(KEY_USE_TUTORIAL_MENU, false)) {
mTutorialSteps = null;
TutorialType tutorialTypeOverride = (TutorialType) args.get(KEY_TUTORIAL_TYPE);
mCurrentFragment = tutorialTypeOverride == null
@@ -101,9 +98,7 @@
.add(R.id.gesture_tutorial_fragment_container, mCurrentFragment)
.commit();
- if (FeatureFlags.ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()) {
- correctUserOrientation();
- }
+ correctUserOrientation();
mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
initWindowInsets();
@@ -115,9 +110,7 @@
super.onConfigurationChanged(newConfig);
// Ensure the prompt to rotate the screen is updated
- if (FeatureFlags.ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()) {
- correctUserOrientation();
- }
+ correctUserOrientation();
}
private void initWindowInsets() {
diff --git a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
index 1129e02..bf4eaf2 100644
--- a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
@@ -15,8 +15,6 @@
*/
package com.android.quickstep.interaction;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_NEW_GESTURE_NAV_TUTORIAL;
-
import android.graphics.PointF;
import com.android.launcher3.R;
@@ -34,35 +32,29 @@
super(fragment, tutorialType);
// Set the Lottie animation colors specifically for the Home gesture
- if (ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()) {
- LottieAnimationColorUtils.updateToArgbColors(
- mAnimatedGestureDemonstration,
- Map.of(".onSurfaceHome", fragment.mRootView.mColorOnSurfaceHome,
- ".surfaceHome", fragment.mRootView.mColorSurfaceHome,
- ".secondaryHome", fragment.mRootView.mColorSecondaryHome));
+ LottieAnimationColorUtils.updateToArgbColors(
+ mAnimatedGestureDemonstration,
+ Map.of(".onSurfaceHome", fragment.mRootView.mColorOnSurfaceHome,
+ ".surfaceHome", fragment.mRootView.mColorSurfaceHome,
+ ".secondaryHome", fragment.mRootView.mColorSecondaryHome));
- LottieAnimationColorUtils.updateToArgbColors(
- mCheckmarkAnimation,
- Map.of(".checkmark",
- Utilities.isDarkTheme(mContext)
- ? fragment.mRootView.mColorOnSurfaceHome
- : fragment.mRootView.mColorSecondaryHome,
- ".checkmarkBackground", fragment.mRootView.mColorSurfaceHome));
- }
+ LottieAnimationColorUtils.updateToArgbColors(
+ mCheckmarkAnimation,
+ Map.of(".checkmark",
+ Utilities.isDarkTheme(mContext)
+ ? fragment.mRootView.mColorOnSurfaceHome
+ : fragment.mRootView.mColorSecondaryHome,
+ ".checkmarkBackground", fragment.mRootView.mColorSurfaceHome));
}
@Override
public int getIntroductionTitle() {
- return ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()
- ? R.string.home_gesture_tutorial_title
- : R.string.home_gesture_intro_title;
+ return R.string.home_gesture_tutorial_title;
}
@Override
public int getIntroductionSubtitle() {
- return ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()
- ? R.string.home_gesture_tutorial_subtitle
- : R.string.home_gesture_intro_subtitle;
+ return R.string.home_gesture_tutorial_subtitle;
}
@Override
@@ -72,9 +64,7 @@
@Override
public int getSuccessFeedbackTitle() {
- return ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()
- ? R.string.home_gesture_tutorial_success
- : R.string.gesture_tutorial_nice;
+ return R.string.home_gesture_tutorial_success;
}
@Override
diff --git a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
index a04dd44..e45f8d8 100644
--- a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
@@ -16,7 +16,6 @@
package com.android.quickstep.interaction;
import static com.android.app.animation.Interpolators.ACCELERATE;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_NEW_GESTURE_NAV_TUTORIAL;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -49,34 +48,28 @@
super(fragment, tutorialType);
// Set the Lottie animation colors specifically for the Overview gesture
- if (ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()) {
- LottieAnimationColorUtils.updateToArgbColors(
- mAnimatedGestureDemonstration,
- Map.of(".onSurfaceOverview", fragment.mRootView.mColorOnSurfaceOverview,
- ".surfaceOverview", fragment.mRootView.mColorSurfaceOverview,
- ".secondaryOverview", fragment.mRootView.mColorSecondaryOverview));
+ LottieAnimationColorUtils.updateToArgbColors(
+ mAnimatedGestureDemonstration,
+ Map.of(".onSurfaceOverview", fragment.mRootView.mColorOnSurfaceOverview,
+ ".surfaceOverview", fragment.mRootView.mColorSurfaceOverview,
+ ".secondaryOverview", fragment.mRootView.mColorSecondaryOverview));
- LottieAnimationColorUtils.updateToArgbColors(
- mCheckmarkAnimation,
- Map.of(".checkmark",
- Utilities.isDarkTheme(mContext)
- ? fragment.mRootView.mColorOnSurfaceOverview
- : fragment.mRootView.mColorSecondaryOverview,
- ".checkmarkBackground", fragment.mRootView.mColorSurfaceOverview));
- }
+ LottieAnimationColorUtils.updateToArgbColors(
+ mCheckmarkAnimation,
+ Map.of(".checkmark",
+ Utilities.isDarkTheme(mContext)
+ ? fragment.mRootView.mColorOnSurfaceOverview
+ : fragment.mRootView.mColorSecondaryOverview,
+ ".checkmarkBackground", fragment.mRootView.mColorSurfaceOverview));
}
@Override
public int getIntroductionTitle() {
- return ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()
- ? R.string.overview_gesture_tutorial_title
- : R.string.overview_gesture_intro_title;
+ return R.string.overview_gesture_tutorial_title;
}
@Override
public int getIntroductionSubtitle() {
- return ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()
- ? R.string.overview_gesture_tutorial_subtitle
- : R.string.overview_gesture_intro_subtitle;
+ return R.string.overview_gesture_tutorial_subtitle;
}
@Override
@@ -86,9 +79,7 @@
@Override
public int getSuccessFeedbackTitle() {
- return ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()
- ? R.string.overview_gesture_tutorial_success
- : R.string.gesture_tutorial_nice;
+ return R.string.overview_gesture_tutorial_success;
}
@Override
@@ -168,10 +159,7 @@
@Override
protected int getMockPreviousAppTaskThumbnailColor() {
- return ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()
- ? mTutorialFragment.mRootView.mColorSurfaceContainer
- : mContext.getResources().getColor(
- R.color.gesture_tutorial_fake_previous_task_view_color);
+ return mTutorialFragment.mRootView.mColorSurfaceContainer;
}
@Override
@@ -224,11 +212,8 @@
case OVERVIEW_GESTURE_COMPLETED:
setGestureCompleted();
mTutorialFragment.releaseFeedbackAnimation();
- animateTaskViewToOverview(ENABLE_NEW_GESTURE_NAV_TUTORIAL.get());
+ animateTaskViewToOverview(true);
onMotionPaused(true /*arbitrary value*/);
- if (!ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()) {
- showSuccessFeedback();
- }
break;
case HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION:
case HOME_OR_OVERVIEW_CANCELLED:
diff --git a/quickstep/src/com/android/quickstep/interaction/RootSandboxLayout.java b/quickstep/src/com/android/quickstep/interaction/RootSandboxLayout.java
index affedb9..d733267 100644
--- a/quickstep/src/com/android/quickstep/interaction/RootSandboxLayout.java
+++ b/quickstep/src/com/android/quickstep/interaction/RootSandboxLayout.java
@@ -15,16 +15,12 @@
*/
package com.android.quickstep.interaction;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_NEW_GESTURE_NAV_TUTORIAL;
-
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.Insets;
-import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
-import android.view.View;
import android.view.WindowInsets;
import android.widget.RelativeLayout;
@@ -39,10 +35,6 @@
/** Root layout that TutorialFragment uses to intercept motion events. */
public class RootSandboxLayout extends RelativeLayout {
- private final Rect mTempStepIndicatorBounds = new Rect();
- private final Rect mTempInclusionBounds = new Rect();
- private final Rect mTempExclusionBounds = new Rect();
-
@ColorInt final int mColorSurfaceContainer;
@ColorInt final int mColorOnSurfaceHome;
@ColorInt final int mColorSurfaceHome;
@@ -54,11 +46,6 @@
@ColorInt final int mColorSurfaceOverview;
@ColorInt final int mColorSecondaryOverview;
- private View mFeedbackView;
- private View mTutorialStepView;
- private View mSkipButton;
- private View mDoneButton;
-
public RootSandboxLayout(Context context) {
this(context, null);
}
@@ -123,56 +110,4 @@
return getHeight() + insets.top + insets.bottom;
}
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- if (ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()) {
- return;
- }
- mFeedbackView = findViewById(R.id.gesture_tutorial_fragment_feedback_view);
- mTutorialStepView =
- mFeedbackView.findViewById(R.id.gesture_tutorial_fragment_feedback_tutorial_step);
- mSkipButton = mFeedbackView.findViewById(R.id.gesture_tutorial_fragment_close_button);
- mDoneButton = mFeedbackView.findViewById(R.id.gesture_tutorial_fragment_action_button);
-
- mFeedbackView.addOnLayoutChangeListener(
- (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
- if (mSkipButton.getVisibility() != VISIBLE
- && mDoneButton.getVisibility() != VISIBLE) {
- return;
- }
- // Either the skip or the done button is ever shown at once, never both.
- boolean showingSkipButton = mSkipButton.getVisibility() == VISIBLE;
- boolean isRTL = Utilities.isRtl(getContext().getResources());
- updateTutorialStepViewTranslation(
- showingSkipButton ? mSkipButton : mDoneButton,
- // Translate the step indicator away from whichever button is being
- // shown. The skip button in on the left in LTR or on the right in RTL.
- // The done button is on the right in LTR or left in RTL.
- (showingSkipButton && !isRTL) || (!showingSkipButton && isRTL));
- });
- }
-
- private void updateTutorialStepViewTranslation(
- @NonNull View anchorView, boolean translateToRight) {
- mTempStepIndicatorBounds.set(
- mTutorialStepView.getLeft(),
- mTutorialStepView.getTop(),
- mTutorialStepView.getRight(),
- mTutorialStepView.getBottom());
- mTempInclusionBounds.set(0, 0, mFeedbackView.getWidth(), mFeedbackView.getHeight());
- mTempExclusionBounds.set(
- anchorView.getLeft(),
- anchorView.getTop(),
- anchorView.getRight(),
- anchorView.getBottom());
-
- Utilities.translateOverlappingView(
- mTutorialStepView,
- mTempStepIndicatorBounds,
- mTempInclusionBounds,
- mTempExclusionBounds,
- translateToRight ? Utilities.TRANSLATE_RIGHT : Utilities.TRANSLATE_LEFT);
- }
}
diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index ad13efb..e462706 100644
--- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
@@ -48,7 +48,6 @@
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.config.FeatureFlags;
import com.android.quickstep.GestureState;
import com.android.quickstep.OverviewComponentObserver;
import com.android.quickstep.RecentsAnimationDeviceState;
@@ -127,9 +126,7 @@
void resetTaskViews() {
mFakeHotseatView.setVisibility(View.INVISIBLE);
mFakeIconView.setVisibility(View.INVISIBLE);
- if (FeatureFlags.ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()) {
- mFakeIconView.getBackground().setTint(getFakeTaskViewColor());
- }
+ mFakeIconView.getBackground().setTint(getFakeTaskViewColor());
if (mTutorialFragment.getActivity() != null) {
int height = mTutorialFragment.getRootView().getFullscreenHeight();
int width = mTutorialFragment.getRootView().getWidth();
@@ -138,9 +135,7 @@
mFakeTaskViewRadius = 0;
mFakeTaskView.invalidateOutline();
mFakeTaskView.setVisibility(View.VISIBLE);
- if (FeatureFlags.ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()) {
- mFakeTaskView.setBackgroundColor(getFakeTaskViewColor());
- }
+ mFakeTaskView.setBackgroundColor(getFakeTaskViewColor());
mFakeTaskView.setAlpha(1);
mFakePreviousTaskView.setVisibility(View.INVISIBLE);
mFakePreviousTaskView.setAlpha(1);
@@ -390,12 +385,10 @@
false, /* isOpening */
mFakeIconView, mDp);
mFakeIconView.setAlpha(1);
- if (FeatureFlags.ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()) {
- int iconColor = ColorUtils.blendARGB(
- getFakeTaskViewColor(), getHotseatIconColor(), progress);
- mFakeIconView.getBackground().setTint(iconColor);
- mFakeTaskView.setBackgroundColor(iconColor);
- }
+ int iconColor = ColorUtils.blendARGB(
+ getFakeTaskViewColor(), getHotseatIconColor(), progress);
+ mFakeIconView.getBackground().setTint(iconColor);
+ mFakeTaskView.setBackgroundColor(iconColor);
mFakeTaskView.setAlpha(getWindowAlpha(progress));
mFakePreviousTaskView.setAlpha(getWindowAlpha(progress));
}
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
index 54653fa..5028da4 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
@@ -19,10 +19,7 @@
import static android.view.View.NO_ID;
import static android.view.View.inflate;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_NEW_GESTURE_NAV_TUTORIAL;
-
import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
@@ -33,7 +30,6 @@
import android.graphics.Matrix;
import android.graphics.Outline;
import android.graphics.Rect;
-import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.RippleDrawable;
import android.util.Log;
@@ -52,7 +48,6 @@
import androidx.annotation.ColorInt;
import androidx.annotation.DrawableRes;
import androidx.annotation.LayoutRes;
-import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.annotation.StyleRes;
@@ -153,8 +148,7 @@
mFakeHotseatView = rootView.findViewById(R.id.gesture_tutorial_fake_hotseat_view);
mFakeIconView = rootView.findViewById(R.id.gesture_tutorial_fake_icon_view);
mFakeTaskView = rootView.findViewById(R.id.gesture_tutorial_fake_task_view);
- mFakeTaskbarView = ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()
- ? null : rootView.findViewById(R.id.gesture_tutorial_fake_taskbar_view);
+ mFakeTaskbarView = null;
mFakePreviousTaskView =
rootView.findViewById(R.id.gesture_tutorial_fake_previous_task_view);
mRippleView = rootView.findViewById(R.id.gesture_tutorial_ripple_view);
@@ -165,32 +159,30 @@
mFingerDotView = rootView.findViewById(R.id.gesture_tutorial_finger_dot);
mSkipTutorialDialog = createSkipTutorialDialog();
- if (ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()) {
- mFullGestureDemonstration = rootView.findViewById(R.id.full_gesture_demonstration);
- mCheckmarkAnimation = rootView.findViewById(R.id.checkmark_animation);
- mAnimatedGestureDemonstration = rootView.findViewById(
- R.id.gesture_demonstration_animations);
- mExitingAppView = rootView.findViewById(R.id.exiting_app_back);
- mScreenWidth = mTutorialFragment.getDeviceProfile().widthPx;
- mScreenHeight = mTutorialFragment.getDeviceProfile().heightPx;
- mExitingAppMargin = mContext.getResources().getDimensionPixelSize(
- R.dimen.gesture_tutorial_back_gesture_exiting_app_margin);
- mExitingAppStartingCornerRadius = QuickStepContract.getWindowCornerRadius(mContext);
- mExitingAppEndingCornerRadius = mContext.getResources().getDimensionPixelSize(
- R.dimen.gesture_tutorial_back_gesture_end_corner_radius);
- mAnimatedGestureDemonstration.addLottieOnCompositionLoadedListener(
- this::createScalingMatrix);
+ mFullGestureDemonstration = rootView.findViewById(R.id.full_gesture_demonstration);
+ mCheckmarkAnimation = rootView.findViewById(R.id.checkmark_animation);
+ mAnimatedGestureDemonstration = rootView.findViewById(
+ R.id.gesture_demonstration_animations);
+ mExitingAppView = rootView.findViewById(R.id.exiting_app_back);
+ mScreenWidth = mTutorialFragment.getDeviceProfile().widthPx;
+ mScreenHeight = mTutorialFragment.getDeviceProfile().heightPx;
+ mExitingAppMargin = mContext.getResources().getDimensionPixelSize(
+ R.dimen.gesture_tutorial_back_gesture_exiting_app_margin);
+ mExitingAppStartingCornerRadius = QuickStepContract.getWindowCornerRadius(mContext);
+ mExitingAppEndingCornerRadius = mContext.getResources().getDimensionPixelSize(
+ R.dimen.gesture_tutorial_back_gesture_end_corner_radius);
+ mAnimatedGestureDemonstration.addLottieOnCompositionLoadedListener(
+ this::createScalingMatrix);
- mFeedbackTitleView.setText(getIntroductionTitle());
- mFeedbackSubtitleView.setText(getIntroductionSubtitle());
- mExitingAppView.setClipToOutline(true);
- mExitingAppView.setOutlineProvider(new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- outline.setRoundRect(mExitingAppRect, mExitingAppRadius);
- }
- });
- }
+ mFeedbackTitleView.setText(getIntroductionTitle());
+ mFeedbackSubtitleView.setText(getIntroductionSubtitle());
+ mExitingAppView.setClipToOutline(true);
+ mExitingAppView.setOutlineProvider(new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ outline.setRoundRect(mExitingAppRect, mExitingAppRadius);
+ }
+ });
mTitleViewCallback = () -> mFeedbackTitleView.sendAccessibilityEvent(
AccessibilityEvent.TYPE_VIEW_FOCUSED);
@@ -261,19 +253,11 @@
@LayoutRes
protected int getMockHotseatResId() {
- if (ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()) {
- return mTutorialFragment.isLargeScreen()
- ? mTutorialFragment.isFoldable()
- ? R.layout.redesigned_gesture_tutorial_foldable_mock_hotseat
- : R.layout.redesigned_gesture_tutorial_tablet_mock_hotseat
- : R.layout.redesigned_gesture_tutorial_mock_hotseat;
- } else {
- return mTutorialFragment.isLargeScreen()
- ? mTutorialFragment.isFoldable()
- ? R.layout.gesture_tutorial_foldable_mock_hotseat
- : R.layout.gesture_tutorial_tablet_mock_hotseat
- : R.layout.gesture_tutorial_mock_hotseat;
- }
+ return mTutorialFragment.isLargeScreen()
+ ? mTutorialFragment.isFoldable()
+ ? R.layout.redesigned_gesture_tutorial_foldable_mock_hotseat
+ : R.layout.redesigned_gesture_tutorial_tablet_mock_hotseat
+ : R.layout.redesigned_gesture_tutorial_mock_hotseat;
}
@LayoutRes
@@ -312,9 +296,7 @@
@DrawableRes
public int getMockAppIconResId() {
- return ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()
- ? R.drawable.redesigned_hotseat_icon
- : R.drawable.default_sandbox_app_icon;
+ return R.drawable.redesigned_hotseat_icon;
}
@DrawableRes
@@ -374,11 +356,7 @@
mFeedbackView.setTranslationY(0);
return;
}
- Animator gestureAnimation = mTutorialFragment.getGestureAnimation();
- AnimatedVectorDrawable edgeAnimation = mTutorialFragment.getEdgeAnimation();
- if (gestureAnimation != null && edgeAnimation != null) {
- playFeedbackAnimation(gestureAnimation, edgeAnimation, mShowFeedbackRunnable, true);
- }
+ playFeedbackAnimation();
}
/**
@@ -442,12 +420,7 @@
}
mFeedbackTitleView.setText(titleResId);
- mFeedbackSubtitleView.setText(
- ENABLE_NEW_GESTURE_NAV_TUTORIAL.get() || spokenSubtitleResId == NO_ID
- ? mContext.getText(subtitleResId)
- : Utilities.wrapForTts(
- mContext.getText(subtitleResId),
- mContext.getString(spokenSubtitleResId)));
+ mFeedbackSubtitleView.setText(subtitleResId);
if (isGestureSuccessful) {
if (mTutorialFragment.isAtFinalStep()) {
showActionButton();
@@ -458,27 +431,16 @@
mFakeTaskViewCallback = null;
}
- if (ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()) {
- showSuccessPage();
- }
+ showSuccessPage();
}
mGestureCompleted = isGestureSuccessful;
-
- Animator gestureAnimation = mTutorialFragment.getGestureAnimation();
- AnimatedVectorDrawable edgeAnimation = mTutorialFragment.getEdgeAnimation();
- if (!isGestureSuccessful && gestureAnimation != null && edgeAnimation != null) {
- playFeedbackAnimation(
- gestureAnimation,
- edgeAnimation,
- mShowFeedbackRunnable,
- useGestureAnimationDelay);
- return;
+ if (!isGestureSuccessful) {
+ playFeedbackAnimation();
} else {
mTutorialFragment.releaseFeedbackAnimation();
+ mFeedbackViewCallback = mShowFeedbackRunnable;
+ mFeedbackView.post(mFeedbackViewCallback);
}
- mFeedbackViewCallback = mShowFeedbackRunnable;
-
- mFeedbackView.post(mFeedbackViewCallback);
}
private void showSuccessPage() {
@@ -517,79 +479,17 @@
mFeedbackTitleView.removeCallbacks(mTitleViewCallback);
}
- private void playFeedbackAnimation(
- @NonNull Animator gestureAnimation,
- @NonNull AnimatedVectorDrawable edgeAnimation,
- @NonNull Runnable onStartRunnable,
- boolean useGestureAnimationDelay) {
-
- if (ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()) {
- mFeedbackView.setVisibility(View.VISIBLE);
- mAnimatedGestureDemonstration.setVisibility(View.VISIBLE);
- mFullGestureDemonstration.setVisibility(View.VISIBLE);
- mAnimatedGestureDemonstration.playAnimation();
- return;
- }
-
- if (gestureAnimation.isRunning()) {
- gestureAnimation.cancel();
- }
- if (edgeAnimation.isRunning()) {
- edgeAnimation.reset();
- }
- gestureAnimation.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- super.onAnimationStart(animation);
-
- mEdgeGestureVideoView.setVisibility(GONE);
- if (edgeAnimation.isRunning()) {
- edgeAnimation.stop();
- }
-
- if (!useGestureAnimationDelay) {
- onStartRunnable.run();
- }
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
-
- mEdgeGestureVideoView.setVisibility(View.VISIBLE);
- edgeAnimation.start();
-
- gestureAnimation.removeListener(this);
- }
- });
-
- cancelQueuedGestureAnimation();
- if (useGestureAnimationDelay) {
- mFeedbackViewCallback = onStartRunnable;
- mFakeTaskViewCallback = gestureAnimation::start;
-
- mFeedbackView.post(mFeedbackViewCallback);
- mFakeTaskView.postDelayed(mFakeTaskViewCallback, GESTURE_ANIMATION_DELAY_MS);
- } else {
- gestureAnimation.start();
- }
+ private void playFeedbackAnimation() {
+ mFeedbackView.setVisibility(View.VISIBLE);
+ mAnimatedGestureDemonstration.setVisibility(View.VISIBLE);
+ mFullGestureDemonstration.setVisibility(View.VISIBLE);
+ mAnimatedGestureDemonstration.playAnimation();
}
void setRippleHotspot(float x, float y) {
mRippleDrawable.setHotspot(x, y);
}
- void showRippleEffect(@Nullable Runnable onCompleteRunnable) {
- mRippleDrawable.setState(
- new int[] {android.R.attr.state_pressed, android.R.attr.state_enabled});
- mRippleView.postDelayed(() -> {
- mRippleDrawable.setState(new int[] {});
- if (onCompleteRunnable != null) {
- onCompleteRunnable.run();
- }
- }, RIPPLE_VISIBLE_MS);
- }
-
void onActionButtonClicked(View button) {
mTutorialFragment.continueTutorial();
}
@@ -601,24 +501,19 @@
updateDrawables();
updateLayout();
- if (ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()) {
- mFeedbackTitleView.setTextAppearance(mContext, getTitleTextAppearance());
- mDoneButton.setTextAppearance(mContext, getDoneButtonTextAppearance());
- mDoneButton.getBackground().setTint(getDoneButtonColor());
- mCheckmarkAnimation.setAnimation(mTutorialFragment.isAtFinalStep()
- ? R.raw.checkmark_animation_end
- : R.raw.checkmark_animation_in_progress);
- if (!isGestureCompleted()) {
- mCheckmarkAnimation.setVisibility(GONE);
- startGestureAnimation();
- if (mTutorialType == TutorialType.BACK_NAVIGATION) {
- resetViewsForBackGesture();
- }
-
+ mFeedbackTitleView.setTextAppearance(mContext, getTitleTextAppearance());
+ mDoneButton.setTextAppearance(mContext, getDoneButtonTextAppearance());
+ mDoneButton.getBackground().setTint(getDoneButtonColor());
+ mCheckmarkAnimation.setAnimation(mTutorialFragment.isAtFinalStep()
+ ? R.raw.checkmark_animation_end
+ : R.raw.checkmark_animation_in_progress);
+ if (!isGestureCompleted()) {
+ mCheckmarkAnimation.setVisibility(GONE);
+ startGestureAnimation();
+ if (mTutorialType == TutorialType.BACK_NAVIGATION) {
+ resetViewsForBackGesture();
}
- } else {
- hideFeedback();
- hideActionButton();
+
}
mGestureCompleted = false;
@@ -654,13 +549,6 @@
: R.style.TextAppearance_GestureTutorial_Feedback_Subtext_Dark);
}
- void hideActionButton() {
- mSkipButton.setVisibility(View.VISIBLE);
- // Invisible to maintain the layout.
- mDoneButton.setVisibility(View.INVISIBLE);
- mDoneButton.setOnClickListener(null);
- }
-
void showActionButton() {
mSkipButton.setVisibility(GONE);
mDoneButton.setVisibility(View.VISIBLE);
@@ -711,10 +599,8 @@
}
private void updateSubtext() {
- if (!ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()) {
- mTutorialStepView.setTutorialProgress(
- mTutorialFragment.getCurrentStep(), mTutorialFragment.getNumSteps());
- }
+ mTutorialStepView.setTutorialProgress(
+ mTutorialFragment.getCurrentStep(), mTutorialFragment.getNumSteps());
}
private void updateHotseatChildViewColor(@Nullable View child) {
@@ -727,9 +613,7 @@
mTutorialFragment.getRootView().setBackground(AppCompatResources.getDrawable(
mContext, getMockWallpaperResId()));
mTutorialFragment.updateFeedbackAnimation();
- mFakeLauncherView.setBackgroundColor(ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()
- ? getFakeLauncherColor()
- : mContext.getColor(R.color.gesture_tutorial_fake_wallpaper_color));
+ mFakeLauncherView.setBackgroundColor(getFakeLauncherColor());
updateFakeViewLayout(mFakeHotseatView, getMockHotseatResId());
mHotseatIconView = mFakeHotseatView.findViewById(R.id.hotseat_icon_1);
mFakeTaskView.animate().alpha(1).setListener(
@@ -738,19 +622,15 @@
mFakeIconView.setBackground(AppCompatResources.getDrawable(
mContext, getMockAppIconResId()));
- if (ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()) {
- mExitingAppView.setBackgroundColor(getExitingAppColor());
- mFakeTaskView.setBackgroundColor(getFakeTaskViewColor());
- updateHotseatChildViewColor(mHotseatIconView);
- updateHotseatChildViewColor(mFakeHotseatView.findViewById(R.id.hotseat_icon_2));
- updateHotseatChildViewColor(mFakeHotseatView.findViewById(R.id.hotseat_icon_3));
- updateHotseatChildViewColor(mFakeHotseatView.findViewById(R.id.hotseat_icon_4));
- updateHotseatChildViewColor(mFakeHotseatView.findViewById(R.id.hotseat_icon_5));
- updateHotseatChildViewColor(mFakeHotseatView.findViewById(R.id.hotseat_icon_6));
- updateHotseatChildViewColor(mFakeHotseatView.findViewById(R.id.hotseat_search_bar));
- } else {
- updateFakeViewLayout(mFakeTaskView, getMockAppTaskLayoutResId());
- }
+ mExitingAppView.setBackgroundColor(getExitingAppColor());
+ mFakeTaskView.setBackgroundColor(getFakeTaskViewColor());
+ updateHotseatChildViewColor(mHotseatIconView);
+ updateHotseatChildViewColor(mFakeHotseatView.findViewById(R.id.hotseat_icon_2));
+ updateHotseatChildViewColor(mFakeHotseatView.findViewById(R.id.hotseat_icon_3));
+ updateHotseatChildViewColor(mFakeHotseatView.findViewById(R.id.hotseat_icon_4));
+ updateHotseatChildViewColor(mFakeHotseatView.findViewById(R.id.hotseat_icon_5));
+ updateHotseatChildViewColor(mFakeHotseatView.findViewById(R.id.hotseat_icon_6));
+ updateHotseatChildViewColor(mFakeHotseatView.findViewById(R.id.hotseat_search_bar));
}
}
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
index 0fafb94..2ff2c83 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
@@ -17,7 +17,6 @@
import static android.view.View.NO_ID;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_NEW_GESTURE_NAV_TUTORIAL;
import static com.android.quickstep.interaction.GestureSandboxActivity.KEY_GESTURE_COMPLETE;
import static com.android.quickstep.interaction.GestureSandboxActivity.KEY_TUTORIAL_TYPE;
import static com.android.quickstep.interaction.GestureSandboxActivity.KEY_USE_TUTORIAL_MENU;
@@ -215,9 +214,7 @@
super.onCreateView(inflater, container, savedInstanceState);
mRootView = (RootSandboxLayout) inflater.inflate(
- ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()
- ? R.layout.redesigned_gesture_tutorial_fragment
- : R.layout.gesture_tutorial_fragment,
+ R.layout.redesigned_gesture_tutorial_fragment,
container,
false);
@@ -383,10 +380,7 @@
if (mTutorialController != null && !isGestureComplete()) {
mTutorialController.hideFeedback();
}
-
- if (ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()) {
- mTutorialController.pauseAndHideLottieAnimation();
- }
+ mTutorialController.pauseAndHideLottieAnimation();
// Note: Using logical-or to ensure both functions get called.
return mEdgeBackGestureHandler.onTouch(view, motionEvent)
diff --git a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
index 717f6c8..995635f 100644
--- a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
+++ b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
@@ -43,16 +43,21 @@
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.R;
+import com.android.launcher3.dagger.ApplicationContext;
+import com.android.launcher3.dagger.LauncherAppSingleton;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.logging.StatsLogManager.StatsLogger;
import com.android.launcher3.model.DeviceGridState;
+import com.android.launcher3.util.DaggerSingletonObject;
+import com.android.launcher3.util.DaggerSingletonTracker;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.Info;
-import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.ExecutorUtil;
import com.android.launcher3.util.NavigationMode;
import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.util.SettingsCache;
+import com.android.quickstep.dagger.QuickstepBaseAppComponent;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -60,9 +65,12 @@
import java.io.IOException;
import java.util.Optional;
+import javax.inject.Inject;
+
/**
* Utility class to log launcher settings changes
*/
+@LauncherAppSingleton
public class SettingsChangeLogger implements
DisplayController.DisplayInfoChangeListener, OnSharedPreferenceChangeListener,
SafeCloseable {
@@ -70,8 +78,8 @@
/**
* Singleton instance
*/
- public static MainThreadInitializedObject<SettingsChangeLogger> INSTANCE =
- new MainThreadInitializedObject<>(SettingsChangeLogger::new);
+ public static DaggerSingletonObject<SettingsChangeLogger> INSTANCE =
+ new DaggerSingletonObject<>(QuickstepBaseAppComponent::getSettingsChangeLogger);
private static final String TAG = "SettingsChangeLogger";
private static final String BOOLEAN_PREF = "SwitchPreference";
@@ -84,25 +92,31 @@
private StatsLogManager.LauncherEvent mNotificationDotsEvent;
private StatsLogManager.LauncherEvent mHomeScreenSuggestionEvent;
- private SettingsChangeLogger(Context context) {
- this(context, StatsLogManager.newInstance(context));
+ @Inject
+ SettingsChangeLogger(@ApplicationContext Context context, DaggerSingletonTracker tracker) {
+ this(context, StatsLogManager.newInstance(context), tracker);
}
@VisibleForTesting
- SettingsChangeLogger(Context context, StatsLogManager statsLogManager) {
+ SettingsChangeLogger(Context context, StatsLogManager statsLogManager,
+ DaggerSingletonTracker tracker) {
mContext = context;
mStatsLogManager = statsLogManager;
mLoggablePrefs = loadPrefKeys(context);
- DisplayController.INSTANCE.get(context).addChangeListener(this);
- mNavMode = DisplayController.getNavigationMode(context);
- getPrefs(context).registerOnSharedPreferenceChangeListener(this);
- getDevicePrefs(context).registerOnSharedPreferenceChangeListener(this);
+ ExecutorUtil.executeSyncOnMainOrFail(() -> {
+ DisplayController.INSTANCE.get(context).addChangeListener(this);
+ mNavMode = DisplayController.getNavigationMode(context);
- SettingsCache mSettingsCache = SettingsCache.INSTANCE.get(context);
- mSettingsCache.register(NOTIFICATION_BADGING_URI,
- this::onNotificationDotsChanged);
- onNotificationDotsChanged(mSettingsCache.getValue(NOTIFICATION_BADGING_URI));
+ getPrefs(context).registerOnSharedPreferenceChangeListener(this);
+ getDevicePrefs(context).registerOnSharedPreferenceChangeListener(this);
+
+ SettingsCache settingsCache = SettingsCache.INSTANCE.get(context);
+ settingsCache.register(NOTIFICATION_BADGING_URI,
+ this::onNotificationDotsChanged);
+ onNotificationDotsChanged(settingsCache.getValue(NOTIFICATION_BADGING_URI));
+ tracker.addCloseable(this);
+ });
}
private static ArrayMap<String, LoggablePref> loadPrefKeys(Context context) {
@@ -209,6 +223,8 @@
public void close() {
getPrefs(mContext).unregisterOnSharedPreferenceChangeListener(this);
getDevicePrefs(mContext).unregisterOnSharedPreferenceChangeListener(this);
+ SettingsCache settingsCache = SettingsCache.INSTANCE.get(mContext);
+ settingsCache.unregister(NOTIFICATION_BADGING_URI, this::onNotificationDotsChanged);
}
@VisibleForTesting
diff --git a/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt b/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt
index 658975c..88ef0a8 100644
--- a/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt
+++ b/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt
@@ -453,6 +453,10 @@
}
}
+ /**
+ * @param inSplitSelection Whether user currently has a task from this task group staged for
+ * split screen. Currently this state is not reachable in fake landscape.
+ */
override fun measureGroupedTaskViewThumbnailBounds(
primarySnapshot: View,
secondarySnapshot: View,
@@ -460,7 +464,8 @@
parentHeight: Int,
splitBoundsConfig: SplitBounds,
dp: DeviceProfile,
- isRtl: Boolean
+ isRtl: Boolean,
+ inSplitSelection: Boolean
) {
val primaryParams = primarySnapshot.layoutParams as FrameLayout.LayoutParams
val secondaryParams = secondarySnapshot.layoutParams as FrameLayout.LayoutParams
@@ -569,6 +574,10 @@
iconAppChipView.setRotation(degreesRotated)
}
+ /**
+ * @param inSplitSelection Whether user currently has a task from this task group staged for
+ * split screen. Currently this state is not reachable in fake landscape.
+ */
override fun setSplitIconParams(
primaryIconView: View,
secondaryIconView: View,
@@ -579,7 +588,8 @@
groupedTaskViewWidth: Int,
isRtl: Boolean,
deviceProfile: DeviceProfile,
- splitConfig: SplitBounds
+ splitConfig: SplitBounds,
+ inSplitSelection: Boolean
) {
val spaceAboveSnapshot = deviceProfile.overviewTaskThumbnailTopMarginPx
val totalThumbnailHeight = groupedTaskViewHeight - spaceAboveSnapshot
diff --git a/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java b/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java
index cc022b2..c0b697d 100644
--- a/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java
+++ b/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java
@@ -530,10 +530,16 @@
}
}
+ /**
+ * @param inSplitSelection Whether user currently has a task from this task group staged for
+ * split screen. If true, we have custom translations/scaling in place
+ * for the remaining snapshot, so we'll skip setting translation/scale
+ * here.
+ */
@Override
public void measureGroupedTaskViewThumbnailBounds(View primarySnapshot, View secondarySnapshot,
int parentWidth, int parentHeight, SplitBounds splitBoundsConfig,
- DeviceProfile dp, boolean isRtl) {
+ DeviceProfile dp, boolean isRtl, boolean inSplitSelection) {
int spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx;
FrameLayout.LayoutParams primaryParams =
@@ -541,11 +547,10 @@
FrameLayout.LayoutParams secondaryParams =
(FrameLayout.LayoutParams) secondarySnapshot.getLayoutParams();
- // Reset margin and translations that aren't used in this method, but are used in other
+ // Reset margins that aren't used in this method, but are used in other
// `RecentsPagedOrientationHandler` variants.
secondaryParams.topMargin = 0;
primaryParams.topMargin = spaceAboveSnapshot;
- primarySnapshot.setTranslationY(0);
int totalThumbnailHeight = parentHeight - spaceAboveSnapshot;
float dividerScale = splitBoundsConfig.appsStackedVertically
@@ -553,28 +558,35 @@
: splitBoundsConfig.dividerWidthPercent;
Pair<Point, Point> taskViewSizes =
getGroupedTaskViewSizes(dp, splitBoundsConfig, parentWidth, parentHeight);
- if (dp.isLeftRightSplit) {
- int scaledDividerBar = Math.round(parentWidth * dividerScale);
- if (isRtl) {
- int translationX = taskViewSizes.second.x + scaledDividerBar;
- primarySnapshot.setTranslationX(-translationX);
- secondarySnapshot.setTranslationX(0);
+ if (!inSplitSelection) {
+ // Reset translations that aren't used in this method, but are used in other
+ // `RecentsPagedOrientationHandler` variants.
+ primarySnapshot.setTranslationY(0);
+
+ if (dp.isLeftRightSplit) {
+ int scaledDividerBar = Math.round(parentWidth * dividerScale);
+ if (isRtl) {
+ int translationX = taskViewSizes.second.x + scaledDividerBar;
+ primarySnapshot.setTranslationX(-translationX);
+ secondarySnapshot.setTranslationX(0);
+ } else {
+ int translationX = taskViewSizes.first.x + scaledDividerBar;
+ secondarySnapshot.setTranslationX(translationX);
+ primarySnapshot.setTranslationX(0);
+ }
+ secondarySnapshot.setTranslationY(spaceAboveSnapshot);
} else {
- int translationX = taskViewSizes.first.x + scaledDividerBar;
- secondarySnapshot.setTranslationX(translationX);
+ float finalDividerHeight = Math.round(totalThumbnailHeight * dividerScale);
+ float translationY =
+ taskViewSizes.first.y + spaceAboveSnapshot + finalDividerHeight;
+ secondarySnapshot.setTranslationY(translationY);
+
+ // Reset unused translations.
+ secondarySnapshot.setTranslationX(0);
primarySnapshot.setTranslationX(0);
}
-
- secondarySnapshot.setTranslationY(spaceAboveSnapshot);
- } else {
- float finalDividerHeight = Math.round(totalThumbnailHeight * dividerScale);
- float translationY = taskViewSizes.first.y + spaceAboveSnapshot + finalDividerHeight;
- secondarySnapshot.setTranslationY(translationY);
-
- // Reset unused translations.
- secondarySnapshot.setTranslationX(0);
- primarySnapshot.setTranslationX(0);
}
+
primarySnapshot.measure(
View.MeasureSpec.makeMeasureSpec(taskViewSizes.first.x, View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(taskViewSizes.first.y, View.MeasureSpec.EXACTLY));
@@ -582,10 +594,6 @@
View.MeasureSpec.makeMeasureSpec(taskViewSizes.second.x, View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(taskViewSizes.second.y,
View.MeasureSpec.EXACTLY));
- primarySnapshot.setScaleX(1);
- secondarySnapshot.setScaleX(1);
- primarySnapshot.setScaleY(1);
- secondarySnapshot.setScaleY(1);
}
@Override
@@ -662,11 +670,16 @@
iconAppChipView.setRotation(getDegreesRotated());
}
+ /**
+ * @param inSplitSelection Whether user currently has a task from this task group staged for
+ * split screen. If true, we have custom translations in place for the
+ * remaining icon, so we'll skip setting translations here.
+ */
@Override
public void setSplitIconParams(View primaryIconView, View secondaryIconView,
int taskIconHeight, int primarySnapshotWidth, int primarySnapshotHeight,
int groupedTaskViewHeight, int groupedTaskViewWidth, boolean isRtl,
- DeviceProfile deviceProfile, SplitBounds splitConfig) {
+ DeviceProfile deviceProfile, SplitBounds splitConfig, boolean inSplitSelection) {
FrameLayout.LayoutParams primaryIconParams =
(FrameLayout.LayoutParams) primaryIconView.getLayoutParams();
FrameLayout.LayoutParams secondaryIconParams = enableOverviewIconMenu()
@@ -680,20 +693,23 @@
secondaryIconParams.gravity = TOP | START;
secondaryIconParams.topMargin = primaryIconParams.topMargin;
secondaryIconParams.setMarginStart(primaryIconParams.getMarginStart());
- if (deviceProfile.isLeftRightSplit) {
- if (isRtl) {
- int secondarySnapshotWidth = groupedTaskViewWidth - primarySnapshotWidth;
- primaryAppChipView.setSplitTranslationX(-secondarySnapshotWidth);
+ if (!inSplitSelection) {
+ if (deviceProfile.isLeftRightSplit) {
+ if (isRtl) {
+ int secondarySnapshotWidth = groupedTaskViewWidth - primarySnapshotWidth;
+ primaryAppChipView.setSplitTranslationX(-secondarySnapshotWidth);
+ } else {
+ secondaryAppChipView.setSplitTranslationX(primarySnapshotWidth);
+ }
} else {
- secondaryAppChipView.setSplitTranslationX(primarySnapshotWidth);
+ primaryAppChipView.setSplitTranslationX(0);
+ secondaryAppChipView.setSplitTranslationX(0);
+ int dividerThickness = Math.min(splitConfig.visualDividerBounds.width(),
+ splitConfig.visualDividerBounds.height());
+ secondaryAppChipView.setSplitTranslationY(
+ primarySnapshotHeight + (deviceProfile.isTablet ? 0
+ : dividerThickness));
}
- } else {
- primaryAppChipView.setSplitTranslationX(0);
- secondaryAppChipView.setSplitTranslationX(0);
- int dividerThickness = Math.min(splitConfig.visualDividerBounds.width(),
- splitConfig.visualDividerBounds.height());
- secondaryAppChipView.setSplitTranslationY(
- primarySnapshotHeight + (deviceProfile.isTablet ? 0 : dividerThickness));
}
} else if (deviceProfile.isLeftRightSplit) {
// We calculate the "midpoint" of the thumbnail area, and place the icons there.
@@ -716,46 +732,53 @@
if (deviceProfile.isSeascape()) {
primaryIconParams.gravity = TOP | (isRtl ? END : START);
secondaryIconParams.gravity = TOP | (isRtl ? END : START);
- if (splitConfig.initiatedFromSeascape) {
- // if the split was initiated from seascape,
- // the task on the right (secondary) is slightly larger
- primaryIconView.setTranslationX(bottomToMidpointOffset - taskIconHeight);
- secondaryIconView.setTranslationX(bottomToMidpointOffset);
- } else {
- // if not,
- // the task on the left (primary) is slightly larger
- primaryIconView.setTranslationX(bottomToMidpointOffset + insetOffset
- - taskIconHeight);
- secondaryIconView.setTranslationX(bottomToMidpointOffset + insetOffset);
+ if (!inSplitSelection) {
+ if (splitConfig.initiatedFromSeascape) {
+ // if the split was initiated from seascape,
+ // the task on the right (secondary) is slightly larger
+ primaryIconView.setTranslationX(bottomToMidpointOffset - taskIconHeight);
+ secondaryIconView.setTranslationX(bottomToMidpointOffset);
+ } else {
+ // if not,
+ // the task on the left (primary) is slightly larger
+ primaryIconView.setTranslationX(bottomToMidpointOffset + insetOffset
+ - taskIconHeight);
+ secondaryIconView.setTranslationX(bottomToMidpointOffset + insetOffset);
+ }
}
} else {
primaryIconParams.gravity = TOP | (isRtl ? START : END);
secondaryIconParams.gravity = TOP | (isRtl ? START : END);
- if (!splitConfig.initiatedFromSeascape) {
- // if the split was initiated from landscape,
- // the task on the left (primary) is slightly larger
- primaryIconView.setTranslationX(-bottomToMidpointOffset);
- secondaryIconView.setTranslationX(-bottomToMidpointOffset + taskIconHeight);
- } else {
- // if not,
- // the task on the right (secondary) is slightly larger
- primaryIconView.setTranslationX(-bottomToMidpointOffset - insetOffset);
- secondaryIconView.setTranslationX(-bottomToMidpointOffset - insetOffset
- + taskIconHeight);
+ if (!inSplitSelection) {
+ if (!splitConfig.initiatedFromSeascape) {
+ // if the split was initiated from landscape,
+ // the task on the left (primary) is slightly larger
+ primaryIconView.setTranslationX(-bottomToMidpointOffset);
+ secondaryIconView.setTranslationX(-bottomToMidpointOffset + taskIconHeight);
+ } else {
+ // if not,
+ // the task on the right (secondary) is slightly larger
+ primaryIconView.setTranslationX(-bottomToMidpointOffset - insetOffset);
+ secondaryIconView.setTranslationX(-bottomToMidpointOffset - insetOffset
+ + taskIconHeight);
+ }
}
}
} else {
primaryIconParams.gravity = TOP | CENTER_HORIZONTAL;
- // shifts icon half a width left (height is used here since icons are square)
- primaryIconView.setTranslationX(-(taskIconHeight / 2f));
secondaryIconParams.gravity = TOP | CENTER_HORIZONTAL;
- secondaryIconView.setTranslationX(taskIconHeight / 2f);
+ if (!inSplitSelection) {
+ // shifts icon half a width left (height is used here since icons are square)
+ primaryIconView.setTranslationX(-(taskIconHeight / 2f));
+ secondaryIconView.setTranslationX(taskIconHeight / 2f);
+ }
}
- if (!enableOverviewIconMenu()) {
+ if (!enableOverviewIconMenu() && !inSplitSelection) {
primaryIconView.setTranslationY(0);
secondaryIconView.setTranslationY(0);
}
+
primaryIconView.setLayoutParams(primaryIconParams);
secondaryIconView.setLayoutParams(secondaryIconParams);
}
diff --git a/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.kt b/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.kt
index 06a0685..b8d0412 100644
--- a/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.kt
+++ b/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.kt
@@ -184,7 +184,8 @@
parentHeight: Int,
splitBoundsConfig: SplitConfigurationOptions.SplitBounds,
dp: DeviceProfile,
- isRtl: Boolean
+ isRtl: Boolean,
+ inSplitSelection: Boolean
)
/**
@@ -235,7 +236,8 @@
groupedTaskViewWidth: Int,
isRtl: Boolean,
deviceProfile: DeviceProfile,
- splitConfig: SplitConfigurationOptions.SplitBounds
+ splitConfig: SplitConfigurationOptions.SplitBounds,
+ inSplitSelection: Boolean
)
/*
diff --git a/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt b/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt
index a972e8c..bc91911 100644
--- a/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt
+++ b/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt
@@ -277,6 +277,10 @@
iconAppChipView.setRotation(degreesRotated)
}
+ /**
+ * @param inSplitSelection Whether user currently has a task from this task group staged for
+ * split screen. Currently this state is not reachable in fake seascape.
+ */
override fun measureGroupedTaskViewThumbnailBounds(
primarySnapshot: View,
secondarySnapshot: View,
@@ -284,7 +288,8 @@
parentHeight: Int,
splitBoundsConfig: SplitBounds,
dp: DeviceProfile,
- isRtl: Boolean
+ isRtl: Boolean,
+ inSplitSelection: Boolean
) {
val primaryParams = primarySnapshot.layoutParams as FrameLayout.LayoutParams
val secondaryParams = secondarySnapshot.layoutParams as FrameLayout.LayoutParams
diff --git a/quickstep/src/com/android/quickstep/recents/data/TaskVisualsChangedDelegate.kt b/quickstep/src/com/android/quickstep/recents/data/TaskVisualsChangedDelegate.kt
index a141e89..a45d194 100644
--- a/quickstep/src/com/android/quickstep/recents/data/TaskVisualsChangedDelegate.kt
+++ b/quickstep/src/com/android/quickstep/recents/data/TaskVisualsChangedDelegate.kt
@@ -23,6 +23,7 @@
import com.android.quickstep.util.TaskVisualsChangeListener
import com.android.systemui.shared.recents.model.Task
import com.android.systemui.shared.recents.model.ThumbnailData
+import java.util.concurrent.ConcurrentHashMap
/** Delegates the checking of task visuals (thumbnails, high res changes, icons) */
interface TaskVisualsChangedDelegate :
@@ -30,7 +31,7 @@
/** Registers a callback for visuals relating to icons */
fun registerTaskIconChangedCallback(
taskKey: Task.TaskKey,
- taskIconChangedCallback: TaskIconChangedCallback
+ taskIconChangedCallback: TaskIconChangedCallback,
)
/** Unregisters a callback for visuals relating to icons */
@@ -39,7 +40,7 @@
/** Registers a callback for visuals relating to thumbnails */
fun registerTaskThumbnailChangedCallback(
taskKey: Task.TaskKey,
- taskThumbnailChangedCallback: TaskThumbnailChangedCallback
+ taskThumbnailChangedCallback: TaskThumbnailChangedCallback,
)
/** Unregisters a callback for visuals relating to thumbnails */
@@ -66,31 +67,9 @@
private val highResLoadingStateNotifier: HighResLoadingStateNotifier,
) : TaskVisualsChangedDelegate {
private val taskIconChangedCallbacks =
- mutableMapOf<Int, Pair<Task.TaskKey, TaskIconChangedCallback>>()
+ ConcurrentHashMap<Int, Pair<Task.TaskKey, TaskIconChangedCallback>>()
private val taskThumbnailChangedCallbacks =
- mutableMapOf<Int, Pair<Task.TaskKey, TaskThumbnailChangedCallback>>()
- private var isListening = false
-
- @Synchronized
- private fun onCallbackRegistered() {
- if (isListening) return
-
- taskVisualsChangeNotifier.addThumbnailChangeListener(this)
- highResLoadingStateNotifier.addCallback(this)
- isListening = true
- }
-
- @Synchronized
- private fun onCallbackUnregistered() {
- if (!isListening) return
-
- if (taskIconChangedCallbacks.size + taskThumbnailChangedCallbacks.size == 0) {
- taskVisualsChangeNotifier.removeThumbnailChangeListener(this)
- highResLoadingStateNotifier.removeCallback(this)
- }
-
- isListening = false
- }
+ ConcurrentHashMap<Int, Pair<Task.TaskKey, TaskThumbnailChangedCallback>>()
override fun onTaskIconChanged(taskId: Int) {
taskIconChangedCallbacks[taskId]?.let { (_, callback) -> callback.onTaskIconChanged() }
@@ -119,27 +98,48 @@
override fun registerTaskIconChangedCallback(
taskKey: Task.TaskKey,
- taskIconChangedCallback: TaskIconChangedCallback
+ taskIconChangedCallback: TaskIconChangedCallback,
) {
- taskIconChangedCallbacks[taskKey.id] = taskKey to taskIconChangedCallback
- onCallbackRegistered()
+ updateCallbacks {
+ taskIconChangedCallbacks[taskKey.id] = taskKey to taskIconChangedCallback
+ }
}
override fun unregisterTaskIconChangedCallback(taskKey: Task.TaskKey) {
- taskIconChangedCallbacks.remove(taskKey.id)
- onCallbackUnregistered()
+ updateCallbacks { taskIconChangedCallbacks.remove(taskKey.id) }
}
override fun registerTaskThumbnailChangedCallback(
taskKey: Task.TaskKey,
- taskThumbnailChangedCallback: TaskThumbnailChangedCallback
+ taskThumbnailChangedCallback: TaskThumbnailChangedCallback,
) {
- taskThumbnailChangedCallbacks[taskKey.id] = taskKey to taskThumbnailChangedCallback
- onCallbackRegistered()
+ updateCallbacks {
+ taskThumbnailChangedCallbacks[taskKey.id] = taskKey to taskThumbnailChangedCallback
+ }
}
override fun unregisterTaskThumbnailChangedCallback(taskKey: Task.TaskKey) {
- taskThumbnailChangedCallbacks.remove(taskKey.id)
- onCallbackUnregistered()
+ updateCallbacks { taskThumbnailChangedCallbacks.remove(taskKey.id) }
+ }
+
+ @Synchronized
+ private fun updateCallbacks(callbackModifier: () -> Unit) {
+ val prevHasCallbacks =
+ taskIconChangedCallbacks.size + taskThumbnailChangedCallbacks.size > 0
+ callbackModifier()
+
+ val currHasCallbacks =
+ taskIconChangedCallbacks.size + taskThumbnailChangedCallbacks.size > 0
+
+ when {
+ prevHasCallbacks && !currHasCallbacks -> {
+ taskVisualsChangeNotifier.removeThumbnailChangeListener(this)
+ highResLoadingStateNotifier.removeCallback(this)
+ }
+ !prevHasCallbacks && currHasCallbacks -> {
+ taskVisualsChangeNotifier.addThumbnailChangeListener(this)
+ highResLoadingStateNotifier.addCallback(this)
+ }
+ }
}
}
diff --git a/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt b/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
index eb3c2d1..dc8d537 100644
--- a/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
+++ b/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
@@ -17,6 +17,7 @@
package com.android.quickstep.recents.data
import android.graphics.drawable.Drawable
+import android.util.Log
import com.android.launcher3.util.coroutines.DispatcherProvider
import com.android.quickstep.recents.data.TaskVisualsChangedDelegate.TaskIconChangedCallback
import com.android.quickstep.recents.data.TaskVisualsChangedDelegate.TaskThumbnailChangedCallback
@@ -106,7 +107,7 @@
}
}
.flowOn(dispatcherProvider.io)
- .shareIn(recentsCoroutineScope, SharingStarted.WhileSubscribed(), replay = 1)
+ .shareIn(recentsCoroutineScope, SharingStarted.WhileSubscribed(5000), replay = 1)
override fun getAllTaskData(forceRefresh: Boolean): Flow<List<Task>> {
if (forceRefresh) {
@@ -122,6 +123,7 @@
getTaskDataById(taskId).map { it?.thumbnail }.distinctUntilChangedBy { it?.snapshotId }
override fun setVisibleTasks(visibleTaskIdList: List<Int>) {
+ Log.d(TAG, "setVisibleTasks: $visibleTaskIdList")
this.visibleTaskIds.value = visibleTaskIdList.toSet()
}
@@ -185,7 +187,7 @@
TaskIconQueryResponse(
it.newDrawable().mutate(),
contentDescription,
- title
+ title,
)
)
}
@@ -193,12 +195,16 @@
continuation.invokeOnCancellation { cancellableTask?.cancel() }
}
}
+
+ companion object {
+ private const val TAG = "TasksRepository"
+ }
}
data class TaskIconQueryResponse(
val icon: Drawable,
val contentDescription: String,
- val title: String
+ val title: String,
)
private fun Task.getTaskIconQueryResponse(): TaskIconQueryResponse? {
diff --git a/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt b/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
index 0a5544f..b53650e 100644
--- a/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
+++ b/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
@@ -66,7 +66,7 @@
val taskVisualsChangedDelegate =
TaskVisualsChangedDelegateImpl(
recentsModel,
- recentsModel.thumbnailCache.highResLoadingState
+ recentsModel.thumbnailCache.highResLoadingState,
)
set(TaskVisualsChangedDelegate::class.java.simpleName, taskVisualsChangedDelegate)
@@ -79,7 +79,7 @@
iconCache,
taskVisualsChangedDelegate,
recentsCoroutineScope,
- ProductionDispatchers
+ ProductionDispatchers,
)
}
set(RecentTasksRepository::class.java.simpleName, recentTasksRepository)
@@ -155,7 +155,8 @@
scopeId: RecentsScopeId,
extras: RecentsDependenciesExtras,
): T {
- log("createDependency ${modelClass.simpleName} with $scopeId and $extras", Log.WARN)
+ log("createDependency ${modelClass.simpleName} with $scopeId and $extras started", Log.WARN)
+ log("linked scopes: ${getScope(scopeId).scopeIdsLinked}")
val instance: Any =
when (modelClass) {
RecentTasksRepository::class.java -> {
@@ -166,7 +167,7 @@
iconCache,
get(),
get(),
- ProductionDispatchers
+ ProductionDispatchers,
)
}
}
@@ -193,7 +194,7 @@
task = task,
recentsViewData = inject(),
recentTasksRepository = inject(),
- getThumbnailPositionUseCase = inject()
+ getThumbnailPositionUseCase = inject(),
)
}
GetThumbnailUseCase::class.java -> GetThumbnailUseCase(taskRepository = inject())
@@ -203,7 +204,7 @@
GetThumbnailPositionUseCase(
deviceProfileRepository = inject(),
rotationStateRepository = inject(),
- tasksRepository = inject()
+ tasksRepository = inject(),
)
SplashAlphaUseCase::class.java ->
SplashAlphaUseCase(
@@ -218,7 +219,12 @@
error("Factory for ${modelClass.simpleName} not defined!")
}
}
- return instance as T
+ return (instance as T).also {
+ log(
+ "createDependency ${modelClass.simpleName} with $scopeId and $extras completed",
+ Log.WARN,
+ )
+ }
}
private fun createScope(scopeId: RecentsScopeId): RecentsDependenciesScope {
@@ -247,11 +253,7 @@
fun initialize(view: View): RecentsDependencies = initialize(view.context)
fun initialize(context: Context): RecentsDependencies {
- synchronized(this) {
- if (!Companion::instance.isInitialized) {
- instance = RecentsDependencies(context.applicationContext)
- }
- }
+ synchronized(this) { instance = RecentsDependencies(context.applicationContext) }
return instance
}
diff --git a/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewModel.kt b/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewModel.kt
index 1716f2e..5cf6823 100644
--- a/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewModel.kt
+++ b/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewModel.kt
@@ -24,7 +24,7 @@
class RecentsViewModel(
private val recentsTasksRepository: RecentTasksRepository,
- private val recentsViewData: RecentsViewData
+ private val recentsViewData: RecentsViewData,
) {
fun refreshAllTaskData() {
recentsTasksRepository.getAllTaskData(true)
@@ -58,7 +58,8 @@
recentsViewData.thumbnailSplashProgress.value = taskThumbnailSplashAlpha
}
- suspend fun waitForThumbnailsToUpdate(updatedThumbnails: Map<Int, ThumbnailData>) {
+ suspend fun waitForThumbnailsToUpdate(updatedThumbnails: Map<Int, ThumbnailData>?) {
+ if (updatedThumbnails.isNullOrEmpty()) return
combine(
updatedThumbnails.map {
recentsTasksRepository.getThumbnailById(it.key).filter { thumbnailData ->
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
index 0279818..e7416ec 100644
--- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
@@ -22,6 +22,7 @@
import android.graphics.Outline
import android.graphics.Rect
import android.util.AttributeSet
+import android.util.Log
import android.view.View
import android.view.ViewOutlineProvider
import androidx.annotation.ColorInt
@@ -92,6 +93,7 @@
CoroutineScope(SupervisorJob() + Dispatchers.Main + CoroutineName("TaskThumbnailView"))
viewModel.uiState
.onEach { viewModelUiState ->
+ Log.d(TAG, "viewModelUiState changed from $uiState to: $viewModelUiState")
uiState = viewModelUiState
resetViews()
when (viewModelUiState) {
@@ -211,6 +213,10 @@
Utilities.mapRange(
viewModel.cornerRadiusProgress.value,
overviewCornerRadius,
- fullscreenCornerRadius
+ fullscreenCornerRadius,
) / inheritedScale
+
+ private companion object {
+ const val TAG = "TaskThumbnailView"
+ }
}
diff --git a/quickstep/src/com/android/quickstep/task/viewmodel/TaskThumbnailViewModel.kt b/quickstep/src/com/android/quickstep/task/viewmodel/TaskThumbnailViewModel.kt
index b1bb65e..4970685 100644
--- a/quickstep/src/com/android/quickstep/task/viewmodel/TaskThumbnailViewModel.kt
+++ b/quickstep/src/com/android/quickstep/task/viewmodel/TaskThumbnailViewModel.kt
@@ -19,6 +19,7 @@
import android.annotation.ColorInt
import android.app.ActivityTaskManager.INVALID_TASK_ID
import android.graphics.Matrix
+import android.util.Log
import androidx.core.graphics.ColorUtils
import com.android.quickstep.recents.data.RecentTasksRepository
import com.android.quickstep.recents.usecase.GetThumbnailPositionUseCase
@@ -82,7 +83,7 @@
combine(
task.flatMapLatest { it }.map { it?.key?.id }.distinctUntilChanged(),
recentsViewData.runningTaskIds,
- recentsViewData.runningTaskShowScreenshot
+ recentsViewData.runningTaskShowScreenshot,
) { taskId, runningTaskIds, runningTaskShowScreenshot ->
runningTaskIds.contains(taskId) && !runningTaskShowScreenshot
}
@@ -90,6 +91,13 @@
val uiState: Flow<TaskThumbnailUiState> =
combine(task.flatMapLatest { it }, isLiveTile) { taskVal, isRunning ->
+ // TODO(b/369339561) This log is firing a lot. Reduce emissions from TasksRepository
+ // then re-enable this log.
+ // Log.d(
+ // TAG,
+ // "Received task and / or live tile update. taskVal: $taskVal"
+ // + " isRunning: $isRunning.",
+ // )
when {
taskVal == null -> Uninitialized
isRunning -> LiveTile
@@ -103,6 +111,7 @@
.distinctUntilChanged()
fun bind(taskId: Int) {
+ Log.d(TAG, "bind taskId: $taskId")
this.taskId = taskId
task.value = tasksRepository.getTaskDataById(taskId)
splashProgress.value = splashAlphaUseCase.execute(taskId)
@@ -139,5 +148,6 @@
private companion object {
const val MAX_SCRIM_ALPHA = 0.4f
+ const val TAG = "TaskThumbnailViewModel"
}
}
diff --git a/quickstep/src/com/android/quickstep/util/BackAnimState.kt b/quickstep/src/com/android/quickstep/util/BackAnimState.kt
new file mode 100644
index 0000000..9009eaa
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/BackAnimState.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.util
+
+import android.animation.AnimatorSet
+import android.content.Context
+import com.android.launcher3.LauncherAnimationRunner.AnimationResult
+import com.android.launcher3.anim.AnimatorListeners.forEndCallback
+import com.android.launcher3.util.RunnableList
+
+/** Interface to represent animation for back to Launcher transition */
+interface BackAnimState {
+
+ fun addOnAnimCompleteCallback(r: Runnable)
+
+ fun applyToAnimationResult(result: AnimationResult, c: Context)
+
+ fun start()
+}
+
+class AnimatorBackState(private val springAnim: RectFSpringAnim?, private val anim: AnimatorSet?) :
+ BackAnimState {
+
+ override fun addOnAnimCompleteCallback(r: Runnable) {
+ val springAnimWait = RunnableList()
+ springAnim?.addAnimatorListener(forEndCallback(springAnimWait::executeAllAndDestroy))
+ ?: springAnimWait.executeAllAndDestroy()
+
+ val animWait = RunnableList()
+ anim?.addListener(
+ forEndCallback(Runnable { springAnimWait.add(animWait::executeAllAndDestroy) })
+ ) ?: springAnimWait.add(animWait::executeAllAndDestroy)
+ animWait.add(r)
+ }
+
+ override fun applyToAnimationResult(result: AnimationResult, c: Context) {
+ result.setAnimation(anim, c)
+ }
+
+ override fun start() {
+ anim?.start()
+ }
+}
+
+class AlreadyStartedBackAnimState(private val onEndCallback: RunnableList) : BackAnimState {
+
+ override fun addOnAnimCompleteCallback(r: Runnable) {
+ onEndCallback.add(r)
+ }
+
+ override fun applyToAnimationResult(result: AnimationResult, c: Context) {
+ addOnAnimCompleteCallback(result::onAnimationFinished)
+ }
+
+ override fun start() {}
+}
diff --git a/quickstep/src/com/android/quickstep/util/ImageActionUtils.java b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
index 3a1c99b..64e46d8 100644
--- a/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
+++ b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
@@ -85,6 +85,7 @@
.setBitmap(screenshot)
.setBoundsOnScreen(screenshotBounds)
.setInsets(visibleInsets)
+ .setDisplayId(task.displayId)
.build();
systemUiProxy.takeScreenshot(request);
}
diff --git a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
index 26668c8..4c26761 100644
--- a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
+++ b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
@@ -31,7 +31,6 @@
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.Hotseat;
import com.android.launcher3.Workspace;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.HorizontalInsettableView;
import com.android.quickstep.SystemUiProxy;
@@ -80,17 +79,12 @@
@UnfoldMain RotationChangeProvider rotationChangeProvider) {
mLauncher = launcher;
- if (FeatureFlags.PREEMPTIVE_UNFOLD_ANIMATION_START.get()) {
- mPreemptiveProgressProvider = new PreemptiveUnfoldTransitionProgressProvider(
- unfoldTransitionProgressProvider, launcher.getMainThreadHandler());
- mPreemptiveProgressProvider.init();
+ mPreemptiveProgressProvider = new PreemptiveUnfoldTransitionProgressProvider(
+ unfoldTransitionProgressProvider, launcher.getMainThreadHandler());
+ mPreemptiveProgressProvider.init();
- mProgressProvider = new ScopedUnfoldTransitionProgressProvider(
- mPreemptiveProgressProvider);
- } else {
- mProgressProvider = new ScopedUnfoldTransitionProgressProvider(
- unfoldTransitionProgressProvider);
- }
+ mProgressProvider = new ScopedUnfoldTransitionProgressProvider(
+ mPreemptiveProgressProvider);
unfoldTransitionProgressProvider.addCallback(mExternalTransitionStatusProvider);
unfoldTransitionProgressProvider.addCallback(
@@ -169,10 +163,6 @@
@Override
public void onDeviceProfileChanged(DeviceProfile dp) {
- if (!FeatureFlags.PREEMPTIVE_UNFOLD_ANIMATION_START.get()) {
- return;
- }
-
if (mIsTablet != null && dp.isTablet != mIsTablet) {
// We should preemptively start the animation only if:
// - We changed to the unfolded screen
diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
index b9338a3..a8460c9 100644
--- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java
+++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
@@ -23,7 +23,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.NavigationMode;
-import com.android.quickstep.LauncherActivityInterface;
+import com.android.quickstep.BaseContainerInterface;
import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
public class LayoutUtils {
@@ -41,11 +41,14 @@
return swipeHeight;
}
- public static int getShelfTrackingDistance(Context context, DeviceProfile dp,
- RecentsPagedOrientationHandler orientationHandler) {
+ public static int getShelfTrackingDistance(
+ Context context,
+ DeviceProfile dp,
+ RecentsPagedOrientationHandler orientationHandler,
+ BaseContainerInterface<?, ?> baseContainerInterface) {
// Track the bottom of the window.
Rect taskSize = new Rect();
- LauncherActivityInterface.INSTANCE.calculateTaskSize(context, dp, taskSize,
+ baseContainerInterface.calculateTaskSize(context, dp, taskSize,
orientationHandler);
return orientationHandler.getDistanceToBottomOfRect(dp, taskSize);
}
diff --git a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
index 15081da..4a9e0d8 100644
--- a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
+++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
@@ -252,7 +252,7 @@
if (Utilities.isRunningInTestHarness()) {
Log.d(TAG, logString.toString());
}
- ActiveGestureLog.INSTANCE.addLog(logString);
+ ActiveGestureProtoLogProxy.logMotionPauseDetectorEvent(logString.toString());
}
public void clear() {
diff --git a/quickstep/src/com/android/quickstep/util/RecentsViewUtils.kt b/quickstep/src/com/android/quickstep/util/RecentsViewUtils.kt
index be1af64..e70372f 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsViewUtils.kt
+++ b/quickstep/src/com/android/quickstep/util/RecentsViewUtils.kt
@@ -66,6 +66,9 @@
fun getLargeTaskViewIds(taskViews: Iterable<TaskView>): List<Int> =
taskViews.filter { it.isLargeTile }.map { it.taskViewId }
+ /** Counts [TaskView]s that are large tiles. */
+ fun getLargeTileCount(taskViews: Iterable<TaskView>): Int = taskViews.count { it.isLargeTile }
+
/**
* Returns the first TaskView that should be displayed as a large tile.
*
diff --git a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
index 256e29e..3449cf2 100644
--- a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
+++ b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
@@ -42,6 +42,8 @@
import android.window.TransitionInfo.Change
import android.window.WindowContainerToken
import androidx.annotation.VisibleForTesting
+import androidx.core.util.component1
+import androidx.core.util.component2
import com.android.app.animation.Interpolators
import com.android.launcher3.DeviceProfile
import com.android.launcher3.Flags.enableOverviewIconMenu
@@ -225,10 +227,22 @@
ObjectAnimator.ofFloat(iconView.splitTranslationY, MULTI_PROPERTY_VALUE, 0f)
)
}
+
+ val splitBoundsConfig =
+ (taskContainer.taskView as? GroupedTaskView)?.splitBoundsConfig ?: return
+ val (primarySnapshotViewSize, secondarySnapshotViewSize) =
+ taskContainer.taskView.pagedOrientationHandler.getGroupedTaskViewSizes(
+ deviceProfile,
+ splitBoundsConfig,
+ taskViewWidth,
+ taskViewHeight,
+ )
+ val snapshotViewSize =
+ if (isPrimaryTaskSplitting) primarySnapshotViewSize else secondarySnapshotViewSize
if (deviceProfile.isLeftRightSplit) {
// Center view first so scaling happens uniformly, alternatively we can move pivotX to 0
- val centerThumbnailTranslationX: Float = (taskViewWidth - snapshot.width) / 2f
- val finalScaleX: Float = taskViewWidth.toFloat() / snapshot.width
+ val centerThumbnailTranslationX: Float = (taskViewWidth - snapshotViewSize.x) / 2f
+ val finalScaleX: Float = taskViewWidth.toFloat() / snapshotViewSize.x
builder.add(
ObjectAnimator.ofFloat(snapshot, View.TRANSLATION_X, centerThumbnailTranslationX)
)
@@ -262,13 +276,13 @@
// thumbnail needs to take that into account. We should migrate to only using
// translations otherwise this asymmetry causes problems..
if (isPrimaryTaskSplitting) {
- centerThumbnailTranslationY = (thumbnailSize - snapshot.height) / 2f
+ centerThumbnailTranslationY = (thumbnailSize - snapshotViewSize.y) / 2f
centerThumbnailTranslationY +=
deviceProfile.overviewTaskThumbnailTopMarginPx.toFloat()
} else {
- centerThumbnailTranslationY = (thumbnailSize - snapshot.height) / 2f
+ centerThumbnailTranslationY = (thumbnailSize - snapshotViewSize.y) / 2f
}
- val finalScaleY: Float = thumbnailSize.toFloat() / snapshot.height
+ val finalScaleY: Float = thumbnailSize.toFloat() / snapshotViewSize.y
builder.add(
ObjectAnimator.ofFloat(snapshot, View.TRANSLATION_Y, centerThumbnailTranslationY)
)
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index 1af12f1..fbeeef2 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -52,7 +52,6 @@
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
-import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -116,7 +115,6 @@
private static final String TAG = "SplitSelectStateCtor";
private RecentsViewContainer mContainer;
- private final Handler mHandler;
private final RecentsModel mRecentTasksModel;
@Nullable
private Runnable mActivityBackCallback;
@@ -182,12 +180,11 @@
}
};
- public SplitSelectStateController(RecentsViewContainer container, Handler handler,
+ public SplitSelectStateController(RecentsViewContainer container,
StateManager stateManager, DepthController depthController,
StatsLogManager statsLogManager, SystemUiProxy systemUiProxy, RecentsModel recentsModel,
Runnable activityBackCallback) {
mContainer = container;
- mHandler = handler;
mStatsLogManager = statsLogManager;
mSystemUiProxy = systemUiProxy;
mStateManager = stateManager;
diff --git a/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java b/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java
index c3270dc..f3b984b8 100644
--- a/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java
+++ b/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java
@@ -15,6 +15,7 @@
*/
package com.android.quickstep.util;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.view.Display.DEFAULT_DISPLAY;
import android.content.Context;
@@ -30,6 +31,8 @@
import com.android.launcher3.util.WindowBounds;
import com.android.launcher3.util.window.CachedDisplayInfo;
import com.android.launcher3.util.window.WindowManagerProxy;
+import com.android.quickstep.SystemUiProxy;
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import java.util.List;
import java.util.Set;
@@ -66,6 +69,24 @@
}
@Override
+ public boolean showLockedTaskbarOnHome(Context displayInfoContext) {
+ if (!DesktopModeStatus.canEnterDesktopMode(displayInfoContext)) {
+ return false;
+ }
+ if (!DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(displayInfoContext)) {
+ return false;
+ }
+ final boolean isFreeformDisplay = displayInfoContext.getResources().getConfiguration()
+ .windowConfiguration.getWindowingMode() == WINDOWING_MODE_FREEFORM;
+ return isFreeformDisplay;
+ }
+
+ @Override
+ public boolean isHomeVisible(Context context) {
+ return SystemUiProxy.INSTANCE.get(context).getHomeVisibilityState().isHomeVisible();
+ }
+
+ @Override
public int getRotation(Context displayInfoContext) {
return displayInfoContext.getResources().getConfiguration().windowConfiguration
.getRotation();
diff --git a/quickstep/src/com/android/quickstep/util/TaskRemovedDuringLaunchListener.java b/quickstep/src/com/android/quickstep/util/TaskRemovedDuringLaunchListener.java
index e80d2a6..40a328c 100644
--- a/quickstep/src/com/android/quickstep/util/TaskRemovedDuringLaunchListener.java
+++ b/quickstep/src/com/android/quickstep/util/TaskRemovedDuringLaunchListener.java
@@ -98,10 +98,7 @@
final Runnable taskLaunchFailedCallback = mTaskLaunchFailedCallback;
RecentsModel.INSTANCE.get(mContext).isTaskRemoved(mLaunchedTaskId, (taskRemoved) -> {
if (taskRemoved) {
- ActiveGestureLog.INSTANCE.addLog(
- new ActiveGestureLog.CompoundString("Launch failed, task (id=")
- .append(launchedTaskId)
- .append(") finished mid transition"));
+ ActiveGestureProtoLogProxy.logTaskLaunchFailed(launchedTaskId);
taskLaunchFailedCallback.run();
}
}, (task) -> true /* filter */);
diff --git a/quickstep/src/com/android/quickstep/util/unfold/LauncherUnfoldTransitionController.kt b/quickstep/src/com/android/quickstep/util/unfold/LauncherUnfoldTransitionController.kt
index 09563f5..915c9e5 100644
--- a/quickstep/src/com/android/quickstep/util/unfold/LauncherUnfoldTransitionController.kt
+++ b/quickstep/src/com/android/quickstep/util/unfold/LauncherUnfoldTransitionController.kt
@@ -22,7 +22,6 @@
import com.android.launcher3.DeviceProfile
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener
import com.android.launcher3.anim.PendingAnimation
-import com.android.launcher3.config.FeatureFlags
import com.android.launcher3.uioverrides.QuickstepLauncher
import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
@@ -30,7 +29,7 @@
/** Controls animations that are happening during unfolding foldable devices */
class LauncherUnfoldTransitionController(
private val launcher: QuickstepLauncher,
- private val progressProvider: ProxyUnfoldTransitionProvider
+ private val progressProvider: ProxyUnfoldTransitionProvider,
) : OnDeviceProfileChangeListener, ActivityLifecycleCallbacksAdapter, TransitionProgressListener {
private var isTablet: Boolean? = null
@@ -57,10 +56,6 @@
}
override fun onDeviceProfileChanged(dp: DeviceProfile) {
- if (!FeatureFlags.PREEMPTIVE_UNFOLD_ANIMATION_START.get()) {
- return
- }
-
if (isTablet != null && dp.isTablet != isTablet) {
// We should preemptively start the animation only if:
// - We changed to the unfolded screen
@@ -93,7 +88,7 @@
provider = this,
factory = this::onPrepareUnfoldAnimation,
duration =
- 1000L // The expected duration for the animation. Then only comes to play if we have
+ 1000L, // The expected duration for the animation. Then only comes to play if we have
// to run the animation ourselves in case sysui misses the end signal
)
timeoutAlarm.cancelAlarm()
@@ -119,7 +114,7 @@
launcher,
isVertical,
dp.displayInfo.currentSize,
- anim
+ anim,
)
}
diff --git a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.kt b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.kt
index 7b97c23..c07b7fb 100644
--- a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.kt
+++ b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.kt
@@ -59,10 +59,10 @@
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
- defStyleRes: Int = 0
+ defStyleRes: Int = 0,
) : TextView(context, attrs, defStyleAttr, defStyleRes) {
- private val recentsViewContainer =
- RecentsViewContainer.containerFromContext<RecentsViewContainer>(context)
+ private val recentsViewContainer: RecentsViewContainer =
+ RecentsViewContainer.containerFromContext(context)
private val launcherApps: LauncherApps? = context.getSystemService(LauncherApps::class.java)
@@ -138,7 +138,7 @@
usageLimit =
launcherApps?.getAppUsageLimit(
task.topComponent.packageName,
- UserHandle.of(task.key.userId)
+ UserHandle.of(task.key.userId),
)
} catch (e: Exception) {
Log.e(TAG, "Error initializing digital well being toast", e)
@@ -162,7 +162,7 @@
task: Task,
taskView: TaskView,
snapshotView: View,
- @StagePosition stagePosition: Int
+ @StagePosition stagePosition: Int,
) {
this.task = task
this.taskView = taskView
@@ -201,7 +201,7 @@
private fun getReadableDuration(
duration: Duration,
- @StringRes durationLessThanOneMinuteStringId: Int
+ @StringRes durationLessThanOneMinuteStringId: Int,
): String {
val hours = Math.toIntExact(duration.toHours())
val minutes = Math.toIntExact(duration.minusHours(hours.toLong()).toMinutes())
@@ -211,7 +211,7 @@
MeasureFormat.getInstance(Locale.getDefault(), MeasureFormat.FormatWidth.NARROW)
.formatMeasures(
Measure(hours, MeasureUnit.HOUR),
- Measure(minutes, MeasureUnit.MINUTE)
+ Measure(minutes, MeasureUnit.MINUTE),
)
// Apply FormatWidth.WIDE if only the hour part is non-zero (unless forced).
hours > 0 ->
@@ -239,7 +239,7 @@
@VisibleForTesting
fun getBannerText(
remainingTime: Long = appRemainingTimeMs,
- forContentDesc: Boolean = false
+ forContentDesc: Boolean = false,
): String {
val duration =
Duration.ofMillis(
@@ -250,7 +250,7 @@
val readableDuration =
getReadableDuration(
duration,
- R.string.shorter_duration_less_than_one_minute /* forceFormatWidth */
+ R.string.shorter_duration_less_than_one_minute, /* forceFormatWidth */
)
val splitBannerConfig = getSplitBannerConfig()
return when {
@@ -277,7 +277,7 @@
Log.e(
TAG,
"Failed to open app usage settings for task " + task.topComponent.packageName,
- e
+ e,
)
}
}
@@ -285,13 +285,13 @@
private fun getContentDescriptionForTask(
task: Task,
appUsageLimitTimeMs: Long,
- appRemainingTimeMs: Long
+ appRemainingTimeMs: Long,
): String? =
if (appUsageLimitTimeMs >= 0 && appRemainingTimeMs >= 0)
context.getString(
R.string.task_contents_description_with_remaining_time,
task.titleDescription,
- getBannerText(appRemainingTimeMs, true /* forContentDesc */)
+ getBannerText(appRemainingTimeMs, true /* forContentDesc */),
)
else task.titleDescription
@@ -310,7 +310,7 @@
recentsViewContainer.deviceProfile,
splitBounds,
taskView.layoutParams.width,
- taskView.layoutParams.height
+ taskView.layoutParams.height,
)
if (stagePosition == STAGE_POSITION_TOP_OR_LEFT) {
snapshotWidth = groupedTaskSize.first.x
@@ -327,7 +327,7 @@
recentsViewContainer.deviceProfile,
snapshotWidth,
snapshotHeight,
- this
+ this,
)
}
@@ -340,7 +340,7 @@
recentsViewContainer.deviceProfile,
taskView.snapshotViews,
task.key.id,
- this
+ this,
)
this.translationX = translationX
this.splitOffsetTranslationY = translationY
@@ -372,7 +372,7 @@
if (taskView.containsMultipleTasks())
context.getString(
R.string.split_app_usage_settings,
- TaskUtils.getTitle(context, task)
+ TaskUtils.getTitle(context, task),
)
else context.getString(R.string.accessibility_app_usage_settings)
return AccessibilityNodeInfo.AccessibilityAction(getAccessibilityActionId(), label)
@@ -394,7 +394,7 @@
/** Used for grid task view, only showing icon and time */
SPLIT_GRID_BANNER_LARGE,
/** Used for grid task view, only showing icon */
- SPLIT_GRID_BANNER_SMALL
+ SPLIT_GRID_BANNER_SMALL,
}
val OPEN_APP_USAGE_SETTINGS_TEMPLATE: Intent = Intent(Settings.ACTION_APP_USAGE_SETTINGS)
diff --git a/quickstep/src/com/android/quickstep/views/FloatingWidgetBackgroundView.java b/quickstep/src/com/android/quickstep/views/FloatingWidgetBackgroundView.java
index 4ea7753..f17be05 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingWidgetBackgroundView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingWidgetBackgroundView.java
@@ -32,7 +32,6 @@
import com.android.launcher3.R;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
-import com.android.launcher3.widget.RoundedCornerEnforcement;
import java.util.stream.IntStream;
@@ -171,8 +170,7 @@
/** Corner radius from source view's outline, or enforced view. */
private static float getOutlineRadius(LauncherAppWidgetHostView hostView, View v) {
- if (RoundedCornerEnforcement.isRoundedCornerEnabled()
- && hostView.hasEnforcedCornerRadius()) {
+ if (hostView.hasEnforcedCornerRadius()) {
return hostView.getEnforcedCornerRadius();
} else if (v.getOutlineProvider() instanceof RemoteViewOutlineProvider
&& v.getClipToOutline()) {
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt b/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
index 3fd1a6b..92c1e93 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
@@ -65,31 +65,18 @@
val heightSize = MeasureSpec.getSize(heightMeasureSpec)
setMeasuredDimension(widthSize, heightSize)
val splitBoundsConfig = splitBoundsConfig ?: return
- val initSplitTaskId = getThisTaskCurrentlyInSplitSelection()
- if (initSplitTaskId == INVALID_TASK_ID) {
- pagedOrientationHandler.measureGroupedTaskViewThumbnailBounds(
- taskContainers[0].snapshotView,
- taskContainers[1].snapshotView,
- widthSize,
- heightSize,
- splitBoundsConfig,
- container.deviceProfile,
- layoutDirection == LAYOUT_DIRECTION_RTL
- )
- } else {
- // Currently being split with this taskView, let the non-split selected thumbnail
- // take up full thumbnail area
- taskContainers
- .firstOrNull { it.task.key.id != initSplitTaskId }
- ?.snapshotView
- ?.measure(
- widthMeasureSpec,
- MeasureSpec.makeMeasureSpec(
- heightSize - container.deviceProfile.overviewTaskThumbnailTopMarginPx,
- MeasureSpec.EXACTLY
- )
- )
- }
+ val inSplitSelection = getThisTaskCurrentlyInSplitSelection() != INVALID_TASK_ID
+ pagedOrientationHandler.measureGroupedTaskViewThumbnailBounds(
+ taskContainers[0].snapshotView,
+ taskContainers[1].snapshotView,
+ widthSize,
+ heightSize,
+ splitBoundsConfig,
+ container.deviceProfile,
+ layoutDirection == LAYOUT_DIRECTION_RTL,
+ inSplitSelection,
+ )
+
if (!enableOverviewIconMenu()) {
updateIconPlacement()
}
@@ -173,6 +160,8 @@
val splitBoundsConfig = splitBoundsConfig ?: return
val taskIconHeight = container.deviceProfile.overviewTaskIconSizePx
val isRtl = layoutDirection == LAYOUT_DIRECTION_RTL
+ val inSplitSelection = getThisTaskCurrentlyInSplitSelection() != INVALID_TASK_ID
+
if (enableOverviewIconMenu()) {
val groupedTaskViewSizes =
pagedOrientationHandler.getGroupedTaskViewSizes(
@@ -191,7 +180,8 @@
layoutParams.width,
isRtl,
container.deviceProfile,
- splitBoundsConfig
+ splitBoundsConfig,
+ inSplitSelection
)
} else {
pagedOrientationHandler.setSplitIconParams(
@@ -204,7 +194,8 @@
measuredWidth,
isRtl,
container.deviceProfile,
- splitBoundsConfig
+ splitBoundsConfig,
+ inSplitSelection
)
}
}
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index d20d0a5..8f20cd38 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -16,17 +16,14 @@
package com.android.quickstep.views;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.window.flags.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY;
-import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.CLEAR_ALL_BUTTON;
-import static com.android.launcher3.LauncherState.EDIT_MODE;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
-import static com.android.launcher3.LauncherState.SPRING_LOADED;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTION_EXIT_HOME;
-import static com.android.wm.shell.shared.desktopmode.DesktopModeFlags.WALLPAPER_ACTIVITY;
import android.annotation.TargetApi;
import android.content.Context;
@@ -173,8 +170,7 @@
@Override
public void onStateTransitionComplete(LauncherState finalState) {
- if (finalState == NORMAL || finalState == SPRING_LOADED || finalState == EDIT_MODE
- || finalState == ALL_APPS) {
+ if (!finalState.isRecentsViewVisible) {
// Clean-up logic that occurs when recents is no longer in use/visible.
reset();
}
@@ -268,7 +264,8 @@
super.onGestureAnimationStart(runningTasks, rotationTouchHelper);
DesktopVisibilityController desktopVisibilityController =
mContainer.getDesktopVisibilityController();
- if (!WALLPAPER_ACTIVITY.isEnabled(mContext) && desktopVisibilityController != null) {
+ if (!ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()
+ && desktopVisibilityController != null) {
// TODO: b/333533253 - Remove after flag rollout
desktopVisibilityController.setRecentsGestureStart();
}
@@ -291,7 +288,8 @@
}
}
super.onGestureAnimationEnd();
- if (!WALLPAPER_ACTIVITY.isEnabled(mContext) && desktopVisibilityController != null) {
+ if (!ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()
+ && desktopVisibilityController != null) {
// TODO: b/333533253 - Remove after flag rollout
desktopVisibilityController.setRecentsGestureEnd(endTarget);
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index bb46a2c..8b2c95d 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -205,8 +205,7 @@
import com.android.quickstep.recents.di.RecentsDependencies;
import com.android.quickstep.recents.viewmodel.RecentsViewData;
import com.android.quickstep.recents.viewmodel.RecentsViewModel;
-import com.android.quickstep.util.ActiveGestureErrorDetector;
-import com.android.quickstep.util.ActiveGestureLog;
+import com.android.quickstep.util.ActiveGestureProtoLogProxy;
import com.android.quickstep.util.AnimUtils;
import com.android.quickstep.util.DesktopTask;
import com.android.quickstep.util.GroupTask;
@@ -236,8 +235,6 @@
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource;
-import kotlin.Unit;
-
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -251,6 +248,8 @@
import java.util.function.Consumer;
import java.util.stream.Collectors;
+import kotlin.Unit;
+
/**
* A list of recent tasks.
*
@@ -258,7 +257,7 @@
* @param <STATE_TYPE> : the type of base state that will be used
*/
public abstract class RecentsView<
- CONTAINER_TYPE extends Context & RecentsViewContainer,
+ CONTAINER_TYPE extends Context & RecentsViewContainer & StatefulContainer<STATE_TYPE>,
STATE_TYPE extends BaseState<STATE_TYPE>> extends PagedView implements Insettable,
TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback,
TaskVisualsChangeListener {
@@ -343,7 +342,7 @@
};
public static final int SCROLL_VIBRATION_PRIMITIVE =
- Utilities.ATLEAST_S ? VibrationEffect.Composition.PRIMITIVE_LOW_TICK : -1;
+ VibrationEffect.Composition.PRIMITIVE_LOW_TICK;
public static final float SCROLL_VIBRATION_PRIMITIVE_SCALE = 0.6f;
public static final VibrationEffect SCROLL_VIBRATION_FALLBACK =
VibrationConstants.EFFECT_TEXTURE_TICK;
@@ -1209,6 +1208,7 @@
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
+
updateTaskStackListenerState();
mModel.getThumbnailCache().getHighResLoadingState().removeCallback(this);
mContainer.removeMultiWindowModeChangedListener(mMultiWindowModeChangedListener);
@@ -1586,8 +1586,7 @@
@Override
protected void onPageEndTransition() {
super.onPageEndTransition();
- ActiveGestureLog.INSTANCE.addLog(
- "onPageEndTransition: current page index updated", getNextPage());
+ ActiveGestureProtoLogProxy.logOnPageEndTransition(getNextPage());
if (isClearAllHidden() && !mContainer.getDeviceProfile().isTablet) {
mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING, false);
}
@@ -1791,8 +1790,7 @@
@Override
protected void onScrollerAnimationAborted() {
- ActiveGestureLog.INSTANCE.addLog("scroller animation aborted",
- ActiveGestureErrorDetector.GestureEvent.SCROLLER_ANIMATION_ABORTED);
+ ActiveGestureProtoLogProxy.logOnScrollerAnimationAborted();
}
@Override
@@ -3671,8 +3669,8 @@
float longGridRowWidthDiff = 0;
int topGridRowSize = mTopRowIdSet.size();
- int bottomGridRowSize = taskCount - mTopRowIdSet.size()
- - (enableGridOnlyOverview() ? 0 : 1);
+ int numLargeTiles = mUtils.getLargeTileCount(getTaskViews());
+ int bottomGridRowSize = taskCount - mTopRowIdSet.size() - numLargeTiles;
boolean topRowLonger = topGridRowSize > bottomGridRowSize;
boolean bottomRowLonger = bottomGridRowSize > topGridRowSize;
boolean dismissedTaskFromTop = mTopRowIdSet.contains(dismissedTaskViewId);
@@ -3971,12 +3969,12 @@
final boolean finalCloseGapBetweenClearAll = closeGapBetweenClearAll;
final boolean finalSnapToLastTask = snapToLastTask;
final boolean finalIsFocusedTaskDismissed = isFocusedTaskDismissed;
- mPendingAnimation.addEndListener(new Consumer<Boolean>() {
+ mPendingAnimation.addEndListener(new Consumer<>() {
@Override
public void accept(Boolean success) {
if (mEnableDrawingLiveTile && dismissedTaskView.isRunningTask() && success) {
finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
- () -> onEnd(success));
+ () -> onEnd(true));
} else {
onEnd(success);
}
@@ -4146,6 +4144,14 @@
// If snapping to last task, find the last task after dismissal.
pageToSnapTo = indexOfChild(
getLastGridTaskView(topRowIdArray, bottomRowIdArray));
+
+ if (pageToSnapTo == INVALID_PAGE) {
+ // Snap to latest large tile page after dismissing the
+ // last grid task. This will prevent snapping to page 0 when
+ // desktop task is visible as large tile.
+ pageToSnapTo = indexOfChild(
+ mUtils.getLastLargeTaskView(getTaskViews()));
+ }
} else if (taskViewIdToSnapTo != -1) {
// If snapping to another page due to indices rearranging, find
// the new index after dismissal & rearrange using the task view id.
@@ -4994,7 +5000,8 @@
mSplitSelectStateController.getSplitAnimationController()
.addInitialSplitFromPair(taskContainer, builder,
mContainer.getDeviceProfile(),
- mSplitHiddenTaskView.getWidth(), mSplitHiddenTaskView.getHeight(),
+ mSplitHiddenTaskView.getLayoutParams().width,
+ mSplitHiddenTaskView.getLayoutParams().height,
primaryTaskSelected);
builder.addOnFrameCallback(() -> {
if (!enableRefactorTaskThumbnail()) {
diff --git a/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java b/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
index 8f19444..d8036aa 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
@@ -17,6 +17,7 @@
package com.android.quickstep.views;
import android.app.Activity;
+import android.content.ComponentName;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.LocusId;
@@ -31,7 +32,6 @@
import com.android.launcher3.BaseActivity;
import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.statehandlers.DesktopVisibilityController;
-import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.ScrimView;
@@ -44,7 +44,7 @@
* Returns an instance of an implementation of RecentsViewContainer
* @param context will find instance of recentsViewContainer from given context.
*/
- static <T extends RecentsViewContainer> T containerFromContext(Context context) {
+ static <T extends Context & RecentsViewContainer> T containerFromContext(Context context) {
if (context instanceof RecentsViewContainer) {
return (T) context;
} else if (context instanceof ContextWrapper) {
@@ -55,11 +55,6 @@
}
/**
- * Returns {@link SystemUiController} to manage various window flags to control system UI.
- */
- SystemUiController getSystemUiController();
-
- /**
* Returns {@link ScrimView}
*/
ScrimView getScrimView();
@@ -95,7 +90,7 @@
/**
* Returns overview actions view as a view
*/
- View getActionsView();
+ OverviewActionsView getActionsView();
/**
* @see BaseActivity#addForceInvisibleFlag(int)
@@ -143,12 +138,6 @@
void runOnBindToTouchInteractionService(Runnable r);
/**
- * @see Activity#getWindow()
- * @return Window
- */
- Window getWindow();
-
- /**
* @see
* BaseActivity#addMultiWindowModeChangedListener(BaseActivity.MultiWindowModeChangedListener)
* @param listener {@link BaseActivity.MultiWindowModeChangedListener}
@@ -177,6 +166,25 @@
boolean isRecentsViewVisible();
/**
+ * Begins transition to start home through container
+ */
+ default void startHome(){
+ // no op
+ }
+
+ /**
+ * Checks container to see if we can start home transition safely
+ */
+ boolean canStartHomeSafely();
+
+
+ /**
+ * Enter staged split directly from the current running app.
+ * @param leftOrTop if the staged split will be positioned left or top.
+ */
+ default void enterStageSplitFromRunningApp(boolean leftOrTop){}
+
+ /**
* Overwrites any logged item in Launcher that doesn't have a container with the
* {@link com.android.launcher3.touch.PagedOrientationHandler} in use for Overview.
*
diff --git a/quickstep/src/com/android/quickstep/views/RecentsViewModelHelper.kt b/quickstep/src/com/android/quickstep/views/RecentsViewModelHelper.kt
index 4604b70..f22c672 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsViewModelHelper.kt
+++ b/quickstep/src/com/android/quickstep/views/RecentsViewModelHelper.kt
@@ -49,9 +49,7 @@
recentsViewModel.setRunningTaskShowScreenshot(true)
viewAttachedScope.launch {
recentsViewModel.waitForRunningTaskShowScreenshotToUpdate()
- if (updatedThumbnails != null) {
- recentsViewModel.waitForThumbnailsToUpdate(updatedThumbnails)
- }
+ recentsViewModel.waitForThumbnailsToUpdate(updatedThumbnails)
ViewUtils.postFrameDrawn(taskView, onFinishRunnable)
}
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskContainer.kt b/quickstep/src/com/android/quickstep/views/TaskContainer.kt
index 13c4f78..6cb7741 100644
--- a/quickstep/src/com/android/quickstep/views/TaskContainer.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskContainer.kt
@@ -56,7 +56,7 @@
@SplitConfigurationOptions.StagePosition val stagePosition: Int,
val digitalWellBeingToast: DigitalWellBeingToast?,
val showWindowsView: View?,
- taskOverlayFactory: TaskOverlayFactory
+ taskOverlayFactory: TaskOverlayFactory,
) {
val overlay: TaskOverlayFactory.TaskOverlay<*> = taskOverlayFactory.createOverlay(this)
lateinit var taskContainerData: TaskContainerData
@@ -160,6 +160,8 @@
if (enableRefactorTaskThumbnail()) {
taskView.removeView(thumbnailView)
}
+ snapshotView.scaleX = 1f
+ snapshotView.scaleY = 1f
overlay.destroy()
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.kt b/quickstep/src/com/android/quickstep/views/TaskView.kt
index 4dcf2e7..f513a82 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskView.kt
@@ -46,7 +46,6 @@
import androidx.core.view.updateLayoutParams
import com.android.app.animation.Interpolators
import com.android.launcher3.Flags.enableCursorHoverStates
-import com.android.launcher3.Flags.enableFocusOutline
import com.android.launcher3.Flags.enableGridOnlyOverview
import com.android.launcher3.Flags.enableHoverOfChildElementsInTaskview
import com.android.launcher3.Flags.enableLargeDesktopWindowingTile
@@ -55,7 +54,6 @@
import com.android.launcher3.R
import com.android.launcher3.Utilities
import com.android.launcher3.anim.AnimatedFloat
-import com.android.launcher3.config.FeatureFlags.ENABLE_KEYBOARD_QUICK_SWITCH
import com.android.launcher3.logging.StatsLogManager.LauncherEvent
import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.testing.TestLogging
@@ -485,27 +483,23 @@
taskViewModel = RecentsDependencies.get(this, "TaskViewType" to type)
}
- val keyboardFocusHighlightEnabled =
- (ENABLE_KEYBOARD_QUICK_SWITCH.get() || enableFocusOutline())
val cursorHoverStatesEnabled = enableCursorHoverStates()
- setWillNotDraw(!keyboardFocusHighlightEnabled && !cursorHoverStatesEnabled)
+ setWillNotDraw(!cursorHoverStatesEnabled)
context.obtainStyledAttributes(attrs, R.styleable.TaskView, defStyleAttr, defStyleRes).use {
this.focusBorderAnimator =
focusBorderAnimator
- ?: if (keyboardFocusHighlightEnabled)
- createSimpleBorderAnimator(
- currentFullscreenParams.cornerRadius.toInt(),
- context.resources.getDimensionPixelSize(
- R.dimen.keyboard_quick_switch_border_width
- ),
- { bounds: Rect -> getThumbnailBounds(bounds) },
- this,
- it.getColor(
- R.styleable.TaskView_focusBorderColor,
- BorderAnimator.DEFAULT_BORDER_COLOR,
- ),
- )
- else null
+ ?: createSimpleBorderAnimator(
+ currentFullscreenParams.cornerRadius.toInt(),
+ context.resources.getDimensionPixelSize(
+ R.dimen.keyboard_quick_switch_border_width
+ ),
+ { bounds: Rect -> getThumbnailBounds(bounds) },
+ this,
+ it.getColor(
+ R.styleable.TaskView_focusBorderColor,
+ BorderAnimator.DEFAULT_BORDER_COLOR,
+ ),
+ )
this.hoverBorderAnimator =
hoverBorderAnimator
?: if (cursorHoverStatesEnabled)
@@ -611,6 +605,7 @@
override fun onRecycle() {
resetPersistentViewTransforms()
+ attachAlpha = 1f
// Clear any references to the thumbnail (it will be re-read either from the cache or the
// system on next bind)
if (!enableRefactorTaskThumbnail()) {
@@ -692,7 +687,6 @@
orientedState: RecentsOrientedState,
taskOverlayFactory: TaskOverlayFactory,
) {
-
cancelPendingLoadTasks()
taskContainers =
listOf(
@@ -725,6 +719,7 @@
thumbnailViewDeprecated.visibility = GONE
val indexOfSnapshotView = indexOfChild(thumbnailViewDeprecated)
LayoutInflater.from(context).inflate(R.layout.task_thumbnail, this, false).also {
+ it.id = thumbnailViewId
addView(it, indexOfSnapshotView, thumbnailViewDeprecated.layoutParams)
}
} else {
@@ -1608,7 +1603,6 @@
}
dismissScale = 1f
translationZ = 0f
- attachAlpha = 1f
setIconScaleAndDim(1f)
setColorTint(0f, 0)
}
diff --git a/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java b/quickstep/src_protolog/com/android/quickstep/util/ActiveGestureErrorDetector.java
similarity index 99%
rename from quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java
rename to quickstep/src_protolog/com/android/quickstep/util/ActiveGestureErrorDetector.java
index 2398e66..ab10979 100644
--- a/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java
+++ b/quickstep/src_protolog/com/android/quickstep/util/ActiveGestureErrorDetector.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
diff --git a/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java b/quickstep/src_protolog/com/android/quickstep/util/ActiveGestureLog.java
similarity index 97%
rename from quickstep/src/com/android/quickstep/util/ActiveGestureLog.java
rename to quickstep/src_protolog/com/android/quickstep/util/ActiveGestureLog.java
index d46b8fc..0eb6f88 100644
--- a/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java
+++ b/quickstep/src_protolog/com/android/quickstep/util/ActiveGestureLog.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -18,8 +18,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.launcher3.util.Preconditions;
-
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -338,8 +336,6 @@
}
private Object[] getArgs() {
- Preconditions.assertTrue(!mIsNoOp);
-
return mArgs.toArray();
}
@@ -349,8 +345,6 @@
}
private String toUnformattedString() {
- Preconditions.assertTrue(!mIsNoOp);
-
StringBuilder sb = new StringBuilder();
for (String substring : mSubstrings) {
sb.append(substring);
diff --git a/quickstep/src_protolog/com/android/quickstep/util/ActiveGestureProtoLogProxy.java b/quickstep/src_protolog/com/android/quickstep/util/ActiveGestureProtoLogProxy.java
new file mode 100644
index 0000000..308eeb5
--- /dev/null
+++ b/quickstep/src_protolog/com/android/quickstep/util/ActiveGestureProtoLogProxy.java
@@ -0,0 +1,538 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.util;
+
+import static android.view.MotionEvent.ACTION_DOWN;
+
+import static com.android.launcher3.Flags.enableActiveGestureProtoLog;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.CANCEL_RECENTS_ANIMATION;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.FINISH_RECENTS_ANIMATION;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.INVALID_VELOCITY_ON_SWIPE_UP;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.LAUNCHER_DESTROYED;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_DOWN;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_MOVE;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_UP;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.NAVIGATION_MODE_SWITCHED;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.ON_CANCEL_RECENTS_ANIMATION;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.ON_FINISH_RECENTS_ANIMATION;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.ON_SETTLED_ON_END_TARGET;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.ON_START_RECENTS_ANIMATION;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.QUICK_SWITCH_FROM_HOME_FAILED;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.QUICK_SWITCH_FROM_HOME_FALLBACK;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.RECENTS_ANIMATION_START_PENDING;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.RECENT_TASKS_MISSING;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.START_RECENTS_ANIMATION;
+import static com.android.quickstep.util.QuickstepProtoLogGroup.ACTIVE_GESTURE_LOG;
+
+import android.view.MotionEvent;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.internal.protolog.ProtoLog;
+import com.android.internal.protolog.common.IProtoLogGroup;
+
+/**
+ * Proxy class used for ActiveGestureLog ProtoLog support.
+ * <p>
+ * This file will have all of its static strings in the
+ * {@link ProtoLog#d(IProtoLogGroup, String, Object...)} calls replaced by dynamic code/strings.
+ * <p>
+ * When a new ActiveGestureLog entry needs to be added to the codebase (or and existing entry needs
+ * to be modified), add it here under a new unique method and make sure the ProtoLog entry matches
+ * to avoid confusion.
+ */
+public class ActiveGestureProtoLogProxy {
+
+ public static void logLauncherDestroyed() {
+ ActiveGestureLog.INSTANCE.addLog("Launcher destroyed", LAUNCHER_DESTROYED);
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG, "Launcher destroyed");
+ }
+
+ public static void logAbsSwipeUpHandlerOnRecentsAnimationCanceled() {
+ ActiveGestureLog.INSTANCE.addLog(
+ /* event= */ "AbsSwipeUpHandler.onRecentsAnimationCanceled",
+ /* gestureEvent= */ CANCEL_RECENTS_ANIMATION);
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG, "AbsSwipeUpHandler.onRecentsAnimationCanceled");
+ }
+
+ public static void logAbsSwipeUpHandlerOnRecentsAnimationFinished() {
+ ActiveGestureLog.INSTANCE.addLog(
+ /* event= */ "RecentsAnimationCallbacks.onAnimationFinished",
+ ON_FINISH_RECENTS_ANIMATION);
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG, "AbsSwipeUpHandler.onAnimationFinished");
+ }
+
+ public static void logAbsSwipeUpHandlerCancelCurrentAnimation() {
+ ActiveGestureLog.INSTANCE.addLog(
+ "AbsSwipeUpHandler.cancelCurrentAnimation",
+ ActiveGestureErrorDetector.GestureEvent.CANCEL_CURRENT_ANIMATION);
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG, "AbsSwipeUpHandler.cancelCurrentAnimation");
+
+ }
+
+ public static void logAbsSwipeUpHandlerOnTasksAppeared() {
+ ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
+ "AbsSwipeUpHandler.onTasksAppeared: ")
+ .append("force finish recents animation complete; clearing state callback."));
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG,
+ "AbsSwipeUpHandler.onTasksAppeared: force finish recents animation complete; "
+ + "clearing state callback.");
+
+ }
+
+ public static void logFinishRecentsAnimationOnTasksAppeared() {
+ ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimationOnTasksAppeared");
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG, "finishRecentsAnimationOnTasksAppeared");
+
+ }
+
+ public static void logRecentsAnimationCallbacksOnAnimationCancelled() {
+ ActiveGestureLog.INSTANCE.addLog(
+ /* event= */ "RecentsAnimationCallbacks.onAnimationCanceled",
+ /* gestureEvent= */ ON_CANCEL_RECENTS_ANIMATION);
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG, "RecentsAnimationCallbacks.onAnimationCanceled");
+
+ }
+
+ public static void logRecentsAnimationCallbacksOnTasksAppeared() {
+ ActiveGestureLog.INSTANCE.addLog("RecentsAnimationCallbacks.onTasksAppeared",
+ ActiveGestureErrorDetector.GestureEvent.TASK_APPEARED);
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG, "RecentsAnimationCallbacks.onTasksAppeared");
+
+ }
+
+ public static void logStartRecentsAnimation() {
+ ActiveGestureLog.INSTANCE.addLog(
+ /* event= */ "TaskAnimationManager.startRecentsAnimation",
+ /* gestureEvent= */ START_RECENTS_ANIMATION);
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG, "TaskAnimationManager.startRecentsAnimation");
+
+ }
+
+ public static void logLaunchingSideTaskFailed() {
+ ActiveGestureLog.INSTANCE.addLog("Unable to launch side task (no recents)");
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG, "Unable to launch side task (no recents)");
+
+ }
+
+ public static void logContinueRecentsAnimation() {
+ ActiveGestureLog.INSTANCE.addLog(/* event= */ "continueRecentsAnimation");
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG, "continueRecentsAnimation");
+
+ }
+
+ public static void logCleanUpRecentsAnimationSkipped() {
+ ActiveGestureLog.INSTANCE.addLog(
+ /* event= */ "cleanUpRecentsAnimation skipped due to wrong callbacks");
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG, "cleanUpRecentsAnimation skipped due to wrong callbacks");
+
+ }
+
+ public static void logCleanUpRecentsAnimation() {
+ ActiveGestureLog.INSTANCE.addLog(/* event= */ "cleanUpRecentsAnimation");
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG, "cleanUpRecentsAnimation");
+
+ }
+
+ public static void logOnInputEventUserLocked() {
+ ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString("TIS.onInputEvent: ")
+ .append("Cannot process input event: user is locked"));
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG,
+ "TIS.onInputEvent: Cannot process input event: user is locked");
+ }
+
+ public static void logOnInputIgnoringFollowingEvents() {
+ ActiveGestureLog.INSTANCE.addLog(
+ new ActiveGestureLog.CompoundString("TIS.onMotionEvent: A new gesture has been ")
+ .append("started, but a previously-requested recents ")
+ .append("animation hasn't started. Ignoring all following ")
+ .append("motion events."),
+ RECENTS_ANIMATION_START_PENDING);
+ if (!enableActiveGestureProtoLog()) 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.");
+ }
+
+ public static void logOnInputEventThreeButtonNav() {
+ ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString("TIS.onInputEvent: ")
+ .append("Cannot process input event: ")
+ .append("using 3-button nav and event is not a trackpad event"));
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG,
+ "TIS.onInputEvent: Cannot process input event: using 3-button nav and "
+ + "event is not a trackpad event");
+ }
+
+ public static void logPreloadRecentsAnimation() {
+ ActiveGestureLog.INSTANCE.addLog("preloadRecentsAnimation");
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG, "preloadRecentsAnimation");
+ }
+
+ public static void logRecentTasksMissing() {
+ ActiveGestureLog.INSTANCE.addLog("Null mRecentTasks", RECENT_TASKS_MISSING);
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG, "Null mRecentTasks");
+ }
+
+ public static void logExecuteHomeCommand() {
+ ActiveGestureLog.INSTANCE.addLog("OverviewCommandHelper.executeCommand(HOME)");
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG, "OverviewCommandHelper.executeCommand(HOME)");
+ }
+
+ public static void logFinishRecentsAnimationCallback() {
+ ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation-callback");
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG, "finishRecentsAnimation-callback");
+ }
+
+ public static void logOnScrollerAnimationAborted() {
+ ActiveGestureLog.INSTANCE.addLog("scroller animation aborted",
+ ActiveGestureErrorDetector.GestureEvent.SCROLLER_ANIMATION_ABORTED);
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG, "scroller animation aborted");
+ }
+
+ public static void logInputConsumerBecameActive(@NonNull String consumerName) {
+ ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(consumerName)
+ .append(" became active"));
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG, "%s became active", consumerName);
+ }
+
+ public static void logTaskLaunchFailed(int launchedTaskId) {
+ ActiveGestureLog.INSTANCE.addLog(
+ new ActiveGestureLog.CompoundString("Launch failed, task (id=")
+ .append(launchedTaskId)
+ .append(") finished mid transition"));
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG,
+ "Launch failed, task (id=%d) finished mid transition", launchedTaskId);
+ }
+
+ public static void logMotionPauseDetectorEvent(@NonNull String event) {
+ ActiveGestureLog.INSTANCE.addLog(event);
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG, "MotionPauseDetector: %s", event);
+ }
+
+ public static void logOnPageEndTransition(int nextPageIndex) {
+ ActiveGestureLog.INSTANCE.addLog(
+ "onPageEndTransition: current page index updated", nextPageIndex);
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG,
+ "onPageEndTransition: current page index updated: %d", nextPageIndex);
+ }
+
+ public static void logQuickSwitchFromHomeFallback(int taskIndex) {
+ ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
+ "Quick switch from home fallback case: The TaskView at index ")
+ .append(taskIndex)
+ .append(" is missing."),
+ QUICK_SWITCH_FROM_HOME_FALLBACK);
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG,
+ "Quick switch from home fallback case: The TaskView at index %d is missing.",
+ taskIndex);
+ }
+
+ public static void logQuickSwitchFromHomeFailed(int taskIndex) {
+ ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
+ "Quick switch from home failed: TaskViews at indices ")
+ .append(taskIndex)
+ .append(" and 0 are missing."),
+ QUICK_SWITCH_FROM_HOME_FAILED);
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG,
+ "Quick switch from home failed: TaskViews at indices %d and 0 are missing.",
+ taskIndex);
+ }
+
+ public static void logFinishRecentsAnimation(boolean toRecents) {
+ ActiveGestureLog.INSTANCE.addLog(
+ /* event= */ "finishRecentsAnimation",
+ /* extras= */ toRecents,
+ /* gestureEvent= */ FINISH_RECENTS_ANIMATION);
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG, "finishRecentsAnimation: %b", toRecents);
+ }
+
+ public static void logSetEndTarget(@NonNull String target) {
+ ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString("setEndTarget ")
+ .append(target),
+ /* gestureEvent= */ SET_END_TARGET);
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG, "setEndTarget %s", target);
+ }
+
+ public static void logStartHomeIntent(@NonNull String reason) {
+ ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
+ "OverviewComponentObserver.startHomeIntent: ").append(reason));
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG, "OverviewComponentObserver.startHomeIntent: %s", reason);
+ }
+
+ public static void logRunningTaskPackage(@NonNull String packageName) {
+ ActiveGestureLog.INSTANCE.addLog(
+ new ActiveGestureLog.CompoundString("Current running task package name=")
+ .append(packageName));
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG, "Current running task package name=%s", packageName);
+ }
+
+ public static void logSysuiStateFlags(@NonNull String stateFlags) {
+ ActiveGestureLog.INSTANCE.addLog(
+ new ActiveGestureLog.CompoundString("Current SystemUi state flags=")
+ .append(stateFlags));
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG, "Current SystemUi state flags=%s", stateFlags);
+ }
+
+ public static void logSetInputConsumer(@NonNull String consumerName, @NonNull String reason) {
+ ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString("setInputConsumer: ")
+ .append(consumerName)
+ .append(". reason(s):")
+ .append(reason));
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG,
+ "setInputConsumer: %s. reason(s):%s", consumerName, reason);
+ }
+
+ public static void logUpdateGestureStateRunningTask(
+ @NonNull String otherTaskPackage, @NonNull String runningTaskPackage) {
+ ActiveGestureLog.INSTANCE.addLog(
+ new ActiveGestureLog.CompoundString("Changing active task to ")
+ .append(otherTaskPackage)
+ .append(" because the previous task running on top of this one (")
+ .append(runningTaskPackage)
+ .append(") was excluded from recents"));
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG,
+ "Changing active task to %s because the previous task running on top of this "
+ + "one (%s) was excluded from recents",
+ otherTaskPackage,
+ runningTaskPackage);
+ }
+
+ public static void logOnInputEventActionUp(
+ int x, int y, int action, @NonNull String classification) {
+ String actionString = MotionEvent.actionToString(action);
+ ActiveGestureLog.INSTANCE.addLog(
+ new ActiveGestureLog.CompoundString("onMotionEvent(")
+ .append(x)
+ .append(", ")
+ .append(y)
+ .append("): ")
+ .append(actionString)
+ .append(", ")
+ .append(classification),
+ /* gestureEvent= */ action == ACTION_DOWN
+ ? MOTION_DOWN
+ : MOTION_UP);
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG,
+ "onMotionEvent(%d, %d): %s, %s", x, y, actionString, classification);
+ }
+
+ public static void logOnInputEventActionMove(
+ @NonNull String action, @NonNull String classification, int pointerCount) {
+ ActiveGestureLog.INSTANCE.addLog(
+ new ActiveGestureLog.CompoundString("onMotionEvent: ")
+ .append(action)
+ .append(",")
+ .append(classification)
+ .append(", pointerCount: ")
+ .append(pointerCount),
+ MOTION_MOVE);
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG,
+ "onMotionEvent: %s, %s, pointerCount: %d", action, classification, pointerCount);
+ }
+
+ public static void logOnInputEventGenericAction(
+ @NonNull String action, @NonNull String classification) {
+ ActiveGestureLog.INSTANCE.addLog(
+ new ActiveGestureLog.CompoundString("onMotionEvent: ")
+ .append(action)
+ .append(",")
+ .append(classification));
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG, "onMotionEvent: %s, %s", action, classification);
+ }
+
+ public static void logOnInputEventNavModeSwitched(
+ @NonNull String startNavMode, @NonNull String currentNavMode) {
+ ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString("TIS.onInputEvent: ")
+ .append("Navigation mode switched mid-gesture (")
+ .append(startNavMode)
+ .append(" -> ")
+ .append(currentNavMode)
+ .append("); cancelling gesture."),
+ NAVIGATION_MODE_SWITCHED);
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG,
+ "TIS.onInputEvent: Navigation mode switched mid-gesture (%s -> %s); "
+ + "cancelling gesture.",
+ startNavMode,
+ currentNavMode);
+ }
+
+ public static void logUnknownInputEvent(@NonNull String event) {
+ ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString("TIS.onInputEvent: ")
+ .append("Cannot process input event: received unknown event ")
+ .append(event));
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG,
+ "TIS.onInputEvent: Cannot process input event: received unknown event %s", event);
+
+ }
+
+ public static void logFinishRunningRecentsAnimation(boolean toHome) {
+ ActiveGestureLog.INSTANCE.addLog(
+ /* event= */ "finishRunningRecentsAnimation", toHome);
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG, "finishRunningRecentsAnimation: %b", toHome);
+
+ }
+
+ public static void logOnRecentsAnimationStartCancelled() {
+ ActiveGestureLog.INSTANCE.addLog(
+ /* event= */ "RecentsAnimationCallbacks.onAnimationStart (canceled)",
+ /* extras= */ 0,
+ /* gestureEvent= */ ON_START_RECENTS_ANIMATION);
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG, "RecentsAnimationCallbacks.onAnimationStart (canceled): 0");
+
+ }
+
+ public static void logOnRecentsAnimationStart(int appCount) {
+ ActiveGestureLog.INSTANCE.addLog(
+ /* event= */ "RecentsAnimationCallbacks.onAnimationStart",
+ /* extras= */ appCount,
+ /* gestureEvent= */ ON_START_RECENTS_ANIMATION);
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG,
+ "RecentsAnimationCallbacks.onAnimationStart (canceled): %d", appCount);
+
+ }
+
+ public static void logStartRecentsAnimationCallback(@NonNull String callback) {
+ ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
+ "TaskAnimationManager.startRecentsAnimation(")
+ .append(callback)
+ .append("): ")
+ .append("Setting mRecentsAnimationStartPending = false"));
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG,
+ "TaskAnimationManager.startRecentsAnimation(%s): "
+ + "Setting mRecentsAnimationStartPending = false",
+ callback);
+
+ }
+
+ public static void logSettingRecentsAnimationStartPending(boolean value) {
+ ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
+ "TaskAnimationManager.startRecentsAnimation: ")
+ .append("Setting mRecentsAnimationStartPending = ")
+ .append(value));
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG,
+ "TaskAnimationManager.startRecentsAnimation: "
+ + "Setting mRecentsAnimationStartPending = %b",
+ value);
+
+ }
+
+ public static void logLaunchingSideTask(int taskId) {
+ ActiveGestureLog.INSTANCE.addLog(
+ new ActiveGestureLog.CompoundString("Launching side task id=")
+ .append(taskId));
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG, "Launching side task id=", taskId);
+
+ }
+
+ public static void logDynamicString(@NonNull String string) {
+ logDynamicString(string, null);
+ }
+
+ public static void logDynamicString(
+ @NonNull String string,
+ @Nullable ActiveGestureErrorDetector.GestureEvent gestureEvent) {
+ ActiveGestureLog.INSTANCE.addLog(string, gestureEvent);
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG, "%s", string);
+ }
+
+ public static void logOnSettledOnEndTarget(@NonNull String endTarget) {
+ ActiveGestureLog.INSTANCE.addLog(
+ new ActiveGestureLog.CompoundString("onSettledOnEndTarget ")
+ .append(endTarget),
+ /* gestureEvent= */ ON_SETTLED_ON_END_TARGET);
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG, "onSettledOnEndTarget %s", endTarget);
+ }
+
+ public static void logOnCalculateEndTarget(float velocityX, float velocityY, double angle) {
+ ActiveGestureLog.INSTANCE.addLog(
+ new ActiveGestureLog.CompoundString("calculateEndTarget: velocities=(x=")
+ .append(velocityX)
+ .append("dp/ms, y=")
+ .append(velocityY)
+ .append("dp/ms), angle=")
+ .append(angle),
+ velocityX == 0 && velocityY == 0 ? INVALID_VELOCITY_ON_SWIPE_UP : null);
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG,
+ "calculateEndTarget: velocities=(x=%fdp/ms, y=%fdp/ms), angle=%f",
+ velocityX,
+ velocityY,
+ angle);
+ }
+
+ public static void logUnexpectedTaskAppeared(int taskId, @NonNull String packageName) {
+ ActiveGestureLog.INSTANCE.addLog(
+ new ActiveGestureLog.CompoundString("Forcefully finishing recents animation: ")
+ .append("Unexpected task appeared id=")
+ .append(taskId)
+ .append(" pkg=")
+ .append(packageName));
+ if (!enableActiveGestureProtoLog()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG,
+ "Forcefully finishing recents animation: Unexpected task appeared id=%d, pkg=%s",
+ taskId,
+ packageName);
+ }
+}
diff --git a/quickstep/src_protolog/com/android/quickstep/util/QuickstepProtoLogGroup.java b/quickstep/src_protolog/com/android/quickstep/util/QuickstepProtoLogGroup.java
new file mode 100644
index 0000000..d0863f8
--- /dev/null
+++ b/quickstep/src_protolog/com/android/quickstep/util/QuickstepProtoLogGroup.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.util;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.protolog.ProtoLog;
+import com.android.internal.protolog.common.IProtoLogGroup;
+
+import java.util.UUID;
+
+/** Enums used to interface with the ProtoLog API. */
+public enum QuickstepProtoLogGroup implements IProtoLogGroup {
+
+ ACTIVE_GESTURE_LOG(true, true, false, "ActiveGestureLog");
+
+ private final boolean mEnabled;
+ private volatile boolean mLogToProto;
+ private volatile boolean mLogToLogcat;
+ private final @NonNull String mTag;
+
+ public static void initProtoLog() {
+ ProtoLog.init(QuickstepProtoLogGroup.values());
+ }
+
+ /**
+ * @param enabled set to false to exclude all log statements for this group from
+ * compilation,
+ * they will not be available in runtime.
+ * @param logToProto enable binary logging for the group
+ * @param logToLogcat enable text logging for the group
+ * @param tag name of the source of the logged message
+ */
+ QuickstepProtoLogGroup(
+ boolean enabled, boolean logToProto, boolean logToLogcat, @NonNull String tag) {
+ this.mEnabled = enabled;
+ this.mLogToProto = logToProto;
+ this.mLogToLogcat = logToLogcat;
+ this.mTag = tag;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return mEnabled;
+ }
+
+ @Override
+ public boolean isLogToProto() {
+ return mLogToProto;
+ }
+
+ @Override
+ public boolean isLogToLogcat() {
+ return mLogToLogcat;
+ }
+
+ @Override
+ public boolean isLogToAny() {
+ return mLogToLogcat || mLogToProto;
+ }
+
+ @Override
+ public int getId() {
+ return Constants.LOG_START_ID + this.ordinal();
+ }
+
+ @Override
+ public @NonNull String getTag() {
+ return mTag;
+ }
+
+ @Override
+ public void setLogToProto(boolean logToProto) {
+ this.mLogToProto = logToProto;
+ }
+
+ @Override
+ public void setLogToLogcat(boolean logToLogcat) {
+ this.mLogToLogcat = logToLogcat;
+ }
+
+ private static final class Constants {
+
+ private static final int LOG_START_ID =
+ (int) (UUID.nameUUIDFromBytes(QuickstepProtoLogGroup.class.getName().getBytes())
+ .getMostSignificantBits() % Integer.MAX_VALUE);
+ }
+}
diff --git a/quickstep/tests/multivalentScreenshotTests/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewScreenshotTest.kt b/quickstep/tests/multivalentScreenshotTests/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewScreenshotTest.kt
index e4b8069..82a7625 100644
--- a/quickstep/tests/multivalentScreenshotTests/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewScreenshotTest.kt
+++ b/quickstep/tests/multivalentScreenshotTests/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewScreenshotTest.kt
@@ -97,6 +97,8 @@
fun bubbleBarView_expanded_threeBubbles() {
// if we're still expanding, wait with taking a screenshot
val shouldWait: (ComponentActivity, View) -> Boolean = { _, _ -> bubbleBarView.isExpanding }
+ // increase the frame limit to allow the animation to end before taking the screenshot
+ screenshotRule.frameLimit = 50
screenshotRule.screenshotTest(
"bubbleBarView_expanded_threeBubbles",
checkView = shouldWait,
diff --git a/quickstep/tests/multivalentScreenshotTests/src/com/android/launcher3/taskbar/bubbles/OWNERS b/quickstep/tests/multivalentScreenshotTests/src/com/android/launcher3/taskbar/bubbles/OWNERS
new file mode 100644
index 0000000..63c1498
--- /dev/null
+++ b/quickstep/tests/multivalentScreenshotTests/src/com/android/launcher3/taskbar/bubbles/OWNERS
@@ -0,0 +1,4 @@
+atsjenk@google.com
+liranb@google.com
+madym@google.com
+mpodolian@google.com
diff --git a/quickstep/tests/multivalentScreenshotTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutViewScreenshotTest.kt b/quickstep/tests/multivalentScreenshotTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutViewScreenshotTest.kt
new file mode 100644
index 0000000..467a9cb
--- /dev/null
+++ b/quickstep/tests/multivalentScreenshotTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutViewScreenshotTest.kt
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.taskbar.bubbles.flyout
+
+import android.content.Context
+import android.graphics.Color
+import android.graphics.PointF
+import android.graphics.drawable.ColorDrawable
+import androidx.test.core.app.ApplicationProvider
+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 [BubbleBarFlyoutView]. */
+@RunWith(ParameterizedAndroidJunit4::class)
+class BubbleBarFlyoutViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
+
+ private val context = ApplicationProvider.getApplicationContext<Context>()
+
+ companion object {
+ @Parameters(name = "{0}")
+ @JvmStatic
+ fun getTestSpecs() =
+ DeviceEmulationSpec.forDisplays(
+ Displays.Phone,
+ isDarkTheme = false,
+ isLandscape = false,
+ )
+ }
+
+ @get:Rule
+ val screenshotRule =
+ ViewScreenshotTestRule(
+ emulationSpec,
+ ViewScreenshotGoldenPathManager(getEmulatedDevicePathConfig(emulationSpec)),
+ )
+
+ @Test
+ fun bubbleBarFlyoutView_noAvatar_onRight() {
+ screenshotRule.screenshotTest("bubbleBarFlyoutView_noAvatar_onRight") { activity ->
+ activity.actionBar?.hide()
+ val flyout =
+ BubbleBarFlyoutView(context, FakeBubbleBarFlyoutPositioner(isOnLeft = false))
+ flyout.showFromCollapsed(
+ BubbleBarFlyoutMessage(
+ senderAvatar = null,
+ senderName = "sender",
+ message = "message",
+ isGroupChat = false,
+ )
+ ) {}
+ flyout.updateExpansionProgress(1f)
+ flyout
+ }
+ }
+
+ @Test
+ fun bubbleBarFlyoutView_noAvatar_onLeft() {
+ screenshotRule.screenshotTest("bubbleBarFlyoutView_noAvatar_onLeft") { activity ->
+ activity.actionBar?.hide()
+ val flyout =
+ BubbleBarFlyoutView(context, FakeBubbleBarFlyoutPositioner(isOnLeft = true))
+ flyout.showFromCollapsed(
+ BubbleBarFlyoutMessage(
+ senderAvatar = null,
+ senderName = "sender",
+ message = "message",
+ isGroupChat = false,
+ )
+ ) {}
+ flyout.updateExpansionProgress(1f)
+ flyout
+ }
+ }
+
+ @Test
+ fun bubbleBarFlyoutView_noAvatar_longMessage() {
+ screenshotRule.screenshotTest("bubbleBarFlyoutView_noAvatar_longMessage") { activity ->
+ activity.actionBar?.hide()
+ val flyout =
+ BubbleBarFlyoutView(context, FakeBubbleBarFlyoutPositioner(isOnLeft = true))
+ flyout.showFromCollapsed(
+ BubbleBarFlyoutMessage(
+ senderAvatar = null,
+ senderName = "sender",
+ message = "really, really, really, really, really long message. like really.",
+ isGroupChat = false,
+ )
+ ) {}
+ flyout.updateExpansionProgress(1f)
+ flyout
+ }
+ }
+
+ @Test
+ fun bubbleBarFlyoutView_avatar_onRight() {
+ screenshotRule.screenshotTest("bubbleBarFlyoutView_avatar_onRight") { activity ->
+ activity.actionBar?.hide()
+ val flyout =
+ BubbleBarFlyoutView(context, FakeBubbleBarFlyoutPositioner(isOnLeft = false))
+ flyout.showFromCollapsed(
+ BubbleBarFlyoutMessage(
+ senderAvatar = ColorDrawable(Color.RED),
+ senderName = "sender",
+ message = "message",
+ isGroupChat = true,
+ )
+ ) {}
+ flyout.updateExpansionProgress(1f)
+ flyout
+ }
+ }
+
+ @Test
+ fun bubbleBarFlyoutView_avatar_onLeft() {
+ screenshotRule.screenshotTest("bubbleBarFlyoutView_avatar_onLeft") { activity ->
+ activity.actionBar?.hide()
+ val flyout =
+ BubbleBarFlyoutView(context, FakeBubbleBarFlyoutPositioner(isOnLeft = true))
+ flyout.showFromCollapsed(
+ BubbleBarFlyoutMessage(
+ senderAvatar = ColorDrawable(Color.RED),
+ senderName = "sender",
+ message = "message",
+ isGroupChat = true,
+ )
+ ) {}
+ flyout.updateExpansionProgress(1f)
+ flyout
+ }
+ }
+
+ @Test
+ fun bubbleBarFlyoutView_avatar_longMessage() {
+ screenshotRule.screenshotTest("bubbleBarFlyoutView_avatar_longMessage") { activity ->
+ activity.actionBar?.hide()
+ val flyout =
+ BubbleBarFlyoutView(context, FakeBubbleBarFlyoutPositioner(isOnLeft = true))
+ flyout.showFromCollapsed(
+ BubbleBarFlyoutMessage(
+ senderAvatar = ColorDrawable(Color.RED),
+ senderName = "sender",
+ message = "really, really, really, really, really long message. like really.",
+ isGroupChat = true,
+ )
+ ) {}
+ flyout.updateExpansionProgress(1f)
+ flyout
+ }
+ }
+
+ private class FakeBubbleBarFlyoutPositioner(override val isOnLeft: Boolean) :
+ BubbleBarFlyoutPositioner {
+ override val targetTy = 0f
+ override val distanceToCollapsedPosition = PointF(0f, 0f)
+ override val collapsedSize = 30f
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendControllerTest.kt
new file mode 100644
index 0000000..f3fff9f
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendControllerTest.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.taskbar
+
+import android.animation.AnimatorTestRule
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_DRAGGING
+import com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_IN_LAUNCHER
+import com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_TOUCHING
+import com.android.launcher3.taskbar.rules.TaskbarModeRule
+import com.android.launcher3.taskbar.rules.TaskbarModeRule.Mode.TRANSIENT
+import com.android.launcher3.taskbar.rules.TaskbarModeRule.TaskbarMode
+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.SystemUiProxy
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TestRule
+import org.junit.runner.RunWith
+import org.junit.runners.model.Statement
+
+@RunWith(LauncherMultivalentJUnit::class)
+@EmulatedDevices(["pixelTablet2023"])
+class TaskbarAutohideSuspendControllerTest {
+
+ private val context = TaskbarWindowSandboxContext.create(getInstrumentation().targetContext)
+
+ @get:Rule(order = 0) val animatorTestRule = AnimatorTestRule(this)
+ @get:Rule(order = 1)
+ val systemUiProxyRule = TestRule { base, _ ->
+ object : Statement() {
+ override fun evaluate() {
+ getInstrumentation().runOnMainSync {
+ context.applicationContext.putObject(
+ SystemUiProxy.INSTANCE,
+ object : SystemUiProxy(context) {
+ override fun notifyTaskbarAutohideSuspend(suspend: Boolean) {
+ latestSuspendNotification = suspend
+ }
+ },
+ )
+ }
+ base.evaluate()
+ }
+ }
+ }
+ @get:Rule(order = 2) val taskbarModeRule = TaskbarModeRule(context)
+ @get:Rule(order = 3) val taskbarUnitTestRule = TaskbarUnitTestRule(this, context)
+
+ @InjectController lateinit var autohideSuspendController: TaskbarAutohideSuspendController
+ @InjectController lateinit var stashController: TaskbarStashController
+
+ private var latestSuspendNotification: Boolean? = null
+
+ @Test
+ fun testUpdateFlag_suspendInLauncher_notifiesSuspend() {
+ getInstrumentation().runOnMainSync {
+ autohideSuspendController.updateFlag(FLAG_AUTOHIDE_SUSPEND_IN_LAUNCHER, true)
+ }
+ assertThat(latestSuspendNotification).isTrue()
+ }
+
+ @Test
+ fun testUpdateFlag_toggleSuspendDraggingTwice_notifiesUnsuspend() {
+ getInstrumentation().runOnMainSync {
+ autohideSuspendController.updateFlag(FLAG_AUTOHIDE_SUSPEND_DRAGGING, true)
+ autohideSuspendController.updateFlag(FLAG_AUTOHIDE_SUSPEND_DRAGGING, false)
+ }
+ assertThat(latestSuspendNotification).isFalse()
+ }
+
+ @Test
+ fun testUpdateFlag_resetsAlreadyUnsetFlag_noNotifyUnsuspend() {
+ getInstrumentation().runOnMainSync {
+ autohideSuspendController.updateFlag(FLAG_AUTOHIDE_SUSPEND_DRAGGING, false)
+ }
+ assertThat(latestSuspendNotification).isNull()
+ }
+
+ @Test
+ @TaskbarMode(TRANSIENT)
+ fun testUpdateFlag_suspendTransientTaskbarForTouch_cancelsAutoStashTimeout() {
+ // Unstash and verify alarm.
+ getInstrumentation().runOnMainSync {
+ stashController.updateAndAnimateTransientTaskbar(false)
+ animatorTestRule.advanceTimeBy(stashController.stashDuration)
+ }
+ assertThat(stashController.timeoutAlarm.alarmPending()).isTrue()
+
+ // EDU opens while unstashed.
+ getInstrumentation().runOnMainSync {
+ autohideSuspendController.updateFlag(FLAG_AUTOHIDE_SUSPEND_TOUCHING, true)
+ }
+ assertThat(stashController.timeoutAlarm.alarmPending()).isFalse()
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarScrimViewControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarScrimViewControllerTest.kt
new file mode 100644
index 0000000..3524961
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarScrimViewControllerTest.kt
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.taskbar
+
+import android.animation.AnimatorTestRule
+import android.view.View.GONE
+import android.view.View.VISIBLE
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import com.android.launcher3.taskbar.rules.TaskbarModeRule
+import com.android.launcher3.taskbar.rules.TaskbarModeRule.Mode.PINNED
+import com.android.launcher3.taskbar.rules.TaskbarModeRule.Mode.TRANSIENT
+import com.android.launcher3.taskbar.rules.TaskbarModeRule.TaskbarMode
+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.SystemUiProxy
+import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED
+import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED
+import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE
+import com.android.wm.shell.shared.bubbles.BubbleConstants.BUBBLE_EXPANDED_SCRIM_ALPHA
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(LauncherMultivalentJUnit::class)
+@EmulatedDevices(["pixelTablet2023"])
+class TaskbarScrimViewControllerTest {
+ private val context = TaskbarWindowSandboxContext.create(getInstrumentation().targetContext)
+
+ @get:Rule(order = 0) val taskbarModeRule = TaskbarModeRule(context)
+ @get:Rule(order = 1) val animatorTestRule = AnimatorTestRule(this)
+ @get:Rule(order = 2) val taskbarUnitTestRule = TaskbarUnitTestRule(this, context)
+
+ @InjectController lateinit var scrimViewController: TaskbarScrimViewController
+
+ // Default animation duration.
+ private val animationDuration =
+ context.resources.getInteger(android.R.integer.config_mediumAnimTime).toLong()
+
+ @Test
+ @TaskbarMode(PINNED)
+ fun testOnTaskbarVisibleChanged_onlyTaskbarVisible_noScrim() {
+ getInstrumentation().runOnMainSync {
+ scrimViewController.onTaskbarVisibilityChanged(VISIBLE)
+ scrimViewController.updateStateForSysuiFlags(0, true)
+ }
+ assertThat(scrimViewController.scrimAlpha).isEqualTo(0)
+ }
+
+ @Test
+ @TaskbarMode(PINNED)
+ fun testOnTaskbarVisibilityChanged_pinnedTaskbarVisibleWithBubblesExpanded_showsScrim() {
+ getInstrumentation().runOnMainSync {
+ scrimViewController.updateStateForSysuiFlags(SYSUI_STATE_BUBBLES_EXPANDED, true)
+ scrimViewController.onTaskbarVisibilityChanged(VISIBLE)
+ animatorTestRule.advanceTimeBy(animationDuration)
+ }
+
+ assertThat(scrimViewController.scrimAlpha).isEqualTo(BUBBLE_EXPANDED_SCRIM_ALPHA)
+ }
+
+ @Test
+ @TaskbarMode(PINNED)
+ fun testOnTaskbarVisibilityChanged_pinnedTaskbarHiddenDuringScrim_hidesScrim() {
+ getInstrumentation().runOnMainSync {
+ scrimViewController.onTaskbarVisibilityChanged(VISIBLE)
+ scrimViewController.updateStateForSysuiFlags(SYSUI_STATE_BUBBLES_EXPANDED, true)
+ }
+ assertThat(scrimViewController.scrimAlpha).isEqualTo(BUBBLE_EXPANDED_SCRIM_ALPHA)
+
+ getInstrumentation().runOnMainSync {
+ scrimViewController.onTaskbarVisibilityChanged(GONE)
+ animatorTestRule.advanceTimeBy(animationDuration)
+ }
+ assertThat(scrimViewController.scrimAlpha).isEqualTo(0)
+ }
+
+ @Test
+ @TaskbarMode(PINNED)
+ fun testOnTaskbarVisibilityChanged_notificationsOverPinnedTaskbarAndBubbles_noScrim() {
+ getInstrumentation().runOnMainSync {
+ scrimViewController.updateStateForSysuiFlags(
+ SYSUI_STATE_BUBBLES_EXPANDED or SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE,
+ true,
+ )
+ scrimViewController.onTaskbarVisibilityChanged(VISIBLE)
+ }
+ assertThat(scrimViewController.scrimAlpha).isEqualTo(0)
+ }
+
+ @Test
+ @TaskbarMode(PINNED)
+ fun testOnTaskbarVisibilityChanged_pinnedTaskbarWithBubbleMenu_darkerScrim() {
+ getInstrumentation().runOnMainSync {
+ scrimViewController.onTaskbarVisibilityChanged(VISIBLE)
+ scrimViewController.updateStateForSysuiFlags(
+ SYSUI_STATE_BUBBLES_EXPANDED or SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED,
+ true,
+ )
+ }
+ assertThat(scrimViewController.scrimAlpha).isGreaterThan(BUBBLE_EXPANDED_SCRIM_ALPHA)
+ }
+
+ @Test
+ @TaskbarMode(TRANSIENT)
+ fun testOnTaskbarVisibilityChanged_stashedTaskbarWithBubbles_noScrim() {
+ getInstrumentation().runOnMainSync {
+ scrimViewController.updateStateForSysuiFlags(SYSUI_STATE_BUBBLES_EXPANDED, true)
+ scrimViewController.onTaskbarVisibilityChanged(VISIBLE)
+ }
+ assertThat(scrimViewController.scrimAlpha).isEqualTo(0)
+ }
+
+ @Test
+ @TaskbarMode(PINNED)
+ fun testOnClick_scrimShown_performsSystemBack() {
+ var backPressed = false
+ context.applicationContext.putObject(
+ SystemUiProxy.INSTANCE,
+ object : SystemUiProxy(context) {
+ override fun onBackPressed() {
+ backPressed = true
+ }
+ },
+ )
+
+ getInstrumentation().runOnMainSync {
+ scrimViewController.updateStateForSysuiFlags(SYSUI_STATE_BUBBLES_EXPANDED, true)
+ scrimViewController.onTaskbarVisibilityChanged(VISIBLE)
+ }
+ assertThat(scrimViewController.scrimView.isClickable).isTrue()
+
+ getInstrumentation().runOnMainSync { scrimViewController.scrimView.performClick() }
+ assertThat(backPressed).isTrue()
+ }
+
+ @Test
+ @TaskbarMode(TRANSIENT)
+ fun testOnClick_scrimHidden_notClickable() {
+ getInstrumentation().runOnMainSync {
+ scrimViewController.updateStateForSysuiFlags(SYSUI_STATE_BUBBLES_EXPANDED, true)
+ scrimViewController.onTaskbarVisibilityChanged(VISIBLE)
+ }
+ assertThat(scrimViewController.scrimView.isClickable).isFalse()
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarStashControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarStashControllerTest.kt
new file mode 100644
index 0000000..e736446
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarStashControllerTest.kt
@@ -0,0 +1,681 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.taskbar
+
+import android.animation.AnimatorTestRule
+import android.platform.test.annotations.EnableFlags
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import com.android.launcher3.R
+import com.android.launcher3.taskbar.StashedHandleViewController.ALPHA_INDEX_STASHED
+import com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_EDU_OPEN
+import com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_APP
+import com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_OVERVIEW
+import com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_STASHED_LAUNCHER_STATE
+import com.android.launcher3.taskbar.TaskbarStashController.FLAG_STASHED_DEVICE_LOCKED
+import com.android.launcher3.taskbar.TaskbarStashController.FLAG_STASHED_IME
+import com.android.launcher3.taskbar.TaskbarStashController.FLAG_STASHED_IN_APP_AUTO
+import com.android.launcher3.taskbar.TaskbarStashController.FLAG_STASHED_SMALL_SCREEN
+import com.android.launcher3.taskbar.TaskbarStashController.FLAG_STASHED_SYSUI
+import com.android.launcher3.taskbar.TaskbarStashController.TASKBAR_STASH_DURATION
+import com.android.launcher3.taskbar.TaskbarStashController.TASKBAR_STASH_DURATION_FOR_IME
+import com.android.launcher3.taskbar.TaskbarStashController.TRANSIENT_TASKBAR_STASH_ALPHA_DURATION
+import com.android.launcher3.taskbar.TaskbarStashController.TRANSIENT_TASKBAR_STASH_DURATION
+import com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_STASH
+import com.android.launcher3.taskbar.bubbles.BubbleControllers
+import com.android.launcher3.taskbar.rules.TaskbarModeRule
+import com.android.launcher3.taskbar.rules.TaskbarModeRule.Mode.PINNED
+import com.android.launcher3.taskbar.rules.TaskbarModeRule.Mode.THREE_BUTTONS
+import com.android.launcher3.taskbar.rules.TaskbarModeRule.Mode.TRANSIENT
+import com.android.launcher3.taskbar.rules.TaskbarModeRule.TaskbarMode
+import com.android.launcher3.taskbar.rules.TaskbarPinningPreferenceRule
+import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule
+import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.InjectController
+import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.UserSetupMode
+import com.android.launcher3.taskbar.rules.TaskbarWindowSandboxContext
+import com.android.launcher3.util.LauncherMultivalentJUnit
+import com.android.launcher3.util.LauncherMultivalentJUnit.EmulatedDevices
+import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED
+import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING
+import com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_BAR
+import com.google.common.truth.Truth.assertThat
+import java.util.Optional
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(LauncherMultivalentJUnit::class)
+@EmulatedDevices(["pixelTablet2023"])
+class TaskbarStashControllerTest {
+ private val context = TaskbarWindowSandboxContext.create(getInstrumentation().targetContext)
+
+ @get:Rule(order = 0) val taskbarModeRule = TaskbarModeRule(context)
+ @get:Rule(order = 1) val taskbarPinningPreferenceRule = TaskbarPinningPreferenceRule(context)
+ @get:Rule(order = 2) val animatorTestRule = AnimatorTestRule(this)
+ @get:Rule(order = 3) val taskbarUnitTestRule = TaskbarUnitTestRule(this, context)
+
+ @InjectController lateinit var stashController: TaskbarStashController
+ @InjectController lateinit var viewController: TaskbarViewController
+ @InjectController lateinit var stashedHandleViewController: StashedHandleViewController
+ @InjectController lateinit var dragLayerController: TaskbarDragLayerController
+ @InjectController lateinit var autohideSuspendController: TaskbarAutohideSuspendController
+ @InjectController lateinit var bubbleControllers: Optional<BubbleControllers>
+
+ private val activityContext by taskbarUnitTestRule::activityContext
+
+ // Disable hardware keyboard mode during tests.
+ @Before fun enableSoftwareIme() = TaskbarStashController.enableSoftwareImeForTests(true)
+
+ @After fun resetIme() = TaskbarStashController.enableSoftwareImeForTests(false)
+
+ @After fun cancelTimeoutIfExists() = stashController.cancelTimeoutIfExists()
+
+ @Test
+ @TaskbarMode(TRANSIENT)
+ fun testInit_transientMode_stashedInApp() {
+ assertThat(stashController.isStashedInApp).isTrue()
+ }
+
+ @Test
+ @TaskbarMode(PINNED)
+ fun testInit_pinnedMode_unstashedInApp() {
+ assertThat(stashController.isStashedInApp).isFalse()
+ }
+
+ @Test
+ @UserSetupMode
+ @TaskbarMode(PINNED)
+ fun testInit_userSetupWithPinnedMode_stashedInApp() {
+ assertThat(stashController.isStashedInApp).isTrue()
+ }
+
+ @Test
+ @TaskbarMode(PINNED)
+ fun testSetSetupUiVisible_true_stashedInApp() {
+ getInstrumentation().runOnMainSync { stashController.setSetupUIVisible(true) }
+ assertThat(stashController.isStashedInApp).isTrue()
+ }
+
+ @Test
+ @TaskbarMode(PINNED)
+ fun testSetSetupUiVisible_false_unstashedInApp() {
+ getInstrumentation().runOnMainSync { stashController.setSetupUIVisible(false) }
+ assertThat(stashController.isStashedInApp).isFalse()
+ }
+
+ @Test
+ fun testRecreateAsTransient_timeoutStarted() {
+ taskbarPinningPreferenceRule.isPinned = true
+ activityContext.controllers.sharedState?.taskbarWasPinned = true
+
+ taskbarPinningPreferenceRule.isPinned = false
+ assertThat(stashController.timeoutAlarm.alarmPending()).isTrue()
+ }
+
+ @Test
+ @TaskbarMode(TRANSIENT)
+ fun testSupportsVisualStashing_transientMode_supported() {
+ assertThat(stashController.supportsVisualStashing()).isTrue()
+ }
+
+ @Test
+ @TaskbarMode(PINNED)
+ fun testSupportsVisualStashing_pinnedMode_supported() {
+ assertThat(stashController.supportsVisualStashing()).isTrue()
+ }
+
+ @Test
+ @TaskbarMode(THREE_BUTTONS)
+ fun testSupportsVisualStashing_threeButtonsMode_unsupported() {
+ assertThat(stashController.supportsVisualStashing()).isFalse()
+ }
+
+ @Test
+ @TaskbarMode(TRANSIENT)
+ fun testGetStashDuration_transientMode() {
+ assertThat(stashController.stashDuration).isEqualTo(TRANSIENT_TASKBAR_STASH_DURATION)
+ }
+
+ @Test
+ @TaskbarMode(PINNED)
+ fun testGetStashDuration_pinnedMode() {
+ assertThat(stashController.stashDuration).isEqualTo(TASKBAR_STASH_DURATION)
+ }
+
+ @Test
+ @TaskbarMode(PINNED)
+ fun testIsStashed_pinnedInApp_isUnstashed() {
+ getInstrumentation().runOnMainSync {
+ stashController.updateStateForFlag(FLAG_IN_APP, true)
+ stashController.applyState(0)
+ }
+ assertThat(stashController.isStashed).isFalse()
+ }
+
+ @Test
+ @TaskbarMode(TRANSIENT)
+ fun testIsStashed_transientInApp_isStashed() {
+ getInstrumentation().runOnMainSync {
+ stashController.updateStateForFlag(FLAG_IN_APP, true)
+ stashController.applyState(0)
+ }
+ assertThat(stashController.isStashed).isTrue()
+ }
+
+ @Test
+ @TaskbarMode(TRANSIENT)
+ fun testIsStashed_transientNotInApp_isUnstashed() {
+ getInstrumentation().runOnMainSync {
+ stashController.updateStateForFlag(FLAG_IN_APP, false)
+ stashController.applyState(0)
+ }
+ assertThat(stashController.isStashed).isFalse()
+ }
+
+ @Test
+ fun testIsStashed_stashedInLauncherState_isStashed() {
+ getInstrumentation().runOnMainSync {
+ stashController.updateStateForFlag(FLAG_IN_APP, false)
+ stashController.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE, true)
+ stashController.applyState(0)
+ }
+ assertThat(stashController.isStashed).isTrue()
+ }
+
+ @Test
+ @TaskbarMode(TRANSIENT)
+ fun testIsStashed_transientInOverview_isUnstashed() {
+ getInstrumentation().runOnMainSync {
+ stashController.updateStateForFlag(FLAG_IN_APP, false)
+ stashController.updateStateForFlag(FLAG_IN_OVERVIEW, true)
+ stashController.applyState(0)
+ }
+ assertThat(stashController.isStashed).isFalse()
+ }
+
+ @Test
+ @TaskbarMode(PINNED)
+ fun testIsStashed_pinnedInOverviewWithIme_isStashed() {
+ getInstrumentation().runOnMainSync {
+ stashController.updateStateForFlag(FLAG_IN_APP, false)
+ stashController.updateStateForFlag(FLAG_IN_OVERVIEW, true)
+ stashController.updateStateForFlag(FLAG_STASHED_IME, true)
+ stashController.applyState(0)
+ }
+ assertThat(stashController.isStashed).isTrue()
+ }
+
+ @Test
+ @TaskbarMode(PINNED)
+ fun testIsStashed_pinnedTaskbarWithPinnedApp_isStashed() {
+ getInstrumentation().runOnMainSync {
+ stashController.updateStateForFlag(FLAG_IN_APP, true)
+ stashController.updateStateForFlag(FLAG_STASHED_SYSUI, true) // App pinned.
+ stashController.applyState(0)
+ }
+ assertThat(stashController.isStashed).isTrue()
+ }
+
+ @Test
+ fun testIsInStashedLauncherState_flagUnset_false() {
+ stashController.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE, false)
+ assertThat(stashController.isInStashedLauncherState).isFalse()
+ }
+
+ @Test
+ @TaskbarMode(THREE_BUTTONS)
+ fun testIsInStashedLauncherState_flagSetInThreeButtonsMode_false() {
+ stashController.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE, true)
+ assertThat(stashController.isInStashedLauncherState).isFalse()
+ }
+
+ @Test
+ @TaskbarMode(PINNED)
+ fun testIsInStashedLauncherState_flagSetInPinnedMode_true() {
+ stashController.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE, true)
+ assertThat(stashController.isInStashedLauncherState).isTrue()
+ }
+
+ @Test
+ @TaskbarMode(PINNED)
+ fun testIsTaskbarVisibleAndNotStashing_pinnedButNotVisible_false() {
+ getInstrumentation().runOnMainSync {
+ viewController.taskbarIconAlpha.get(ALPHA_INDEX_STASH).value = 0f
+ }
+ assertThat(stashController.isTaskbarVisibleAndNotStashing).isFalse()
+ }
+
+ @Test
+ @TaskbarMode(TRANSIENT)
+ fun testIsTaskbarVisibleAndNotStashing_visibleButStashed_false() {
+ getInstrumentation().runOnMainSync {
+ viewController.taskbarIconAlpha.get(ALPHA_INDEX_STASH).value = 1f
+ }
+ assertThat(stashController.isTaskbarVisibleAndNotStashing).isFalse()
+ }
+
+ @Test
+ @TaskbarMode(PINNED)
+ fun testIsTaskbarVisibleAndNotStashing_pinnedAndVisible_true() {
+ getInstrumentation().runOnMainSync {
+ viewController.taskbarIconAlpha.get(ALPHA_INDEX_STASH).value = 1f
+ }
+ assertThat(stashController.isTaskbarVisibleAndNotStashing).isTrue()
+ }
+
+ @Test
+ @TaskbarMode(TRANSIENT)
+ fun testGetTouchableHeight_isStashed_stashedHeight() {
+ assertThat(stashController.touchableHeight).isEqualTo(stashController.stashedHeight)
+ }
+
+ @Test
+ @TaskbarMode(TRANSIENT)
+ fun testGetTouchableHeight_unstashedTransientMode_heightAndBottomMargin() {
+ getInstrumentation().runOnMainSync {
+ stashController.updateStateForFlag(FLAG_STASHED_IN_APP_AUTO, false)
+ stashController.applyState(0)
+ }
+
+ val expectedHeight =
+ activityContext.deviceProfile.run { taskbarHeight + taskbarBottomMargin }
+ assertThat(stashController.touchableHeight).isEqualTo(expectedHeight)
+ }
+
+ @Test
+ @TaskbarMode(PINNED)
+ fun testGetTouchableHeight_pinnedMode_taskbarHeight() {
+ assertThat(stashController.touchableHeight)
+ .isEqualTo(activityContext.deviceProfile.taskbarHeight)
+ }
+
+ @Test
+ @TaskbarMode(TRANSIENT)
+ fun testGetContentHeightToReportToApps_transientMode_stashedHeight() {
+ assertThat(stashController.contentHeightToReportToApps)
+ .isEqualTo(stashController.stashedHeight)
+ }
+
+ @Test
+ @TaskbarMode(THREE_BUTTONS)
+ fun testGetContentHeightToReportToApps_threeButtonsMode_taskbarHeight() {
+ assertThat(stashController.contentHeightToReportToApps)
+ .isEqualTo(activityContext.deviceProfile.taskbarHeight)
+ }
+
+ @Test
+ @TaskbarMode(PINNED)
+ fun testGetContentHeightToReportToApps_pinnedMode_taskbarHeight() {
+ assertThat(stashController.contentHeightToReportToApps)
+ .isEqualTo(activityContext.deviceProfile.taskbarHeight)
+ }
+
+ @Test
+ @TaskbarMode(PINNED)
+ @UserSetupMode
+ fun testGetContentHeightToReportToApps_pinnedInSetupMode_setupWizardInsets() {
+ assertThat(stashController.contentHeightToReportToApps)
+ .isEqualTo(context.resources.getDimensionPixelSize(R.dimen.taskbar_suw_insets))
+ }
+
+ @Test
+ @TaskbarMode(PINNED)
+ fun testGetContentHeightToReportToApps_pinnedModeButFolded_stashedHeight() {
+ getInstrumentation().runOnMainSync {
+ stashedHandleViewController.stashedHandleAlpha.get(ALPHA_INDEX_STASHED).value = 1f
+ stashController.updateStateForFlag(FLAG_STASHED_SMALL_SCREEN, true)
+ }
+ assertThat(stashController.contentHeightToReportToApps)
+ .isEqualTo(stashController.stashedHeight)
+ }
+
+ @Test
+ @TaskbarMode(PINNED)
+ fun testGetContentHeightToReportToApps_homeDisabledWhenFolded_zeroHeight() {
+ getInstrumentation().runOnMainSync {
+ stashedHandleViewController.stashedHandleAlpha.get(ALPHA_INDEX_STASHED).value = 1f
+ stashedHandleViewController.setIsHomeButtonDisabled(true)
+ stashController.updateStateForFlag(FLAG_STASHED_SMALL_SCREEN, true)
+ }
+ assertThat(stashController.contentHeightToReportToApps).isEqualTo(0)
+ }
+
+ @Test
+ @TaskbarMode(TRANSIENT)
+ fun testGetTappableHeightToReportToApps_transientMode_zeroHeight() {
+ assertThat(stashController.tappableHeightToReportToApps).isEqualTo(0)
+ }
+
+ @Test
+ @TaskbarMode(PINNED)
+ fun testGetTappableHeightToReportToApps_pinnedMode_taskbarHeight() {
+ assertThat(stashController.tappableHeightToReportToApps)
+ .isEqualTo(activityContext.deviceProfile.taskbarHeight)
+ }
+
+ @Test
+ @TaskbarMode(TRANSIENT)
+ fun testUpdateAndAnimateTransientTaskbar_unstashTaskbar_updatesState() {
+ getInstrumentation().runOnMainSync {
+ stashController.updateAndAnimateTransientTaskbar(false)
+ }
+ assertThat(stashController.isStashed).isFalse()
+ }
+
+ @Test
+ @TaskbarMode(TRANSIENT)
+ fun testUpdateAndAnimateTransientTaskbar_runUnstashAnimation_startsTaskbarTimeout() {
+ getInstrumentation().runOnMainSync {
+ stashController.updateAndAnimateTransientTaskbar(false)
+ animatorTestRule.advanceTimeBy(stashController.stashDuration)
+ }
+ assertThat(stashController.timeoutAlarm.alarmPending()).isTrue()
+ }
+
+ @Test
+ @TaskbarMode(TRANSIENT)
+ fun testUpdateAndAnimateTransientTaskbar_finishTaskbarTimeout_taskbarStashes() {
+ getInstrumentation().runOnMainSync {
+ stashController.updateAndAnimateTransientTaskbar(false)
+ animatorTestRule.advanceTimeBy(stashController.stashDuration)
+ }
+ assertThat(stashController.timeoutAlarm.alarmPending()).isTrue()
+
+ getInstrumentation().runOnMainSync {
+ stashController.timeoutAlarm.finishAlarm()
+ animatorTestRule.advanceTimeBy(stashController.stashDuration)
+ }
+ assertThat(stashController.isStashed).isTrue()
+ }
+
+ @Test
+ @TaskbarMode(TRANSIENT)
+ fun testUpdateAndAnimateTransientTaskbar_autoHideSuspendedForEdu_remainsUnstashed() {
+ getInstrumentation().runOnMainSync {
+ stashController.updateAndAnimateTransientTaskbar(false)
+ animatorTestRule.advanceTimeBy(stashController.stashDuration)
+ }
+
+ getInstrumentation().runOnMainSync {
+ autohideSuspendController.updateFlag(FLAG_AUTOHIDE_SUSPEND_EDU_OPEN, true)
+ stashController.updateAndAnimateTransientTaskbar(true)
+ animatorTestRule.advanceTimeBy(stashController.stashDuration)
+ }
+ assertThat(stashController.isStashed).isFalse()
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
+ @TaskbarMode(TRANSIENT)
+ fun testUpdateAndAnimateTransientTaskbar_unstashTaskbarWithBubbles_bubbleBarUnstashes() {
+ getInstrumentation().runOnMainSync {
+ bubbleControllers.get().bubbleBarViewController.setHiddenForBubbles(false)
+ bubbleControllers.get().bubbleStashController.stashBubbleBarImmediate()
+ stashController.updateAndAnimateTransientTaskbar(false, true)
+ }
+ assertThat(bubbleControllers.get().bubbleStashController.isStashed).isFalse()
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
+ @TaskbarMode(TRANSIENT)
+ fun testUpdateAndAnimateTransientTaskbar_unstashTaskbarWithoutBubbles_bubbleBarStashed() {
+ getInstrumentation().runOnMainSync {
+ bubbleControllers.get().bubbleBarViewController.setHiddenForBubbles(false)
+ bubbleControllers.get().bubbleStashController.stashBubbleBarImmediate()
+ stashController.updateAndAnimateTransientTaskbar(false, false)
+ }
+ assertThat(bubbleControllers.get().bubbleStashController.isStashed).isTrue()
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
+ @TaskbarMode(TRANSIENT)
+ fun testUpdateAndAnimateTransientTaskbar_stashTaskbarWithBubbles_bubbleBarStashes() {
+ getInstrumentation().runOnMainSync {
+ bubbleControllers.get().bubbleBarViewController.setHiddenForBubbles(false)
+ bubbleControllers.get().bubbleStashController.showBubbleBarImmediate()
+ stashController.updateAndAnimateTransientTaskbar(true, true)
+ }
+ assertThat(bubbleControllers.get().bubbleStashController.isStashed).isTrue()
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
+ @TaskbarMode(TRANSIENT)
+ fun testUpdateAndAnimateTransientTaskbar_stashTaskbarWithoutBubbles_bubbleBarUnstashed() {
+ getInstrumentation().runOnMainSync {
+ bubbleControllers.get().bubbleBarViewController.setHiddenForBubbles(false)
+ bubbleControllers.get().bubbleStashController.showBubbleBarImmediate()
+ stashController.updateAndAnimateTransientTaskbar(true, false)
+ }
+ assertThat(bubbleControllers.get().bubbleStashController.isStashed).isFalse()
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
+ @TaskbarMode(TRANSIENT)
+ fun testUpdateAndAnimateTransientTaskbar_bubbleBarExpandedBeforeTimeout_expandedAfterwards() {
+ getInstrumentation().runOnMainSync {
+ bubbleControllers.get().bubbleBarViewController.setHiddenForBubbles(false)
+ bubbleControllers.get().bubbleBarViewController.isExpanded = true
+ stashController.updateAndAnimateTransientTaskbar(false)
+ animatorTestRule.advanceTimeBy(stashController.stashDuration)
+ }
+ assertThat(stashController.timeoutAlarm.alarmPending()).isTrue()
+
+ getInstrumentation().runOnMainSync {
+ stashController.timeoutAlarm.finishAlarm()
+ animatorTestRule.advanceTimeBy(stashController.stashDuration)
+ }
+ assertThat(bubbleControllers.get().bubbleBarViewController.isExpanded).isTrue()
+ }
+
+ @Test
+ @TaskbarMode(PINNED)
+ fun testToggleTaskbarStash_pinnedMode_doesNothing() {
+ getInstrumentation().runOnMainSync { stashController.toggleTaskbarStash() }
+ assertThat(stashController.isStashed).isFalse()
+ }
+
+ @Test
+ @TaskbarMode(TRANSIENT)
+ fun testToggleTaskbarStash_transientMode_unstashesTaskbar() {
+ getInstrumentation().runOnMainSync { stashController.toggleTaskbarStash() }
+ assertThat(stashController.isStashed).isFalse()
+ }
+
+ @Test
+ @TaskbarMode(TRANSIENT)
+ fun testToggleTaskbarStash_twiceInTransientMode_stashesTaskbar() {
+ getInstrumentation().runOnMainSync {
+ stashController.toggleTaskbarStash()
+ stashController.toggleTaskbarStash()
+ }
+ assertThat(stashController.isStashed).isTrue()
+ }
+
+ @Test
+ @TaskbarMode(TRANSIENT)
+ fun testToggleTaskbarStash_notInAppWithTransientMode_doesNothing() {
+ getInstrumentation().runOnMainSync {
+ stashController.updateStateForFlag(FLAG_IN_APP, false)
+ stashController.applyState(0)
+ stashController.toggleTaskbarStash()
+ }
+ assertThat(stashController.isStashed).isFalse()
+ }
+
+ @Test
+ @TaskbarMode(TRANSIENT)
+ fun testAnimateTransientTaskbar_bubblesShownInOverview_stashesTaskbar() {
+ // Start in Overview. Should unstash Taskbar.
+ getInstrumentation().runOnMainSync {
+ stashController.updateStateForFlag(FLAG_STASHED_IN_APP_AUTO, false)
+ stashController.updateStateForFlag(FLAG_IN_APP, false)
+ stashController.updateStateForFlag(FLAG_IN_OVERVIEW, true)
+ stashController.applyState(0)
+ }
+ assertThat(stashController.isStashed).isFalse()
+
+ // Expand bubbles. Should stash Taskbar.
+ getInstrumentation().runOnMainSync {
+ stashController.updateStateForSysuiFlags(SYSUI_STATE_BUBBLES_EXPANDED, false)
+ animatorTestRule.advanceTimeBy(TASKBAR_STASH_DURATION)
+ }
+ assertThat(stashController.isStashed).isTrue()
+ }
+
+ @Test
+ @TaskbarMode(PINNED)
+ fun testAnimatePinnedTaskbar_imeShown_replacesIconsWithHandle() {
+ getInstrumentation().runOnMainSync {
+ stashController.updateStateForSysuiFlags(SYSUI_STATE_IME_SHOWING, false)
+ animatorTestRule.advanceTimeBy(TASKBAR_STASH_DURATION_FOR_IME)
+ }
+ assertThat(viewController.areIconsVisible()).isFalse()
+ assertThat(stashedHandleViewController.isStashedHandleVisible).isTrue()
+ }
+
+ @Test
+ @TaskbarMode(PINNED)
+ fun testAnimatePinnedTaskbar_imeHidden_replacesHandleWithIcons() {
+ getInstrumentation().runOnMainSync {
+ stashController.updateStateForSysuiFlags(SYSUI_STATE_IME_SHOWING, true)
+ animatorTestRule.advanceTimeBy(0)
+ }
+
+ getInstrumentation().runOnMainSync {
+ stashController.updateStateForSysuiFlags(0, true)
+ animatorTestRule.advanceTimeBy(0)
+ }
+ assertThat(stashedHandleViewController.isStashedHandleVisible).isFalse()
+ assertThat(viewController.areIconsVisible()).isTrue()
+ }
+
+ @Test
+ @TaskbarMode(PINNED)
+ fun testAnimatePinnedTaskbar_imeHidden_verifyAnimationDuration() {
+ // Start with IME shown.
+ getInstrumentation().runOnMainSync {
+ stashController.updateStateForSysuiFlags(SYSUI_STATE_IME_SHOWING, true)
+ animatorTestRule.advanceTimeBy(0)
+ }
+
+ // Hide IME with animation.
+ getInstrumentation().runOnMainSync {
+ stashController.updateStateForSysuiFlags(0, false)
+ // Fast forward without start delay.
+ animatorTestRule.advanceTimeBy(TASKBAR_STASH_DURATION_FOR_IME)
+ }
+ // Icons should not be visible yet due to start delay.
+ assertThat(viewController.areIconsVisible()).isFalse()
+
+ // Advance by start delay retroactively. Animation should complete.
+ getInstrumentation().runOnMainSync {
+ animatorTestRule.advanceTimeBy(stashController.taskbarStashStartDelayForIme)
+ }
+ assertThat(viewController.areIconsVisible()).isTrue()
+ }
+
+ @Test
+ @TaskbarMode(THREE_BUTTONS)
+ fun testAnimateThreeButtonsTaskbar_imeShown_hidesIconsAndBg() {
+ getInstrumentation().runOnMainSync {
+ stashController.updateStateForSysuiFlags(SYSUI_STATE_IME_SHOWING, false)
+ animatorTestRule.advanceTimeBy(TASKBAR_STASH_DURATION_FOR_IME)
+ }
+ assertThat(viewController.areIconsVisible()).isFalse()
+ assertThat(dragLayerController.imeBgTaskbar.value).isEqualTo(0)
+ }
+
+ @Test
+ @TaskbarMode(THREE_BUTTONS)
+ fun testAnimateThreeButtonsTaskbar_imeHidden_showsIconsAndBg() {
+ getInstrumentation().runOnMainSync {
+ stashController.updateStateForSysuiFlags(SYSUI_STATE_IME_SHOWING, false)
+ animatorTestRule.advanceTimeBy(TASKBAR_STASH_DURATION_FOR_IME)
+ }
+
+ getInstrumentation().runOnMainSync {
+ stashController.updateStateForSysuiFlags(0, false)
+ animatorTestRule.advanceTimeBy(
+ TASKBAR_STASH_DURATION_FOR_IME + stashController.taskbarStashStartDelayForIme
+ )
+ }
+ assertThat(viewController.areIconsVisible()).isTrue()
+ assertThat(dragLayerController.imeBgTaskbar.value).isEqualTo(1)
+ }
+
+ @Test
+ @TaskbarMode(PINNED)
+ fun testSetSystemGestureInProgress_whileImeShown_unstashesTaskbar() {
+ getInstrumentation().runOnMainSync {
+ stashController.updateStateForSysuiFlags(SYSUI_STATE_IME_SHOWING, true)
+ animatorTestRule.advanceTimeBy(0)
+ }
+
+ getInstrumentation().runOnMainSync {
+ stashController.setSystemGestureInProgress(true)
+ animatorTestRule.advanceTimeBy(
+ TASKBAR_STASH_DURATION_FOR_IME + stashController.taskbarStashStartDelayForIme
+ )
+ }
+ assertThat(stashController.isStashed).isFalse()
+ }
+
+ @Test
+ @TaskbarMode(PINNED)
+ fun testUnlockTransition_pinnedMode_fadesOutHandle() {
+ getInstrumentation().runOnMainSync {
+ stashController.updateStateForFlag(FLAG_STASHED_DEVICE_LOCKED, true)
+ stashController.applyState(0)
+ }
+ assertThat(stashedHandleViewController.isStashedHandleVisible).isTrue()
+
+ getInstrumentation().runOnMainSync {
+ stashController.updateStateForFlag(FLAG_STASHED_DEVICE_LOCKED, false)
+ stashController.applyState()
+ animatorTestRule.advanceTimeBy(stashController.stashDuration)
+ }
+ assertThat(stashedHandleViewController.isStashedHandleVisible).isFalse()
+ }
+
+ @Test
+ @TaskbarMode(TRANSIENT)
+ fun testUnlockTransition_transientMode_fadesOutHandleEarly() {
+ getInstrumentation().runOnMainSync {
+ stashController.updateStateForFlag(FLAG_IN_APP, false)
+ stashController.updateStateForFlag(FLAG_STASHED_DEVICE_LOCKED, true)
+ stashController.applyState(0)
+ }
+ assertThat(stashedHandleViewController.isStashedHandleVisible).isTrue()
+
+ getInstrumentation().runOnMainSync {
+ stashController.updateStateForFlag(FLAG_STASHED_DEVICE_LOCKED, false)
+ stashController.applyState()
+ // Time it takes for just the handle to hide (full stash animation is longer).
+ animatorTestRule.advanceTimeBy(TRANSIENT_TASKBAR_STASH_ALPHA_DURATION)
+ }
+ assertThat(stashedHandleViewController.isStashedHandleVisible).isFalse()
+ }
+}
+
+private fun TaskbarStashController.updateStateForFlag(flag: Int, value: Boolean) {
+ updateStateForFlag(flag.toLong(), value)
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsControllerTest.kt
index 43d924a..f783e40 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsControllerTest.kt
@@ -199,8 +199,8 @@
assertThat(editText?.hasFocus()).isTrue()
}
- private companion object {
- private val TEST_APPS =
+ companion object {
+ val TEST_APPS =
Array(16) {
AppInfo(
ComponentName(
@@ -213,6 +213,6 @@
)
}
- private val TEST_PREDICTED_APPS = TEST_APPS.take(4).map { WorkspaceItemInfo(it) }
+ val TEST_PREDICTED_APPS = TEST_APPS.take(4).map { WorkspaceItemInfo(it) }
}
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewControllerTest.kt
new file mode 100644
index 0000000..04f02e9
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewControllerTest.kt
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.taskbar.allapps
+
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import com.android.launcher3.R
+import com.android.launcher3.appprediction.AppsDividerView
+import com.android.launcher3.appprediction.AppsDividerView.DividerType
+import com.android.launcher3.appprediction.PredictionRowView
+import com.android.launcher3.taskbar.TaskbarStashController
+import com.android.launcher3.taskbar.TaskbarStashController.FLAG_STASHED_IN_APP_AUTO
+import com.android.launcher3.taskbar.allapps.TaskbarAllAppsControllerTest.Companion.TEST_PREDICTED_APPS
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayController
+import com.android.launcher3.taskbar.rules.TaskbarModeRule
+import com.android.launcher3.taskbar.rules.TaskbarModeRule.Mode.PINNED
+import com.android.launcher3.taskbar.rules.TaskbarModeRule.Mode.TRANSIENT
+import com.android.launcher3.taskbar.rules.TaskbarModeRule.TaskbarMode
+import com.android.launcher3.taskbar.rules.TaskbarPreferenceRule
+import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule
+import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.InjectController
+import com.android.launcher3.taskbar.rules.TaskbarWindowSandboxContext
+import com.android.launcher3.util.LauncherMultivalentJUnit
+import com.android.launcher3.util.LauncherMultivalentJUnit.EmulatedDevices
+import com.android.launcher3.util.OnboardingPrefs.ALL_APPS_VISITED_COUNT
+import com.android.launcher3.util.TestUtil
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(LauncherMultivalentJUnit::class)
+@EmulatedDevices(["pixelFoldable2023"])
+class TaskbarAllAppsViewControllerTest {
+
+ private val context = TaskbarWindowSandboxContext.create(getInstrumentation().targetContext)
+
+ @get:Rule(order = 0) val taskbarModeRule = TaskbarModeRule(context)
+ @get:Rule(order = 1)
+ val allAppsVisitedPreferenceRule =
+ TaskbarPreferenceRule(context, ALL_APPS_VISITED_COUNT.prefItem)
+ @get:Rule(order = 2) val taskbarUnitTestRule = TaskbarUnitTestRule(this, context)
+
+ @InjectController lateinit var overlayController: TaskbarOverlayController
+ @InjectController lateinit var stashController: TaskbarStashController
+
+ private val searchSessionController =
+ TestUtil.getOnUiThread { TaskbarSearchSessionController.newInstance(context) }
+
+ @After
+ fun cleanUpSearchSessionController() {
+ getInstrumentation().runOnMainSync { searchSessionController.onDestroy() }
+ }
+
+ @Test
+ @TaskbarMode(TRANSIENT)
+ fun testShow_transientMode_stashesTaskbar() {
+ getInstrumentation().runOnMainSync {
+ stashController.updateStateForFlag(FLAG_STASHED_IN_APP_AUTO.toLong(), false)
+ stashController.applyState(0)
+ }
+
+ val viewController = createViewController()
+ getInstrumentation().runOnMainSync { viewController.show(false) }
+ assertThat(stashController.isStashed).isTrue()
+ }
+
+ @Test
+ @TaskbarMode(PINNED)
+ fun testShow_pinnedMode_taskbarDoesNotStash() {
+ val viewController = createViewController()
+ getInstrumentation().runOnMainSync { viewController.show(false) }
+ assertThat(stashController.isStashed).isFalse()
+ }
+
+ @Test
+ @TaskbarMode(TRANSIENT)
+ fun testHide_transientMode_unstashesTaskbar() {
+ getInstrumentation().runOnMainSync {
+ stashController.updateStateForFlag(FLAG_STASHED_IN_APP_AUTO.toLong(), false)
+ stashController.applyState(0)
+ }
+
+ val viewController = createViewController()
+ getInstrumentation().runOnMainSync { viewController.show(false) }
+ getInstrumentation().runOnMainSync { viewController.close(false) }
+ assertThat(stashController.isStashed).isFalse()
+ }
+
+ @Test
+ fun testShow_firstAllAppsVisit_hasAllAppsTextDivider() {
+ allAppsVisitedPreferenceRule.value = 0
+ val viewController = createViewController()
+ getInstrumentation().runOnMainSync { viewController.show(false) }
+
+ val appsView = overlayController.requestWindow().appsView
+ getInstrumentation().runOnMainSync {
+ appsView.floatingHeaderView
+ .findFixedRowByType(PredictionRowView::class.java)
+ .setPredictedApps(TEST_PREDICTED_APPS)
+ }
+
+ val dividerView =
+ appsView.floatingHeaderView.findFixedRowByType(AppsDividerView::class.java)
+ assertThat(dividerView.dividerType).isEqualTo(DividerType.ALL_APPS_LABEL)
+ }
+
+ @Test
+ fun testShow_maxAllAppsVisitedCount_hasLineDivider() {
+ allAppsVisitedPreferenceRule.value = ALL_APPS_VISITED_COUNT.maxCount
+ val viewController = createViewController()
+ getInstrumentation().runOnMainSync { viewController.show(false) }
+
+ val appsView = overlayController.requestWindow().appsView
+ getInstrumentation().runOnMainSync {
+ appsView.floatingHeaderView
+ .findFixedRowByType(PredictionRowView::class.java)
+ .setPredictedApps(TEST_PREDICTED_APPS)
+ }
+
+ val dividerView =
+ appsView.floatingHeaderView.findFixedRowByType(AppsDividerView::class.java)
+ assertThat(dividerView.dividerType).isEqualTo(DividerType.LINE)
+ }
+
+ private fun createViewController(): TaskbarAllAppsViewController {
+ return TestUtil.getOnUiThread {
+ val overlayContext = overlayController.requestWindow()
+ TaskbarAllAppsViewController(
+ overlayContext,
+ overlayContext.layoutInflater.inflate(
+ R.layout.taskbar_all_apps_sheet,
+ overlayContext.dragLayer,
+ false,
+ ) as TaskbarAllAppsSlideInView,
+ taskbarUnitTestRule.activityContext.controllers,
+ searchSessionController,
+ /* showKeyboard= */ false, // Covered in TaskbarAllAppsControllerTest.
+ )
+ }
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/BubbleBarInputConsumerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/BubbleBarInputConsumerTest.kt
index 785ec66..c8f50f7 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/BubbleBarInputConsumerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/BubbleBarInputConsumerTest.kt
@@ -49,6 +49,7 @@
@Mock private lateinit var bubbleDismissController: BubbleDismissController
@Mock private lateinit var bubbleBarPinController: BubbleBarPinController
@Mock private lateinit var bubblePinController: BubblePinController
+ @Mock private lateinit var bubbleBarSwipeController: BubbleBarSwipeController
@Mock private lateinit var bubbleCreator: BubbleCreator
@Mock private lateinit var motionEvent: MotionEvent
@@ -67,7 +68,8 @@
bubbleDismissController,
bubbleBarPinController,
bubblePinController,
- bubbleCreator
+ Optional.of(bubbleBarSwipeController),
+ bubbleCreator,
)
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/BubbleBarSwipeControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/BubbleBarSwipeControllerTest.kt
new file mode 100644
index 0000000..97847be
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/BubbleBarSwipeControllerTest.kt
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.taskbar.bubbles
+
+import android.animation.AnimatorTestRule
+import android.content.Context
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController
+import com.android.launcher3.touch.OverScroll
+import com.google.common.truth.Truth.assertThat
+import java.util.Optional
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.atLeastOnce
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@RunWith(AndroidJUnit4::class)
+class BubbleBarSwipeControllerTest {
+
+ companion object {
+ const val UNSTASH_THRESHOLD = 100
+ const val EXPAND_THRESHOLD = 200
+ const val MAX_OVERSCROLL = 300
+
+ const val UP_BELOW_UNSTASH = -UNSTASH_THRESHOLD + 10f
+ const val UP_ABOVE_UNSTASH = -UNSTASH_THRESHOLD - 10f
+ const val UP_ABOVE_EXPAND = -EXPAND_THRESHOLD - 10f
+ const val DOWN_BELOW_UNSTASH = UNSTASH_THRESHOLD + 10f
+ }
+
+ private val context = ApplicationProvider.getApplicationContext<Context>()
+
+ @get:Rule(order = 0) val mockitoRule: MockitoRule = MockitoJUnit.rule()
+ @get:Rule(order = 1) val animatorTestRule: AnimatorTestRule = AnimatorTestRule(this)
+
+ private lateinit var bubbleBarSwipeController: BubbleBarSwipeController
+
+ @Mock private lateinit var bubbleBarController: BubbleBarController
+ @Mock private lateinit var bubbleBarViewController: BubbleBarViewController
+ @Mock private lateinit var bubbleStashController: BubbleStashController
+ @Mock private lateinit var bubbleStashedHandleViewController: BubbleStashedHandleViewController
+ @Mock private lateinit var bubbleDragController: BubbleDragController
+ @Mock private lateinit var bubbleDismissController: BubbleDismissController
+ @Mock private lateinit var bubbleBarPinController: BubbleBarPinController
+ @Mock private lateinit var bubblePinController: BubblePinController
+ @Mock private lateinit var bubbleCreator: BubbleCreator
+
+ @Before
+ fun setUp() {
+ val dimensionProvider =
+ object : BubbleBarSwipeController.DimensionProvider {
+ override val unstashThreshold: Int
+ get() = UNSTASH_THRESHOLD
+
+ override val expandThreshold: Int
+ get() = EXPAND_THRESHOLD
+
+ override val maxOverscroll: Int
+ get() = MAX_OVERSCROLL
+ }
+ bubbleBarSwipeController = BubbleBarSwipeController(context, dimensionProvider)
+
+ val bubbleControllers =
+ BubbleControllers(
+ bubbleBarController,
+ bubbleBarViewController,
+ bubbleStashController,
+ Optional.of(bubbleStashedHandleViewController),
+ bubbleDragController,
+ bubbleDismissController,
+ bubbleBarPinController,
+ bubblePinController,
+ Optional.of(bubbleBarSwipeController),
+ bubbleCreator,
+ )
+
+ bubbleBarSwipeController.init(bubbleControllers)
+ }
+
+ private fun testViewsHaveDampedTranslationOnSwipe(swipe: Float) {
+ val dampedTranslation = -OverScroll.dampedScroll(-swipe, MAX_OVERSCROLL).toFloat()
+ getInstrumentation().runOnMainSync {
+ bubbleBarSwipeController.start()
+ bubbleBarSwipeController.swipeTo(swipe)
+ }
+ verify(bubbleStashedHandleViewController).setTranslationYForSwipe(dampedTranslation)
+ verify(bubbleBarViewController).setTranslationYForSwipe(dampedTranslation)
+ }
+
+ @Test
+ fun swipeUp_stashedBar_belowUnstashThreshold_viewsHaveDampedTranslation() {
+ setUpStashedBar()
+ testViewsHaveDampedTranslationOnSwipe(UP_BELOW_UNSTASH)
+ }
+
+ @Test
+ fun swipeUp_stashedBar_aboveUnstashThreshold_viewsHaveDampedTranslation() {
+ setUpStashedBar()
+ testViewsHaveDampedTranslationOnSwipe(UP_ABOVE_UNSTASH)
+ }
+
+ @Test
+ fun swipeUp_stashedBar_aboveExpandThreshold_viewsHaveDampedTranslation() {
+ setUpStashedBar()
+ testViewsHaveDampedTranslationOnSwipe(UP_ABOVE_EXPAND)
+ }
+
+ @Test
+ fun swipeUp_collapsedBar_aboveUnstashThreshold_viewsHaveDampedTranslation() {
+ setUpCollapsedBar()
+ testViewsHaveDampedTranslationOnSwipe(UP_ABOVE_UNSTASH)
+ }
+
+ @Test
+ fun swipeUp_collapsedBar_aboveExpandThreshold_viewsHaveDampedTranslation() {
+ setUpCollapsedBar()
+ testViewsHaveDampedTranslationOnSwipe(UP_ABOVE_EXPAND)
+ }
+
+ private fun testViewsTranslationResetOnFinish(swipe: Float) {
+ getInstrumentation().runOnMainSync {
+ bubbleBarSwipeController.start()
+ bubbleBarSwipeController.swipeTo(swipe)
+ bubbleBarSwipeController.finish()
+ // We use a spring animation. Advance by 5 seconds to give it time to finish
+ animatorTestRule.advanceTimeBy(5000)
+ }
+ val handleSwipeTranslation = argumentCaptor<Float>()
+ val barSwipeTranslation = argumentCaptor<Float>()
+ verify(bubbleStashedHandleViewController, atLeastOnce())
+ .setTranslationYForSwipe(handleSwipeTranslation.capture())
+ verify(bubbleBarViewController, atLeastOnce())
+ .setTranslationYForSwipe(barSwipeTranslation.capture())
+
+ assertThat(handleSwipeTranslation.firstValue).isNonZero()
+ assertThat(handleSwipeTranslation.lastValue).isZero()
+
+ assertThat(barSwipeTranslation.firstValue).isNonZero()
+ assertThat(barSwipeTranslation.lastValue).isZero()
+ }
+
+ @Test
+ fun swipeUp_stashedBar_belowUnstashThreshold_animateTranslationToZeroOnFinish() {
+ setUpStashedBar()
+ testViewsTranslationResetOnFinish(UP_BELOW_UNSTASH)
+ }
+
+ @Test
+ fun swipeUp_stashedBar_aboveUnstashThreshold_animateTranslationToZeroOnFinish() {
+ setUpStashedBar()
+ testViewsTranslationResetOnFinish(UP_ABOVE_UNSTASH)
+ }
+
+ @Test
+ fun swipeUp_stashedBar_aboveExpandThreshold_animateTranslationToZeroOnFinish() {
+ setUpStashedBar()
+ testViewsTranslationResetOnFinish(UP_ABOVE_EXPAND)
+ }
+
+ @Test
+ fun swipeUp_collapsedBar_aboveUnstashThreshold_animateTranslationToZeroOnFinish() {
+ setUpCollapsedBar()
+ testViewsTranslationResetOnFinish(UP_ABOVE_UNSTASH)
+ }
+
+ @Test
+ fun swipeUp_collapsedBar_aboveExpandThreshold_animateTranslationToZeroOnFinish() {
+ setUpCollapsedBar()
+ testViewsTranslationResetOnFinish(UP_ABOVE_EXPAND)
+ }
+
+ @Test
+ fun swipeUp_stashedBar_belowUnstashThreshold_doesNotShowBar() {
+ setUpStashedBar()
+ getInstrumentation().runOnMainSync {
+ bubbleBarSwipeController.start()
+ bubbleBarSwipeController.swipeTo(UP_BELOW_UNSTASH)
+ }
+ verify(bubbleStashController, never()).showBubbleBar(any())
+ }
+
+ @Test
+ fun swipeUp_stashedBar_belowUnstashThreshold_isSwipeGestureFalse() {
+ setUpStashedBar()
+ getInstrumentation().runOnMainSync {
+ bubbleBarSwipeController.start()
+ bubbleBarSwipeController.swipeTo(UP_BELOW_UNSTASH)
+ }
+ assertThat(bubbleBarSwipeController.isSwipeGesture()).isFalse()
+ }
+
+ @Test
+ fun swipeUp_stashedBar_aboveUnstashThreshold_unstashBubbleBar() {
+ setUpStashedBar()
+ getInstrumentation().runOnMainSync {
+ bubbleBarSwipeController.start()
+ bubbleBarSwipeController.swipeTo(UP_ABOVE_UNSTASH)
+ }
+ verify(bubbleStashController).showBubbleBar(expandBubbles = false)
+ }
+
+ @Test
+ fun swipeUp_stashedBar_overUnstashThreshold_isSwipeGestureTrue() {
+ setUpStashedBar()
+ getInstrumentation().runOnMainSync {
+ bubbleBarSwipeController.start()
+ bubbleBarSwipeController.swipeTo(UP_ABOVE_UNSTASH)
+ }
+ assertThat(bubbleBarSwipeController.isSwipeGesture()).isTrue()
+ }
+
+ @Test
+ fun swipeUp_stashedBar_overUnstashThresholdMultipleTimes_unstashBubbleBarOnce() {
+ setUpStashedBar()
+ getInstrumentation().runOnMainSync {
+ bubbleBarSwipeController.start()
+ bubbleBarSwipeController.swipeTo(UP_ABOVE_UNSTASH)
+ bubbleBarSwipeController.swipeTo(UP_BELOW_UNSTASH)
+ bubbleBarSwipeController.swipeTo(UP_ABOVE_UNSTASH)
+ }
+ verify(bubbleStashController).showBubbleBar(expandBubbles = false)
+ }
+
+ @Test
+ fun swipeUp_stashedBar_overExpandThreshold_doesNotExpandBeforeFinish() {
+ setUpStashedBar()
+ getInstrumentation().runOnMainSync {
+ bubbleBarSwipeController.start()
+ bubbleBarSwipeController.swipeTo(UP_ABOVE_EXPAND)
+ }
+ verify(bubbleStashController).showBubbleBar(expandBubbles = false)
+ getInstrumentation().runOnMainSync { bubbleBarSwipeController.finish() }
+ verify(bubbleStashController).showBubbleBar(expandBubbles = true)
+ }
+
+ @Test
+ fun swipeUp_stashedBar_overExpandThreshold_isSwipeGestureTrue() {
+ setUpStashedBar()
+ getInstrumentation().runOnMainSync {
+ bubbleBarSwipeController.start()
+ bubbleBarSwipeController.swipeTo(UP_ABOVE_EXPAND)
+ }
+ assertThat(bubbleBarSwipeController.isSwipeGesture()).isTrue()
+ }
+
+ @Test
+ fun swipeUp_stashedBar_overExpandThresholdAndBackDown_doesNotExpandAfterFinish() {
+ setUpStashedBar()
+ getInstrumentation().runOnMainSync {
+ bubbleBarSwipeController.start()
+ bubbleBarSwipeController.swipeTo(UP_ABOVE_EXPAND)
+ bubbleBarSwipeController.swipeTo(UP_ABOVE_UNSTASH)
+ }
+ verify(bubbleStashController).showBubbleBar(expandBubbles = false)
+ getInstrumentation().runOnMainSync { bubbleBarSwipeController.finish() }
+ verify(bubbleStashController).showBubbleBar(expandBubbles = false)
+ }
+
+ @Test
+ fun swipeUp_expandedBar_swipeIgnored() {
+ setUpExpandedBar()
+ getInstrumentation().runOnMainSync {
+ bubbleBarSwipeController.start()
+ bubbleBarSwipeController.swipeTo(UP_ABOVE_EXPAND)
+ bubbleBarSwipeController.swipeTo(DOWN_BELOW_UNSTASH)
+ bubbleBarSwipeController.finish()
+ }
+ verify(bubbleStashedHandleViewController, never()).setTranslationYForSwipe(any())
+ verify(bubbleBarViewController, never()).setTranslationYForSwipe(any())
+ verify(bubbleStashController, never()).showBubbleBar(any())
+ }
+
+ @Test
+ fun swipeDown_stashedBar_swipeIgnored() {
+ setUpStashedBar()
+ getInstrumentation().runOnMainSync {
+ bubbleBarSwipeController.start()
+ bubbleBarSwipeController.swipeTo(DOWN_BELOW_UNSTASH)
+ }
+ verify(bubbleStashedHandleViewController, never()).setTranslationYForSwipe(any())
+ verify(bubbleBarViewController, never()).setTranslationYForSwipe(any())
+ verify(bubbleStashController, never()).showBubbleBar(any())
+ }
+
+ private fun setUpStashedBar() {
+ whenever(bubbleStashController.isStashed).thenReturn(true)
+ whenever(bubbleStashController.isBubbleBarVisible()).thenReturn(false)
+ whenever(bubbleBarViewController.isExpanded).thenReturn(false)
+ }
+
+ private fun setUpCollapsedBar() {
+ whenever(bubbleStashController.isStashed).thenReturn(false)
+ whenever(bubbleStashController.isBubbleBarVisible()).thenReturn(true)
+ whenever(bubbleBarViewController.isExpanded).thenReturn(false)
+ }
+
+ private fun setUpExpandedBar() {
+ whenever(bubbleStashController.isStashed).thenReturn(false)
+ whenever(bubbleStashController.isBubbleBarVisible()).thenReturn(true)
+ whenever(bubbleBarViewController.isExpanded).thenReturn(true)
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutControllerTest.kt
new file mode 100644
index 0000000..fb12ac9
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutControllerTest.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.taskbar.bubbles.flyout
+
+import android.content.Context
+import android.graphics.PointF
+import android.view.Gravity
+import android.widget.FrameLayout
+import android.widget.TextView
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.R
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** Unit tests for [BubbleBarFlyoutController] */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class BubbleBarFlyoutControllerTest {
+
+ private lateinit var flyoutController: BubbleBarFlyoutController
+ private lateinit var flyoutContainer: FrameLayout
+ private val context = ApplicationProvider.getApplicationContext<Context>()
+ private val flyoutMessage =
+ BubbleBarFlyoutMessage(senderAvatar = null, "sender name", "message", isGroupChat = false)
+ private var onLeft = true
+
+ @Before
+ fun setUp() {
+ flyoutContainer = FrameLayout(context)
+ val positioner =
+ object : BubbleBarFlyoutPositioner {
+ override val isOnLeft
+ get() = onLeft
+
+ override val targetTy = 50f
+ override val distanceToCollapsedPosition = PointF(100f, 200f)
+ override val collapsedSize = 30f
+ }
+ flyoutController = BubbleBarFlyoutController(flyoutContainer, positioner)
+ }
+
+ @Test
+ fun flyoutPosition_left() {
+ flyoutController.setUpFlyout(flyoutMessage)
+ assertThat(flyoutContainer.childCount).isEqualTo(1)
+ val flyout = flyoutContainer.getChildAt(0)
+ val lp = flyout.layoutParams as FrameLayout.LayoutParams
+ assertThat(lp.gravity).isEqualTo(Gravity.BOTTOM or Gravity.LEFT)
+ assertThat(flyout.translationY).isEqualTo(50f)
+ }
+
+ @Test
+ fun flyoutPosition_right() {
+ onLeft = false
+ flyoutController.setUpFlyout(flyoutMessage)
+ assertThat(flyoutContainer.childCount).isEqualTo(1)
+ val flyout = flyoutContainer.getChildAt(0)
+ val lp = flyout.layoutParams as FrameLayout.LayoutParams
+ assertThat(lp.gravity).isEqualTo(Gravity.BOTTOM or Gravity.RIGHT)
+ assertThat(flyout.translationY).isEqualTo(50f)
+ }
+
+ @Test
+ fun flyoutMessage() {
+ flyoutController.setUpFlyout(flyoutMessage)
+ assertThat(flyoutContainer.childCount).isEqualTo(1)
+ val flyout = flyoutContainer.getChildAt(0)
+ val sender = flyout.findViewById<TextView>(R.id.bubble_flyout_name)
+ assertThat(sender.text).isEqualTo("sender name")
+ val message = flyout.findViewById<TextView>(R.id.bubble_flyout_text)
+ assertThat(message.text).isEqualTo("message")
+ }
+
+ @Test
+ fun hideFlyout_removedFromContainer() {
+ flyoutController.setUpFlyout(flyoutMessage)
+ assertThat(flyoutContainer.childCount).isEqualTo(1)
+ flyoutController.hideFlyout()
+ assertThat(flyoutContainer.childCount).isEqualTo(0)
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt
index 1d13956..d4a3b3a 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt
@@ -32,6 +32,7 @@
import com.android.launcher3.taskbar.bubbles.BubbleBarView
import com.android.launcher3.taskbar.bubbles.BubbleBarViewController
import com.android.launcher3.taskbar.bubbles.BubbleStashedHandleViewController
+import com.android.launcher3.taskbar.bubbles.BubbleView
import com.android.launcher3.util.MultiValueAlpha
import com.android.wm.shell.shared.animation.PhysicsAnimator
import com.android.wm.shell.shared.animation.PhysicsAnimatorTestUtils
@@ -55,8 +56,8 @@
companion object {
const val TASKBAR_BOTTOM_SPACE = 5
- const val BUBBLE_BAR_WIDTH = 200f
- const val BUBBLE_BAR_HEIGHT = 100f
+ const val BUBBLE_BAR_WIDTH = 200
+ const val BUBBLE_BAR_HEIGHT = 100
const val HOTSEAT_TRANSLATION_Y = -45f
const val TASK_BAR_TRANSLATION_Y = -TASKBAR_BOTTOM_SPACE
const val HANDLE_VIEW_WIDTH = 150
@@ -77,10 +78,12 @@
private val context = ApplicationProvider.getApplicationContext<Context>()
private lateinit var bubbleBarView: BubbleBarView
private lateinit var stashedHandleView: StashedHandleView
+ private lateinit var bubbleView: BubbleView
private lateinit var barTranslationY: AnimatedFloat
private lateinit var barScaleX: AnimatedFloat
private lateinit var barScaleY: AnimatedFloat
private lateinit var barAlpha: MultiValueAlpha
+ private lateinit var bubbleOffsetY: AnimatedFloat
private lateinit var bubbleAlpha: AnimatedFloat
private lateinit var backgroundAlpha: AnimatedFloat
private lateinit var stashedHandleAlpha: MultiValueAlpha
@@ -105,7 +108,7 @@
taskbarInsetsController,
bubbleBarViewController,
bubbleStashedHandleViewController,
- ImmediateAction()
+ ImmediateAction(),
)
}
@@ -161,11 +164,13 @@
mTransientBubbleStashController.isStashed = false
whenever(bubbleBarViewController.isHiddenForNoBubbles).thenReturn(false)
+ val bubbleInitialTranslation = bubbleView.translationY
+
// When stash
getInstrumentation().runOnMainSync {
mTransientBubbleStashController.updateStashedAndExpandedState(
stash = true,
- expand = false
+ expand = false,
)
}
@@ -181,9 +186,13 @@
assertThat(bubbleBarView.alpha).isEqualTo(0f)
assertThat(bubbleBarView.scaleX).isEqualTo(mTransientBubbleStashController.getStashScaleX())
assertThat(bubbleBarView.scaleY).isEqualTo(mTransientBubbleStashController.getStashScaleY())
+ assertThat(bubbleBarView.background.alpha).isEqualTo(255)
// Handle view is visible
assertThat(stashedHandleView.translationY).isEqualTo(0)
assertThat(stashedHandleView.alpha).isEqualTo(1)
+ // Bubble view is reset
+ assertThat(bubbleView.translationY).isEqualTo(bubbleInitialTranslation)
+ assertThat(bubbleView.alpha).isEqualTo(1f)
}
@Test
@@ -274,7 +283,7 @@
val height = mTransientBubbleStashController.getTouchableHeight()
// Then bubble bar height is returned
- assertThat(height).isEqualTo(BUBBLE_BAR_HEIGHT.toInt())
+ assertThat(height).isEqualTo(BUBBLE_BAR_HEIGHT)
}
private fun advanceTimeBy(advanceMs: Long) {
@@ -285,20 +294,26 @@
private fun setUpBubbleBarView() {
getInstrumentation().runOnMainSync {
bubbleBarView = BubbleBarView(context)
- bubbleBarView.layoutParams = FrameLayout.LayoutParams(0, 0)
+ bubbleBarView.layoutParams =
+ FrameLayout.LayoutParams(BUBBLE_BAR_WIDTH, BUBBLE_BAR_HEIGHT)
+ bubbleView = BubbleView(context)
+ bubbleBarView.addBubble(bubbleView)
+ bubbleBarView.layout(0, 0, BUBBLE_BAR_WIDTH, BUBBLE_BAR_HEIGHT)
}
}
private fun setUpStashedHandleView() {
getInstrumentation().runOnMainSync {
stashedHandleView = StashedHandleView(context)
- stashedHandleView.layoutParams = FrameLayout.LayoutParams(0, 0)
+ stashedHandleView.layoutParams =
+ FrameLayout.LayoutParams(HANDLE_VIEW_WIDTH, HANDLE_VIEW_HEIGHT)
}
}
private fun setUpBubbleBarController() {
barTranslationY =
AnimatedFloat(Runnable { bubbleBarView.translationY = barTranslationY.value })
+ bubbleOffsetY = AnimatedFloat { value -> bubbleBarView.setBubbleOffsetY(value) }
barScaleX = AnimatedFloat { value -> bubbleBarView.scaleX = value }
barScaleY = AnimatedFloat { value -> bubbleBarView.scaleY = value }
barAlpha = MultiValueAlpha(bubbleBarView, 1 /* num alpha channels */)
@@ -307,13 +322,16 @@
whenever(bubbleBarViewController.hasBubbles()).thenReturn(true)
whenever(bubbleBarViewController.bubbleBarTranslationY).thenReturn(barTranslationY)
+ whenever(bubbleBarViewController.bubbleOffsetY).thenReturn(bubbleOffsetY)
whenever(bubbleBarViewController.bubbleBarBackgroundScaleX).thenReturn(barScaleX)
whenever(bubbleBarViewController.bubbleBarBackgroundScaleY).thenReturn(barScaleY)
whenever(bubbleBarViewController.bubbleBarAlpha).thenReturn(barAlpha)
whenever(bubbleBarViewController.bubbleBarBubbleAlpha).thenReturn(bubbleAlpha)
whenever(bubbleBarViewController.bubbleBarBackgroundAlpha).thenReturn(backgroundAlpha)
- whenever(bubbleBarViewController.bubbleBarCollapsedWidth).thenReturn(BUBBLE_BAR_WIDTH)
- whenever(bubbleBarViewController.bubbleBarCollapsedHeight).thenReturn(BUBBLE_BAR_HEIGHT)
+ whenever(bubbleBarViewController.bubbleBarCollapsedWidth)
+ .thenReturn(BUBBLE_BAR_WIDTH.toFloat())
+ whenever(bubbleBarViewController.bubbleBarCollapsedHeight)
+ .thenReturn(BUBBLE_BAR_HEIGHT.toFloat())
whenever(bubbleBarViewController.createRevealAnimatorForStashChange(any()))
.thenReturn(AnimatorSet())
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java
index 2a0aa4c..87a7cda 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java
@@ -41,11 +41,14 @@
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Flags;
import com.android.launcher3.LauncherRootView;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.statemanager.StatefulContainer;
import com.android.launcher3.util.SystemUiController;
+import com.android.quickstep.fallback.window.RecentsWindowManager;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.RecentsViewContainer;
@@ -62,16 +65,14 @@
import java.util.HashMap;
public abstract class AbsSwipeUpHandlerTestCase<
- RECENTS_CONTAINER extends Context & RecentsViewContainer,
- STATE extends BaseState<STATE>,
- RECENTS_VIEW extends RecentsView<RECENTS_CONTAINER, STATE>,
+ RECENTS_CONTAINER extends Context & RecentsViewContainer & StatefulContainer<STATE>,
+ STATE extends BaseState<STATE>, RECENTS_VIEW extends RecentsView<RECENTS_CONTAINER, STATE>,
ACTIVITY_TYPE extends StatefulActivity<STATE> & RecentsViewContainer,
ACTIVITY_INTERFACE extends BaseActivityInterface<STATE, ACTIVITY_TYPE>,
SWIPE_HANDLER extends AbsSwipeUpHandler<RECENTS_CONTAINER, RECENTS_VIEW, STATE>> {
protected final Context mContext =
InstrumentationRegistry.getInstrumentation().getTargetContext();
- protected final TaskAnimationManager mTaskAnimationManager = new TaskAnimationManager(mContext);
protected final RecentsAnimationDeviceState mRecentsAnimationDeviceState =
new RecentsAnimationDeviceState(mContext, true);
protected final InputConsumerController mInputConsumerController =
@@ -105,6 +106,9 @@
/* minimizedHomeBounds= */ null,
new Bundle());
+ protected RecentsWindowManager mRecentsWindowManager;
+ protected TaskAnimationManager mTaskAnimationManager;
+
@Mock protected ACTIVITY_INTERFACE mActivityInterface;
@Mock protected ActivityInitListener<?> mActivityInitListener;
@Mock protected RecentsAnimationController mRecentsAnimationController;
@@ -119,6 +123,16 @@
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@Before
+ public void setUpTaskAnimationManager() {
+ runOnMainSync(() -> {
+ if(Flags.enableFallbackOverviewInWindow()){
+ mRecentsWindowManager = new RecentsWindowManager(mContext);
+ }
+ mTaskAnimationManager = new TaskAnimationManager(mContext, mRecentsWindowManager);
+ });
+ }
+
+ @Before
public void setUpRunningTaskInfo() {
mRunningTaskInfo.baseIntent = new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_HOME)
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/FallbackSwipeHandlerTestCase.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/FallbackSwipeHandlerTestCase.java
index dd0b4b3..8d6906f 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/FallbackSwipeHandlerTestCase.java
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/FallbackSwipeHandlerTestCase.java
@@ -30,7 +30,7 @@
public class FallbackSwipeHandlerTestCase extends AbsSwipeUpHandlerTestCase<
RecentsActivity,
RecentsState,
- FallbackRecentsView,
+ FallbackRecentsView<RecentsActivity>,
RecentsActivity,
FallbackActivityInterface,
FallbackSwipeHandler> {
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/logging/SettingsChangeLoggerTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/logging/SettingsChangeLoggerTest.kt
index 7c48ea4..0a60774 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/logging/SettingsChangeLoggerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/logging/SettingsChangeLoggerTest.kt
@@ -34,6 +34,7 @@
import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NOTIFICATION_DOT_ENABLED
import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_THEMED_ICON_DISABLED
import com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY
+import com.android.launcher3.util.DaggerSingletonTracker
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
@@ -62,6 +63,7 @@
@Mock private lateinit var mMockLogger: StatsLogManager.StatsLogger
@Captor private lateinit var mEventCaptor: ArgumentCaptor<StatsLogManager.EventEnum>
+ @Mock private lateinit var mTracker: DaggerSingletonTracker
private var mDefaultThemedIcons = false
private var mDefaultAllowRotation = false
@@ -79,7 +81,7 @@
// To match the default value of ALLOW_ROTATION
LauncherPrefs.get(mContext).put(item = ALLOW_ROTATION, value = false)
- mSystemUnderTest = SettingsChangeLogger(mContext, mStatsLogManager)
+ mSystemUnderTest = SettingsChangeLogger(mContext, mStatsLogManager, mTracker)
}
@After
@@ -90,7 +92,7 @@
@Test
fun loggingPrefs_correctDefaultValue() {
- val systemUnderTest = SettingsChangeLogger(mContext, mStatsLogManager)
+ val systemUnderTest = SettingsChangeLogger(mContext, mStatsLogManager, mTracker)
assertThat(systemUnderTest.loggingPrefs[ALLOW_ROTATION_PREFERENCE_KEY]!!.defaultValue)
.isFalse()
@@ -117,7 +119,7 @@
LauncherPrefs.get(mContext).put(item = ALLOW_ROTATION, value = true)
// This a new object so the values of mLoggablePrefs will be different
- SettingsChangeLogger(mContext, mStatsLogManager).logSnapshot(mInstanceId)
+ SettingsChangeLogger(mContext, mStatsLogManager, mTracker).logSnapshot(mInstanceId)
verify(mMockLogger, atLeastOnce()).log(mEventCaptor.capture())
val capturedEvents = mEventCaptor.allValues
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt
index f31467f..a87465f 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt
@@ -20,6 +20,7 @@
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
+import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.launcher3.util.TestDispatcherProvider
import com.android.quickstep.task.thumbnail.TaskThumbnailViewModelTest
import com.android.quickstep.util.DesktopTask
@@ -36,17 +37,19 @@
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
class TasksRepositoryTest {
private val tasks = (0..5).map(::createTaskWithId)
private val defaultTaskList =
listOf(
GroupTask(tasks[0]),
GroupTask(tasks[1], tasks[2], null),
- DesktopTask(tasks.subList(3, 6))
+ DesktopTask(tasks.subList(3, 6)),
)
private val recentsModel = FakeRecentTasksDataSource()
private val taskThumbnailDataSource = FakeTaskThumbnailDataSource()
@@ -65,7 +68,7 @@
taskIconDataSource,
taskVisualsChangedDelegate,
testScope.backgroundScope,
- TestDispatcherProvider(dispatcher)
+ TestDispatcherProvider(dispatcher),
)
@Test
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/viewmodel/RecentsViewModelTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/viewmodel/RecentsViewModelTest.kt
index fe67313..33d96a8 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/viewmodel/RecentsViewModelTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/viewmodel/RecentsViewModelTest.kt
@@ -70,6 +70,55 @@
assertThat(thumbnailDataFlow2.first()).isNull()
}
+ @Test
+ fun updatesRunningTaskShowScreenshot() = runTest {
+ systemUnderTest.setRunningTaskShowScreenshot(true)
+ systemUnderTest.waitForRunningTaskShowScreenshotToUpdate()
+ }
+
+ @Test
+ fun waitForThumbnailsToUpdate() = runTest {
+ // Given taskRepository with visible 2 tasks containing thumbnailData
+ val thumbnailData1 = createThumbnailData().apply { snapshotId = 1 }
+ val thumbnailData2 = createThumbnailData().apply { snapshotId = 2 }
+ tasksRepository.seedTasks(tasks)
+ tasksRepository.seedThumbnailData(mapOf(1 to thumbnailData1, 2 to thumbnailData2))
+ systemUnderTest.updateVisibleTasks(listOf(1, 2))
+
+ val thumbnailDataFlow1 = tasksRepository.getThumbnailById(1)
+ val thumbnailDataFlow2 = tasksRepository.getThumbnailById(2)
+
+ // Then getThumbnailById should initially contains correct thumbnailData
+ assertThat(thumbnailDataFlow1.first()).isEqualTo(thumbnailData1)
+ assertThat(thumbnailDataFlow2.first()).isEqualTo(thumbnailData2)
+
+ // When thumbnailData is updated in taskRepository
+ tasksRepository.seedThumbnailData(
+ mapOf(1 to thumbnailData1, 2 to createThumbnailData().apply { snapshotId = 3 })
+ )
+ // setVisibleTasks forces FakeTasksRepository to update the flows returned by
+ // getThumbnailById
+ tasksRepository.setVisibleTasks(listOf(1, 2))
+
+ // Then wait for thumbnailData should complete, and the previous getThumbnailById flow
+ // should return updated values
+ systemUnderTest.waitForThumbnailsToUpdate(
+ mapOf(2 to createThumbnailData().apply { snapshotId = 3 })
+ )
+ assertThat(thumbnailDataFlow1.first()).isEqualTo(thumbnailData1)
+ assertThat(thumbnailDataFlow2.first()?.snapshotId).isEqualTo(3)
+ }
+
+ @Test
+ fun waitForThumbnailsToUpdate_emptyMap() = runTest {
+ systemUnderTest.waitForThumbnailsToUpdate(emptyMap())
+ }
+
+ @Test
+ fun waitForThumbnailsToUpdate_null() = runTest {
+ systemUnderTest.waitForThumbnailsToUpdate(null)
+ }
+
private fun createTaskWithId(taskId: Int) =
Task(Task.TaskKey(taskId, 0, Intent(), ComponentName("", ""), 0, 2000)).apply {
colorBackground = Color.argb(taskId, taskId, taskId, taskId)
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
index fc4c4f6..936e996 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
@@ -22,7 +22,6 @@
import android.content.ComponentName
import android.content.Intent
import android.graphics.Rect
-import android.os.Handler
import android.os.UserHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.launcher3.LauncherState
@@ -40,7 +39,6 @@
import com.android.quickstep.views.RecentsViewContainer
import com.android.systemui.shared.recents.model.Task
import com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_50_50
-import java.util.function.Consumer
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNull
@@ -54,6 +52,7 @@
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
+import java.util.function.Consumer
@RunWith(AndroidJUnit4::class)
class SplitSelectStateControllerTest {
@@ -63,7 +62,6 @@
private val statsLogManager: StatsLogManager = mock()
private val statsLogger: StatsLogger = mock()
private val stateManager: StateManager<LauncherState, StatefulActivity<LauncherState>> = mock()
- private val handler: Handler = mock()
private val context: RecentsViewContainer = mock()
private val recentsModel: RecentsModel = mock()
private val pendingIntent: PendingIntent = mock()
@@ -87,7 +85,6 @@
splitSelectStateController =
SplitSelectStateController(
context,
- handler,
stateManager,
depthController,
statsLogManager,
diff --git a/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt b/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt
index 885a7f6..231c113 100644
--- a/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt
@@ -24,6 +24,7 @@
import com.android.dx.mockito.inline.extended.StaticMockitoSession
import com.android.launcher3.AbstractFloatingView
import com.android.launcher3.AbstractFloatingViewHelper
+import com.android.launcher3.Flags.enableRefactorTaskThumbnail
import com.android.launcher3.logging.StatsLogManager
import com.android.launcher3.logging.StatsLogManager.LauncherEvent
import com.android.launcher3.model.data.WorkspaceItemInfo
@@ -31,6 +32,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.TaskThumbnailView
import com.android.quickstep.views.LauncherRecentsView
import com.android.quickstep.views.TaskContainer
import com.android.quickstep.views.TaskThumbnailViewDeprecated
@@ -67,7 +69,6 @@
private val taskView: TaskView = mock()
private val workspaceItemInfo: WorkspaceItemInfo = mock()
private val abstractFloatingViewHelper: AbstractFloatingViewHelper = mock()
- private val thumbnailViewDeprecated: TaskThumbnailViewDeprecated = mock()
private val iconView: TaskViewIcon = mock()
private val transformingTouchDelegate: TransformingTouchDelegate = mock()
private val factory: TaskShortcutFactory =
@@ -175,7 +176,7 @@
.moveTaskToDesktop(
eq(taskContainer),
eq(DesktopModeTransitionSource.APP_FROM_OVERVIEW),
- any()
+ any(),
)
verify(statsLogger).withItemInfo(workspaceItemInfo)
verify(statsLogger).log(LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_DESKTOP_TAP)
@@ -188,16 +189,19 @@
}
private fun createTaskContainer(task: Task): TaskContainer {
+ val snapshotView =
+ if (enableRefactorTaskThumbnail()) mock<TaskThumbnailView>()
+ else mock<TaskThumbnailViewDeprecated>()
return TaskContainer(
taskView,
task,
- thumbnailViewDeprecated,
+ snapshotView,
iconView,
transformingTouchDelegate,
SplitConfigurationOptions.STAGE_POSITION_UNDEFINED,
digitalWellBeingToast = null,
showWindowsView = null,
- overlayFactory
+ overlayFactory,
)
}
}
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index 2e456a7..2858929 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -248,7 +248,6 @@
}
@Test
- @ScreenRecordRule.ScreenRecord // b/355042336
public void testOverview() throws IOException {
startAppFast(getAppPackageName());
startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
diff --git a/quickstep/tests/src/com/android/quickstep/TaplPrivateSpaceTest.java b/quickstep/tests/src/com/android/quickstep/TaplPrivateSpaceTest.java
index 23a29f7..800fd4a 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplPrivateSpaceTest.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplPrivateSpaceTest.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeFalse;
import android.util.Log;
@@ -117,7 +118,6 @@
}
@Test
- @ScreenRecordRule.ScreenRecord // b/334946529
public void testUserInstalledAppIsShownAboveDivider() throws IOException {
// Ensure that the App is not installed in main user otherwise, it may not be found in
// PS container.
@@ -142,7 +142,6 @@
}
@Test
- @ScreenRecordRule.ScreenRecord // b/334946529
public void testPrivateSpaceAppLongPressUninstallMenu() throws IOException {
// Ensure that the App is not installed in main user otherwise, it may not be found in
// PS container.
@@ -166,8 +165,9 @@
}
@Test
- @ScreenRecordRule.ScreenRecord // b/334946529
+ @ScreenRecordRule.ScreenRecord // b/355466672
public void testPrivateSpaceLockingBehaviour() throws IOException {
+ assumeFalse(mLauncher.isTablet()); // b/367258373
// Scroll to the bottom of All Apps
executeOnLauncher(launcher -> launcher.getAppsView().resetAndScrollToPrivateSpaceHeader());
HomeAllApps homeAllApps = mLauncher.getAllApps();
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 3e6436b..5ff2af7 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -350,7 +350,6 @@
@Test
@TaskbarModeSwitch
- @ScreenRecord // b/358607191
public void testQuickSwitchToPreviousAppForTablet() throws Exception {
assumeTrue(mLauncher.isTablet());
startTestActivity(2);
@@ -396,7 +395,6 @@
@Test
@NavigationModeSwitch
@PortraitLandscape
- @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/325659406
public void testQuickSwitchFromHome() throws Exception {
startTestActivity(2);
mLauncher.goHome().quickSwitchToPreviousApp();
@@ -578,7 +576,7 @@
public void testExcludeFromRecents() throws Exception {
startExcludeFromRecentsTestActivity();
OverviewTask currentTask = getAndAssertLaunchedApp().switchToOverview().getCurrentTask();
- // TODO(b/326565120): the expected content description shouldn't be null but for now there
+ // TODO(b/342627272): the expected content description shouldn't be null but for now there
// is a bug that causes it to sometimes be for excludeForRecents tasks.
assertTrue("Can't find ExcludeFromRecentsTestActivity after entering Overview from it",
currentTask.containsContentDescription("ExcludeFromRecents")
diff --git a/quickstep/tests/src/com/android/quickstep/TaskAnimationManagerTest.java b/quickstep/tests/src/com/android/quickstep/TaskAnimationManagerTest.java
index 28c8a4a..ec07b93 100644
--- a/quickstep/tests/src/com/android/quickstep/TaskAnimationManagerTest.java
+++ b/quickstep/tests/src/com/android/quickstep/TaskAnimationManagerTest.java
@@ -29,6 +29,8 @@
import androidx.test.filters.SmallTest;
+import com.android.quickstep.fallback.window.RecentsWindowManager;
+
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
@@ -42,6 +44,9 @@
private Context mContext;
@Mock
+ private RecentsWindowManager mRecentsWindowManager;
+
+ @Mock
private SystemUiProxy mSystemUiProxy;
private TaskAnimationManager mTaskAnimationManager;
@@ -49,7 +54,7 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mTaskAnimationManager = new TaskAnimationManager(mContext) {
+ mTaskAnimationManager = new TaskAnimationManager(mContext, mRecentsWindowManager) {
@Override
SystemUiProxy getSystemUiProxy() {
return mSystemUiProxy;
diff --git a/res/drawable-sw720dp/ic_transient_taskbar_all_apps_button.xml b/res/drawable-sw720dp/ic_transient_taskbar_all_apps_button.xml
deleted file mode 100644
index 47f2a5d..0000000
--- a/res/drawable-sw720dp/ic_transient_taskbar_all_apps_button.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="52dp"
- android:height="52dp"
- android:viewportWidth="52"
- android:viewportHeight="52">
- <path
- android:pathData="M15.5,19C14.538,19 13.715,18.65 13.033,17.968C12.35,17.285 12,16.462 12,15.5C12,14.538 12.35,13.715 13.033,13.033C13.715,12.35 14.538,12 15.5,12C16.462,12 17.285,12.35 17.968,13.033C18.65,13.715 19,14.538 19,15.5C19,16.462 18.65,17.285 17.968,17.968C17.285,18.65 16.462,19 15.5,19Z"
- android:fillColor="#40484B"/>
- <path
- android:pathData="M26,19C25.038,19 24.215,18.65 23.532,17.968C22.85,17.285 22.5,16.462 22.5,15.5C22.5,14.538 22.85,13.715 23.532,13.033C24.215,12.35 25.038,12 26,12C26.962,12 27.785,12.35 28.468,13.033C29.15,13.715 29.5,14.538 29.5,15.5C29.5,16.462 29.15,17.285 28.468,17.968C27.785,18.65 26.962,19 26,19Z"
- android:fillColor="#40484B"/>
- <path
- android:pathData="M36.5,19C35.537,19 34.715,18.65 34.033,17.968C33.35,17.285 33,16.462 33,15.5C33,14.538 33.35,13.715 34.033,13.033C34.715,12.35 35.537,12 36.5,12C37.463,12 38.285,12.35 38.967,13.033C39.65,13.715 40,14.538 40,15.5C40,16.462 39.65,17.285 38.967,17.968C38.285,18.65 37.463,19 36.5,19Z"
- android:fillColor="#40484B"/>
- <path
- android:pathData="M15.5,29.5C14.538,29.5 13.715,29.15 13.033,28.468C12.35,27.785 12,26.962 12,26C12,25.038 12.35,24.215 13.033,23.532C13.715,22.85 14.538,22.5 15.5,22.5C16.462,22.5 17.285,22.85 17.968,23.532C18.65,24.215 19,25.038 19,26C19,26.962 18.65,27.785 17.968,28.468C17.285,29.15 16.462,29.5 15.5,29.5Z"
- android:fillColor="#40484B"/>
- <path
- android:pathData="M26,29.5C25.038,29.5 24.215,29.15 23.532,28.468C22.85,27.785 22.5,26.962 22.5,26C22.5,25.038 22.85,24.215 23.532,23.532C24.215,22.85 25.038,22.5 26,22.5C26.962,22.5 27.785,22.85 28.468,23.532C29.15,24.215 29.5,25.038 29.5,26C29.5,26.962 29.15,27.785 28.468,28.468C27.785,29.15 26.962,29.5 26,29.5Z"
- android:fillColor="#40484B"/>
- <path
- android:pathData="M36.5,29.5C35.537,29.5 34.715,29.15 34.033,28.468C33.35,27.785 33,26.962 33,26C33,25.038 33.35,24.215 34.033,23.532C34.715,22.85 35.537,22.5 36.5,22.5C37.463,22.5 38.285,22.85 38.967,23.532C39.65,24.215 40,25.038 40,26C40,26.962 39.65,27.785 38.967,28.468C38.285,29.15 37.463,29.5 36.5,29.5Z"
- android:fillColor="#40484B"/>
- <path
- android:pathData="M15.5,40C14.538,40 13.715,39.65 13.033,38.967C12.35,38.285 12,37.463 12,36.5C12,35.537 12.35,34.715 13.033,34.033C13.715,33.35 14.538,33 15.5,33C16.462,33 17.285,33.35 17.968,34.033C18.65,34.715 19,35.537 19,36.5C19,37.463 18.65,38.285 17.968,38.967C17.285,39.65 16.462,40 15.5,40Z"
- android:fillColor="#40484B"/>
- <path
- android:pathData="M26,40C25.038,40 24.215,39.65 23.532,38.967C22.85,38.285 22.5,37.463 22.5,36.5C22.5,35.537 22.85,34.715 23.532,34.033C24.215,33.35 25.038,33 26,33C26.962,33 27.785,33.35 28.468,34.033C29.15,34.715 29.5,35.537 29.5,36.5C29.5,37.463 29.15,38.285 28.468,38.967C27.785,39.65 26.962,40 26,40Z"
- android:fillColor="#40484B"/>
- <path
- android:pathData="M36.5,40C35.537,40 34.715,39.65 34.033,38.967C33.35,38.285 33,37.463 33,36.5C33,35.537 33.35,34.715 34.033,34.033C34.715,33.35 35.537,33 36.5,33C37.463,33 38.285,33.35 38.967,34.033C39.65,34.715 40,35.537 40,36.5C40,37.463 39.65,38.285 38.967,38.967C38.285,39.65 37.463,40 36.5,40Z"
- android:fillColor="#40484B"/>
-</vector>
diff --git a/res/drawable/desktop_mode_ic_taskbar_menu_new_window.xml b/res/drawable/desktop_mode_ic_taskbar_menu_new_window.xml
new file mode 100644
index 0000000..b96a596
--- /dev/null
+++ b/res/drawable/desktop_mode_ic_taskbar_menu_new_window.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="20dp"
+ android:viewportWidth="20"
+ android:viewportHeight="20">
+ <path
+ android:pathData="M15 16V14H13V12.5H15V10.5H16.5V12.5H18.5V14H16.5V16H15ZM3.5 17C3.09722 17 2.74306 16.8542 2.4375 16.5625C2.14583 16.2569 2 15.9028 2 15.5V4.5C2 4.08333 2.14583 3.72917 2.4375 3.4375C2.74306 3.14583 3.09722 3 3.5 3H14.5C14.9167 3 15.2708 3.14583 15.5625 3.4375C15.8542 3.72917 16 4.08333 16 4.5V9H14.5V7H3.5V15.5H13.625V17H3.5ZM3.5 5.5H14.5V4.5H3.5V5.5ZM3.5 5.5V4.5V5.5Z"
+ android:fillColor="#1C1C14"/>
+</vector>
diff --git a/res/drawable/ic_taskbar_all_apps_button.xml b/res/drawable/ic_taskbar_all_apps_button.xml
deleted file mode 100644
index 82fbbea..0000000
--- a/res/drawable/ic_taskbar_all_apps_button.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="44dp"
- android:height="44dp"
- android:viewportWidth="44"
- android:viewportHeight="44">
- <path
- android:pathData="M13,16C12.175,16 11.47,15.7 10.885,15.115C10.3,14.53 10,13.825 10,13C10,12.175 10.3,11.47 10.885,10.885C11.47,10.3 12.175,10 13,10C13.825,10 14.53,10.3 15.115,10.885C15.7,11.47 16,12.175 16,13C16,13.825 15.7,14.53 15.115,15.115C14.53,15.7 13.825,16 13,16Z"
- android:fillColor="#40484B"/>
- <path
- android:pathData="M22,16C21.175,16 20.47,15.7 19.885,15.115C19.3,14.53 19,13.825 19,13C19,12.175 19.3,11.47 19.885,10.885C20.47,10.3 21.175,10 22,10C22.825,10 23.53,10.3 24.115,10.885C24.7,11.47 25,12.175 25,13C25,13.825 24.7,14.53 24.115,15.115C23.53,15.7 22.825,16 22,16Z"
- android:fillColor="#40484B"/>
- <path
- android:pathData="M31,16C30.175,16 29.47,15.7 28.885,15.115C28.3,14.53 28,13.825 28,13C28,12.175 28.3,11.47 28.885,10.885C29.47,10.3 30.175,10 31,10C31.825,10 32.53,10.3 33.115,10.885C33.7,11.47 34,12.175 34,13C34,13.825 33.7,14.53 33.115,15.115C32.53,15.7 31.825,16 31,16Z"
- android:fillColor="#40484B"/>
- <path
- android:pathData="M13,25C12.175,25 11.47,24.7 10.885,24.115C10.3,23.53 10,22.825 10,22C10,21.175 10.3,20.47 10.885,19.885C11.47,19.3 12.175,19 13,19C13.825,19 14.53,19.3 15.115,19.885C15.7,20.47 16,21.175 16,22C16,22.825 15.7,23.53 15.115,24.115C14.53,24.7 13.825,25 13,25Z"
- android:fillColor="#40484B"/>
- <path
- android:pathData="M22,25C21.175,25 20.47,24.7 19.885,24.115C19.3,23.53 19,22.825 19,22C19,21.175 19.3,20.47 19.885,19.885C20.47,19.3 21.175,19 22,19C22.825,19 23.53,19.3 24.115,19.885C24.7,20.47 25,21.175 25,22C25,22.825 24.7,23.53 24.115,24.115C23.53,24.7 22.825,25 22,25Z"
- android:fillColor="#40484B"/>
- <path
- android:pathData="M31,25C30.175,25 29.47,24.7 28.885,24.115C28.3,23.53 28,22.825 28,22C28,21.175 28.3,20.47 28.885,19.885C29.47,19.3 30.175,19 31,19C31.825,19 32.53,19.3 33.115,19.885C33.7,20.47 34,21.175 34,22C34,22.825 33.7,23.53 33.115,24.115C32.53,24.7 31.825,25 31,25Z"
- android:fillColor="#40484B"/>
- <path
- android:pathData="M13,34C12.175,34 11.47,33.7 10.885,33.115C10.3,32.53 10,31.825 10,31C10,30.175 10.3,29.47 10.885,28.885C11.47,28.3 12.175,28 13,28C13.825,28 14.53,28.3 15.115,28.885C15.7,29.47 16,30.175 16,31C16,31.825 15.7,32.53 15.115,33.115C14.53,33.7 13.825,34 13,34Z"
- android:fillColor="#40484B"/>
- <path
- android:pathData="M22,34C21.175,34 20.47,33.7 19.885,33.115C19.3,32.53 19,31.825 19,31C19,30.175 19.3,29.47 19.885,28.885C20.47,28.3 21.175,28 22,28C22.825,28 23.53,28.3 24.115,28.885C24.7,29.47 25,30.175 25,31C25,31.825 24.7,32.53 24.115,33.115C23.53,33.7 22.825,34 22,34Z"
- android:fillColor="#40484B"/>
- <path
- android:pathData="M31,34C30.175,34 29.47,33.7 28.885,33.115C28.3,32.53 28,31.825 28,31C28,30.175 28.3,29.47 28.885,28.885C29.47,28.3 30.175,28 31,28C31.825,28 32.53,28.3 33.115,28.885C33.7,29.47 34,30.175 34,31C34,31.825 33.7,32.53 33.115,33.115C32.53,33.7 31.825,34 31,34Z"
- android:fillColor="#40484B"/>
-</vector>
diff --git a/res/drawable/ic_transient_taskbar_all_apps_button.xml b/res/drawable/ic_transient_taskbar_all_apps_button.xml
deleted file mode 100644
index 6e740ae..0000000
--- a/res/drawable/ic_transient_taskbar_all_apps_button.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48"
- android:viewportHeight="48">
- <path
- android:pathData="M13.5,17C12.538,17 11.715,16.65 11.033,15.967C10.35,15.285 10,14.462 10,13.5C10,12.538 10.35,11.715 11.033,11.033C11.715,10.35 12.538,10 13.5,10C14.462,10 15.285,10.35 15.967,11.033C16.65,11.715 17,12.538 17,13.5C17,14.462 16.65,15.285 15.967,15.967C15.285,16.65 14.462,17 13.5,17Z"
- android:fillColor="#40484B"/>
- <path
- android:pathData="M24,17C23.038,17 22.215,16.65 21.532,15.967C20.85,15.285 20.5,14.462 20.5,13.5C20.5,12.538 20.85,11.715 21.532,11.033C22.215,10.35 23.038,10 24,10C24.962,10 25.785,10.35 26.468,11.033C27.15,11.715 27.5,12.538 27.5,13.5C27.5,14.462 27.15,15.285 26.468,15.967C25.785,16.65 24.962,17 24,17Z"
- android:fillColor="#40484B"/>
- <path
- android:pathData="M34.5,17C33.537,17 32.715,16.65 32.033,15.967C31.35,15.285 31,14.462 31,13.5C31,12.538 31.35,11.715 32.033,11.033C32.715,10.35 33.537,10 34.5,10C35.463,10 36.285,10.35 36.967,11.033C37.65,11.715 38,12.538 38,13.5C38,14.462 37.65,15.285 36.967,15.967C36.285,16.65 35.463,17 34.5,17Z"
- android:fillColor="#40484B"/>
- <path
- android:pathData="M13.5,27.5C12.538,27.5 11.715,27.15 11.033,26.468C10.35,25.785 10,24.962 10,24C10,23.038 10.35,22.215 11.033,21.532C11.715,20.85 12.538,20.5 13.5,20.5C14.462,20.5 15.285,20.85 15.967,21.532C16.65,22.215 17,23.038 17,24C17,24.962 16.65,25.785 15.967,26.468C15.285,27.15 14.462,27.5 13.5,27.5Z"
- android:fillColor="#40484B"/>
- <path
- android:pathData="M24,27.5C23.038,27.5 22.215,27.15 21.532,26.468C20.85,25.785 20.5,24.962 20.5,24C20.5,23.038 20.85,22.215 21.532,21.532C22.215,20.85 23.038,20.5 24,20.5C24.962,20.5 25.785,20.85 26.468,21.532C27.15,22.215 27.5,23.038 27.5,24C27.5,24.962 27.15,25.785 26.468,26.468C25.785,27.15 24.962,27.5 24,27.5Z"
- android:fillColor="#40484B"/>
- <path
- android:pathData="M34.5,27.5C33.537,27.5 32.715,27.15 32.033,26.468C31.35,25.785 31,24.962 31,24C31,23.038 31.35,22.215 32.033,21.532C32.715,20.85 33.537,20.5 34.5,20.5C35.463,20.5 36.285,20.85 36.967,21.532C37.65,22.215 38,23.038 38,24C38,24.962 37.65,25.785 36.967,26.468C36.285,27.15 35.463,27.5 34.5,27.5Z"
- android:fillColor="#40484B"/>
- <path
- android:pathData="M13.5,38C12.538,38 11.715,37.65 11.033,36.967C10.35,36.285 10,35.463 10,34.5C10,33.537 10.35,32.715 11.033,32.033C11.715,31.35 12.538,31 13.5,31C14.462,31 15.285,31.35 15.967,32.033C16.65,32.715 17,33.537 17,34.5C17,35.463 16.65,36.285 15.967,36.967C15.285,37.65 14.462,38 13.5,38Z"
- android:fillColor="#40484B"/>
- <path
- android:pathData="M24,38C23.038,38 22.215,37.65 21.532,36.967C20.85,36.285 20.5,35.463 20.5,34.5C20.5,33.537 20.85,32.715 21.532,32.033C22.215,31.35 23.038,31 24,31C24.962,31 25.785,31.35 26.468,32.033C27.15,32.715 27.5,33.537 27.5,34.5C27.5,35.463 27.15,36.285 26.468,36.967C25.785,37.65 24.962,38 24,38Z"
- android:fillColor="#40484B"/>
- <path
- android:pathData="M34.5,38C33.537,38 32.715,37.65 32.033,36.967C31.35,36.285 31,35.463 31,34.5C31,33.537 31.35,32.715 32.033,32.033C32.715,31.35 33.537,31 34.5,31C35.463,31 36.285,31.35 36.967,32.033C37.65,32.715 38,33.537 38,34.5C38,35.463 37.65,36.285 36.967,36.967C36.285,37.65 35.463,38 34.5,38Z"
- android:fillColor="#40484B"/>
-</vector>
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 490a7c2..4f62bda 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Verdeelde skerm"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Programinligting vir %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Gebruikinstellings vir %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Stoor apppaar"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Hierdie apppaar word nie op hierdie toestel gesteun nie"</string>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 7292eec..be91c0b 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"የተከፈለ ማያ ገፅ"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"የመተግበሪያ መረጃ ለ%1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"የ%1$s የአጠቃቀም ቅንብሮች"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"የመተግበሪያ ጥምረትን ያስቀምጡ"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"ይህ የመተግበሪያ ጥምረት በዚህ መሣሪያ ላይ አይደገፍም"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 06fc0a8..04c2f2f 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"تقسيم الشاشة"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"معلومات تطبيق %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"إعدادات استخدام \"%1$s\""</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"حفظ استخدام التطبيقين معًا"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"لا يمكن استخدام هذين التطبيقَين في الوقت نفسه على هذا الجهاز"</string>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index cd6e347..4d377c2 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"বিভাজিত স্ক্ৰীন"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$sৰ বাবে এপৰ তথ্য"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"%1$sৰ বাবে ব্যৱহাৰৰ ছেটিং"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"এপৰ পেয়াৰ ছেভ কৰক"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"এই ডিভাইচটোত এই এপ্ পেয়াৰ কৰাৰ সুবিধাটো সমৰ্থিত নহয়"</string>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index b8d660f..4c2ba9b 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Ekran bölünməsi"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s ilə bağlı tətbiq məlumatı"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"%1$s üzrə istifadə ayarları"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Tətbiq cütünü saxlayın"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Bu tətbiq cütü bu cihazda dəstəklənmir"</string>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index 4d4764e..35ba183 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Podeljeni ekran"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Informacije o aplikaciji za: %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Podešavanja potrošnje za %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Sačuvaj par aplikacija"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Ovaj par aplikacija nije podržan na ovom uređaju"</string>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index 641509e..1a2bcc1 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Падзелены экран"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Інфармацыя пра праграму для: %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"%1$s: налады выкарыстання"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Захаваць спалучэнне праграм"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Дадзенае спалучэнне праграм не падтрымліваецца на гэтай прыладзе"</string>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 3ce3c5f..270374d 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Разделен екран"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Информация за приложението за %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Настройки за използването на %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Запазване на двойката приложения"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Тази двойка приложения не се поддържа на устройството"</string>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index 9b23590..5097784 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"স্প্লিট স্ক্রিন"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s-এর জন্য অ্যাপ সম্পর্কিত তথ্য"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"%1$s-এর জন্য ব্যবহারের সেটিংস"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"অ্যাপ পেয়ার সেভ করুন"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"এই ডিভাইসে এই অ্যাপ পেয়ারটি কাজ করে না"</string>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 4a34da7..e4bbd30 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Podijeljeni ekran"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Informacije o aplikaciji %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Postavke korištenja za: %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Sačuvaj par aplikacija"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Par aplikacija nije podržan na uređaju"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index c341ec7..77642f0 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Pantalla dividida"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Informació de l\'aplicació %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Configuració d\'ús de %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Desa la parella d\'aplicacions"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Aquesta parella d\'aplicacions no s\'admet en aquest dispositiu"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index d3512c9..08747e0 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Rozdělit obrazovku"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Informace o aplikaci %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Nastavení využití pro aplikaci %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Uložit dvojici aplikací"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Tento pár aplikací není na tomto zařízení podporován"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 8aae860..0bf8514 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Opdel skærm"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Appinfo for %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Indstillinger for brug af %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Gem appsammenknytning"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Denne appsammenknytning understøttes ikke på enheden"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 374f5a1..c3628ea 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Splitscreen"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"App-Info für %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Nutzungseinstellungen für %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"App-Paar speichern"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Dieses App-Paar wird auf diesem Gerät nicht unterstützt"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index cafe86e..85c6a31 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Διαχωρισμός οθόνης"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Πληροφορίες εφαρμογής για %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Ρυθμίσεις χρήσης για %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Αποθήκευση ζεύγους εφαρμογών"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Αυτό το ζεύγος εφαρμογών δεν υποστηρίζεται σε αυτή τη συσκευή"</string>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index 1b0722d..aee49be 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Split screen"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"App info for %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Usage settings for %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Save app pair"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"This app pair isn\'t supported on this device"</string>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index de41d2c..8feccb0 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -31,6 +31,7 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Split screen"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"App info for %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Usage settings for %1$s"</string>
+ <string name="new_window_option_taskbar" msgid="6448780542727767211">"New Window"</string>
<string name="save_app_pair" msgid="5647523853662686243">"Save app pair"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"This app pair isn\'t supported on this device"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 1b0722d..aee49be 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Split screen"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"App info for %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Usage settings for %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Save app pair"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"This app pair isn\'t supported on this device"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 1b0722d..aee49be 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Split screen"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"App info for %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Usage settings for %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Save app pair"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"This app pair isn\'t supported on this device"</string>
diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml
index a856340..769567c 100644
--- a/res/values-en-rXC/strings.xml
+++ b/res/values-en-rXC/strings.xml
@@ -31,6 +31,7 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Split screen"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"App info for %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Usage settings for %1$s"</string>
+ <string name="new_window_option_taskbar" msgid="6448780542727767211">"New Window"</string>
<string name="save_app_pair" msgid="5647523853662686243">"Save app pair"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"This app pair isn\'t supported on this device"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index ba1b0af..e051843 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Pantalla dividida"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Información de la app de %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Configuración del uso de %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Guardar vinculación"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"No se admite esta vinculación de apps en este dispositivo"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index ad12192..169afe5 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Pantalla dividida"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Información de la aplicación %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Ajustes de uso para %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Guardar apps emparejadas"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"El dispositivo no admite esta aplicación emparejada"</string>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 96d0b2c..844a0c7 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Jagatud ekraanikuva"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Rakenduse teave: %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Kasutuse seaded: %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Salvesta rakendusepaar"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"See rakendusepaar ei ole selles seadmes toetatud"</string>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index bc9b8c1..8c9375a 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Pantaila zatitzea"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s aplikazioari buruzko informazioa"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"%1$s aplikazioaren erabilera-ezarpenak"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Gorde aplikazio parea"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Aplikazio pare hori ez da onartzen gailu honetan"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index c167194..d841770 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"صفحهٔ دونیمه"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"اطلاعات برنامه %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"تنظیمات مصرف برای %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"ذخیره جفت برنامه"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"از این جفت برنامه در این دستگاه پشتیبانی نمیشود"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 007d077..567a1ea 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Jaettu näyttö"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Sovellustiedot: %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Käyttöasetus tälle: %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Tallenna sovelluspari"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Sovellusparia ei tueta tällä laitteella"</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index c443505..a886420 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Écran divisé"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Renseignements sur l\'appli pour %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Paramètres d\'utilisation pour %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Enr. paire d\'applis"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Cette paire d\'applis n\'est pas prise en charge sur cet appareil"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 4f5d111..d96bd98 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Écran partagé"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Infos sur l\'appli pour %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Paramètres d\'utilisation pour %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Enregistrer une paire d\'applis"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Cette paire d\'applications n\'est pas prise en charge sur cet appareil"</string>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index ff7c029..c224a19 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Pantalla dividida"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Información da aplicación para %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Configuración de uso para %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Gardar parella de apps"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"O dispositivo non admite este emparellamento de aplicacións"</string>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index 872faef..732a8e4 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"સ્ક્રીનને વિભાજિત કરો"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s માટે ઍપ માહિતી"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"%1$sના વપરાશ સંબંધિત સેટિંગ"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"ઍપની જોડી સાચવો"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"આ ડિવાઇસ પર, આ ઍપની જોડીને સપોર્ટ આપવામાં આવતો નથી"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index a44b874..d4fb0ca 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"स्प्लिट स्क्रीन"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s के लिए ऐप्लिकेशन की जानकारी"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"%1$s के लिए खर्च की सेटिंग"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"ऐप पेयर सेव करें"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"साथ में इस्तेमाल किए जा सकने वाले ये ऐप्लिकेशन, इस डिवाइस पर काम नहीं कर सकते"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index d9f2072..3d5380a 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Podijeljeni zaslon"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Informacije o aplikaciji %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Postavke upotrebe za %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Spremi par aplikacija"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Taj par aplikacija nije podržan na ovom uređaju"</string>
@@ -189,7 +191,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtrirajte"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Nije uspjelo: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Privatni prostor"</string>
- <string name="private_space_secondary_label" msgid="9203933341714508907">"Dodirnite da biste postavili ili otvorili"</string>
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Dodirnite za postavljanje ili otvaranje"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Privatno"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Postavke privatnog prostora"</string>
<string name="ps_container_unlock_button_content_description" msgid="9181551784092204234">"Privatno, otključano."</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 6bc8b70..1a38777 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Osztott képernyő"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Alkalmazásinformáció a következőhöz: %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"A(z) %1$s használati beállításai"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Alkalmazáspár mentése"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Ezt az alkalmazáspárt nem támogatja az eszköz"</string>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index 69b320d..eaaf435 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Տրոհել էկրանը"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Տեղեկություններ %1$s հավելվածի մասին"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Օգտագործման կարգավորումներ (%1$s)"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Պահել հավելվ. զույգը"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Հավելվածների զույգը չի աջակցվում այս սարքում"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 58a429f..62e9d9d 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Layar terpisah"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Info aplikasi untuk %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Setelan penggunaan untuk %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Simpan pasangan aplikasi"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Pasangan aplikasi ini tidak didukung di perangkat ini"</string>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index 95bd21f..7a87b26 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Skipta skjá"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Upplýsingar um forrit fyrir %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Notkunarstillingar fyrir %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Vista forritapar"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Þetta forritapar er ekki stutt í þessu tæki"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 3c01cd4..100da6c 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Schermo diviso"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Informazioni sull\'app %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Impostazioni di utilizzo per %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Salva coppia di app"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Questa coppia di app non è supportata su questo dispositivo"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index f198166..f85e571 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"מסך מפוצל"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"פרטים על האפליקציה %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"הגדרות שימוש ב-%1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"שמירת צמד אפליקציות"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"צמד האפליקציות הזה לא נתמך במכשיר הזה"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index d2f9a97..cea0a08 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"分割画面"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s のアプリ情報"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"%1$s の使用設定"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"アプリのペア設定を保存"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"このデバイスは、このアプリのペア設定に対応していません"</string>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index e67cc41..2211a7d 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"ეკრანის გაყოფა"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s-ის აპის ინფო"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"გამოყენების პარამეტრები %1$s-ისთვის"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"აპთა წყვილის შენახვა"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"ამ მოწყობილობაზე აღნიშნული აპთა წყვილი არ არის მხარდაჭერილი"</string>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index d5ccae5..ca26ea4 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Экранды бөлу"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s қолданбасы туралы ақпарат"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"%1$s пайдалану параметрлері"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Қолданбаларды жұптау әрекетін сақтау"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Бұл құрылғы қолданбаларды жұптау функциясын қолдамайды."</string>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index 3c9135b..dc4fd80 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"មុខងារបំបែកអេក្រង់"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"ព័ត៌មានកម្មវិធីសម្រាប់ %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"ការកំណត់ការប្រើប្រាស់សម្រាប់ %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"រក្សាទុកគូកម្មវិធី"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"មិនអាចប្រើគូកម្មវិធីនេះនៅលើឧបករណ៍នេះបានទេ"</string>
@@ -117,7 +119,7 @@
<string name="folder_name_format_exact" msgid="8626242716117004803">"ថត៖ <xliff:g id="NAME">%1$s</xliff:g>, ធាតុ <xliff:g id="SIZE">%2$d</xliff:g>"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"ថត៖ <xliff:g id="NAME">%1$s</xliff:g>, ធាតុ <xliff:g id="SIZE">%2$d</xliff:g> ឬច្រើនជាងនេះ"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"គូកម្មវិធី៖ <xliff:g id="APP1">%1$s</xliff:g> និង <xliff:g id="APP2">%2$s</xliff:g>"</string>
- <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"ផ្ទាំងរូបភាព និងរចនាប័ទ្ម"</string>
+ <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"ផ្ទាំងរូបភាព និងរចនាបថ"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"កែអេក្រង់ដើម"</string>
<string name="settings_button_text" msgid="8873672322605444408">"ការកំណត់ទំព័រដើម"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"បានបិទដំណើរការដោយអ្នកគ្រប់គ្រងរបស់អ្នក"</string>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index ab84833..eda9fb8 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s ಗಾಗಿ ಆ್ಯಪ್ ಮಾಹಿತಿ"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"%1$s ಗೆ ಸಂಬಂಧಿಸಿದ ಬಳಕೆಯ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"ಆ್ಯಪ್ ಪೇರ್ ಸೇವ್ ಮಾಡಿ"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"ಈ ಆ್ಯಪ್ ಜೋಡಿಯು ಈ ಸಾಧನದಲ್ಲಿ ಬೆಂಬಲಿತವಾಗಿಲ್ಲ"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 318cd00..94ebd15 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"화면 분할"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s 앱 정보"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"%1$s의 사용량 설정"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"앱 페어링 저장"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"이 앱 페어링은 이 기기에서 지원되지 않습니다"</string>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index 856e2b2..b326181 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Экранды бөлүү"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s колдонмосу жөнүндө маалымат"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"%1$s колдонмосун пайдалануу параметрлери"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Колдонмолорду сактап коюу"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Бул эки колдонмону бул түзмөктө бир маалда пайдаланууга болбойт"</string>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index 3d1a6c9..741556a 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"ແບ່ງໜ້າຈໍ"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"ຂໍ້ມູນແອັບສຳລັບ %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"ການຕັ້ງຄ່າການນຳໃຊ້ສຳລັບ %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"ບັນທຶກຈັບຄູ່ແອັບ"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"ການຈັບຄູ່ແອັບນີ້ບໍ່ຮອງຮັບຢູ່ອຸປະກອນນີ້"</string>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 4c9bd9b..a613cb1 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Išskaidyto ekrano režimas"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Programos „%1$s“ informacija"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"„%1$s“ naudojimo nustatymai"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Išsaugoti programų porą"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Ši programų pora šiame įrenginyje nepalaikoma"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 0a82705..de00e4d 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Sadalīt ekrānu"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s: informācija par lietotni"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Lietojuma iestatījumi: %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Saglabāt lietotņu pāri"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Šis lietotņu pāris netiek atbalstīts šajā ierīcē"</string>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 887ca82..91b30c9 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Поделен екран"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Податоци за апликација за %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Поставки за користење за %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Зачувај го парот апликации"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Паров апликации не е поддржан на уредов"</string>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index dda5679..2eb1b2b 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"സ്ക്രീൻ വിഭജന മോഡ്"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s എന്നതിന്റെ ആപ്പ് വിവരങ്ങൾ"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"%1$s എന്നതിനുള്ള ഉപയോഗ ക്രമീകരണം"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"ആപ്പ് ജോടി സംരക്ഷിക്കുക"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"ഈ ഉപകരണത്തിൽ ഈ ആപ്പ് ജോടിക്ക് പിന്തുണയില്ല"</string>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index 49d71c2..bba7e16 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Дэлгэцийг хуваах"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s-н аппын мэдээлэл"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"%1$s-н ашиглалтын тохиргоо"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Апп хослуулалтыг хадгалах"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Энэ апп хослуулалтыг уг төхөөрөмж дээр дэмждэггүй"</string>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index fdf864a..548e764 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"स्प्लिट स्क्रीन"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s साठी ॲपशी संबंधित माहिती"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"%1$s साठी वापरासंबंधित सेटिंग्ज"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"ॲपची जोडी सेव्ह करा"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"या ॲपची जोडीला या डिव्हाइसवर सपोर्ट नाही"</string>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index b86f657..b548e67 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Skrin pisah"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Maklumat apl untuk %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Tetapan penggunaan sebanyak %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Simpan gandingan apl"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Gandingan apl ini tidak disokong pada peranti ini"</string>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index 7e8fd14..e94dd8e 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"မျက်နှာပြင် ခွဲ၍ပြသခြင်း"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s အတွက် အက်ပ်အချက်အလက်"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"%1$s အတွက် အသုံးပြုမှုဆက်တင်များ"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"အက်ပ်တွဲချိတ်ခြင်း သိမ်းရန်"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"ဤအက်ပ်တွဲချိတ်ခြင်းကို ဤစက်တွင် ပံ့ပိုးမထားပါ"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 2a6611f..38003eb 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -31,7 +31,9 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Delt skjerm"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Appinformasjon for %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Bruksinnstillinger for %1$s"</string>
- <string name="save_app_pair" msgid="5647523853662686243">"Lagre apptilkoblingen"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
+ <string name="save_app_pair" msgid="5647523853662686243">"Lagre app-paret"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Denne apptilkoblingen støttes ikke på denne enheten"</string>
<string name="app_pair_needs_unfold" msgid="4588897528143807002">"Åpne enheten for å bruke denne apptilkoblingen"</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index fa2e59b..93c4339 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"स्प्लिट स्क्रिन"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s का हकमा एपसम्बन्धी जानकारी"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"%1$s को प्रयोगसम्बन्धी सेटिङ"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"एपको पेयर सेभ गर्नुहोस्"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"यस डिभाइसमा यो एप पेयर प्रयोग गर्न मिल्दैन"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 9271b96..1c2bdc8 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Gesplitst scherm"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"App-info voor %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Gebruiksinstellingen voor %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"App-paar opslaan"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Dit app-paar wordt niet ondersteund op dit apparaat"</string>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index 98d52de..9a03926 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"ସ୍କ୍ରିନକୁ ସ୍ପ୍ଲିଟ କରନ୍ତୁ"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s ପାଇଁ ଆପ ସୂଚନା"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"%1$s ପାଇଁ ବ୍ୟବହାର ସେଟିଂସ"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"ଆପ ପେୟାର ସେଭ କରନ୍ତୁ"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"ଏହି ଆପ ପେୟାର ଏ ଡିଭାଇସରେ ସମର୍ଥିତ ନୁହେଁ"</string>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 782979e..461513c 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s ਲਈ ਐਪ ਜਾਣਕਾਰੀ"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"%1$s ਲਈ ਵਰਤੋਂ ਸੈਟਿੰਗਾਂ"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"ਐਪ ਜੋੜਾਬੱਧ ਰੱਖਿਅਤ ਕਰੋ"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"ਇਸ ਐਪ ਜੋੜਾਬੱਧ ਦਾ ਇਸ ਡੀਵਾਈਸ \'ਤੇ ਸਮਰਥਨ ਨਹੀਂ ਕੀਤਾ ਜਾਂਦਾ"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 71e569c..eeb9eb3 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Podziel ekran"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Informacje o aplikacji: %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"%1$s – ustawienia użycia"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Zapisz parę aplikacji"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Ta para aplikacji nie jest obsługiwana na tym urządzeniu"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 1c44d9b..c86c58d 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Ecrã dividido"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Informações da app para %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Definições de utilização para %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Guardar par de apps"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Este par de apps não é suportado neste dispositivo"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 3f44591..f10b93a 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Tela dividida"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Informações do app %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Configurações de uso de %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Salvar par de apps"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Este Par de apps não está disponível no dispositivo"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index b37d93b..fbab69c 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Ecran împărțit"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Informații despre aplicație pentru %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Setări de utilizare pentru %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Salvează perechea de aplicații"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Perechea de aplicații nu este acceptată pe acest dispozitiv"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 321bf37..aea3d2b 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Разделить экран"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Сведения о приложении \"%1$s\""</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Настройки использования приложения \"%1$s\""</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Сохранить приложения"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Одновременно использовать эти два приложения на устройстве нельзя."</string>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index 91c818a..a3b9a71 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"බෙදුම් තිරය"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s සඳහා යෙදුම් තතු"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"%1$s සඳහා භාවිත සැකසීම්"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"යෙදුම් යුගල සුරකින්න"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"මෙම යෙදුම් යුගලය මෙම උපාංගයෙහි සහාය නොදක්වයි"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index eaae7c0..c3b3422 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Rozdeliť obrazovku"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Informácie o aplikácii pre %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Nastavenia používania pre %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Uložiť pár aplikácií"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Tento pár aplikácií nie je v tomto zariadení podporovaný"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index dccf2f1..51b0c7b 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Razdeljen zaslon"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Podatki o aplikaciji za: %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Nastavitve uporabe za »%1$s«"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Shrani par aplikacij"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Ta par aplikacij ni podprt v tej napravi"</string>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index 84484e8..d3bfce6 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Ekrani i ndarë"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Informacioni i aplikacionit për %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Cilësimet e përdorimit për \"%1$s\""</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Ruaj çiftin e aplikacioneve"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Ky çift aplikacionesh nuk mbështetet në këtë pajisje"</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 64383f2..c93519f 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Подељени екран"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Информације о апликацији за: %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Подешавања потрошње за %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Сачувај пар апликација"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Овај пар апликација није подржан на овом уређају"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 47aed86..c92d6f0 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Delad skärm"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Appinformation för %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Användningsinställningar för %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Spara app-par"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"De här apparna som ska användas tillsammans stöds inte på den här enheten"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 5eadd1d..7d2768b 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Gawa skrini"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Maelezo ya programu ya %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Mipangilio ya matumizi ya %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Hifadhi jozi ya programu"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Jozi hii ya programu haitumiki kwenye kifaa hiki"</string>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index d84485a..2ae4c30 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"திரைப் பிரிப்பு"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$sக்கான ஆப்ஸ் தகவல்கள்"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"%1$sக்கான உபயோக அமைப்புகள்"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"ஆப்ஸ் ஜோடியைச் சேமி"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"இந்தச் சாதனத்தில் இந்த ஆப்ஸ் ஜோடி ஆதரிக்கப்படவில்லை"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 8487e96..5348a22 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"స్ప్లిట్ స్క్రీన్"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s కోసం యాప్ సమాచారం"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"%1$sకు సంబంధించిన వినియోగ సెట్టింగ్లు"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"యాప్ పెయిర్ను సేవ్ చేయండి"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"ఈ పరికరంలో ఈ యాప్ పెయిర్ సపోర్ట్ చేయదు"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 71f4d15..a6d3b7e 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"แยกหน้าจอ"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"ข้อมูลแอปสำหรับ %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"การตั้งค่าการใช้งานสำหรับ %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"บันทึกคู่แอป"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"ไม่รองรับคู่แอปนี้ในอุปกรณ์เครื่องนี้"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 7cf6a44..417962d 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Split screen"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Impormasyon ng app para sa %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Mga setting ng paggamit para sa %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"I-save ang app pair"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Hindi sinusuportahan sa device na ito ang pares ng app na ito"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index d55181c..0caa1f9 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Bölünmüş ekran"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s uygulama bilgileri"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"%1$s ile ilgili kullanım ayarları"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Uygulama çiftini kaydedin"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Bu uygulama çifti bu cihazda desteklenmiyor"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index b5c1c70..e980d22 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Розділити екран"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Інформація про додаток для %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Параметри використання (%1$s)"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Зберегти пару додатків"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Ці два додатки не можна одночасно використовувати на цьому пристрої"</string>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index 6fa76dd..4d59e17 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"اسپلٹ اسکرین"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s کے لیے ایپ کی معلومات"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"%1$s کیلئے استعمال کی ترتیبات"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"ایپس کے جوڑے کو محفوظ کریں"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"ایپس کا یہ جوڑا اس آلے پر تعاون یافتہ نہیں ہے"</string>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index 21a8145..d2225c7 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Ekranni ikkiga ajratish"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s ilovasi axboroti"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"%1$s uchun sarf sozlamalari"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Ilova juftini saqlash"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Bu ilova jufti ushbu qurilmada ishlamaydi"</string>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index b0bac73..4c14574 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Chia đôi màn hình"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Thông tin ứng dụng cho %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Chế độ cài đặt mức sử dụng %1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Lưu cặp ứng dụng"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Cặp ứng dụng này không hoạt động được trên thiết bị này"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 112b945..92591fb 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"分屏"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s 的应用信息"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"%1$s的使用设置"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"保存应用组合"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"在该设备上无法使用此应用对"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index e63093e..05f3320 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"分割螢幕"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s 的應用程式資料"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"「%1$s」的用量設定"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"儲存應用程式配對"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"此裝置不支援此應用程式配對"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 25f9703..6ee3e99 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"分割畫面"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"「%1$s」的應用程式資訊"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"「%1$s」的用量設定"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"儲存應用程式配對"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"這部裝置不支援這組應用程式配對"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index ec1f941..8d2e2f3 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -31,6 +31,8 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Hlukanisa isikrini"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Ulwazi lwe-App ye-%1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Amasethingi okusetshenziswa ka-%1$s"</string>
+ <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Londoloza i-app ebhangqiwe"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Lokhu kubhanqwa kwe-app akusekelwa kule divayisi"</string>
diff --git a/res/values/config.xml b/res/values/config.xml
index 507ce9a..701e64a 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -67,7 +67,6 @@
<string name="main_process_initializer_class" translatable="false"></string>
<string name="app_launch_tracker_class" translatable="false"></string>
<string name="test_information_handler_class" translatable="false"></string>
- <string name="launcher_activity_logic_class" translatable="false"></string>
<string name="model_delegate_class" translatable="false"></string>
<string name="window_manager_proxy_class" translatable="false"></string>
<string name="secondary_display_predictions_class" translatable="false"></string>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index fd724a5..9d06021 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -45,6 +45,9 @@
<string name="split_app_info_accessibility">App info for %1$s</string>
<string name="split_app_usage_settings">Usage settings for %1$s</string>
+ <!-- Title for an option to open a new window for a given app -->
+ <string name="new_window_option_taskbar">New Window</string>
+
<!-- App pairs -->
<string name="save_app_pair">Save app pair</string>
<!-- App pair default title -->
diff --git a/src/com/android/launcher3/Alarm.java b/src/com/android/launcher3/Alarm.java
index fb8088c..e516ad0 100644
--- a/src/com/android/launcher3/Alarm.java
+++ b/src/com/android/launcher3/Alarm.java
@@ -20,6 +20,8 @@
import android.os.Looper;
import android.os.SystemClock;
+import androidx.annotation.VisibleForTesting;
+
public class Alarm implements Runnable{
// if we reach this time and the alarm hasn't been cancelled, call the listener
private long mAlarmTriggerTime;
@@ -96,4 +98,13 @@
public long getLastSetTimeout() {
return mLastSetTimeout;
}
+
+ /** Simulates the alarm firing for tests. */
+ @VisibleForTesting
+ public void finishAlarm() {
+ if (!mAlarmPending) return;
+ mAlarmPending = false;
+ mHandler.removeCallbacks(this);
+ mAlarmListener.onAlarm(this);
+ }
}
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index fec94fe..2e75261 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -188,7 +188,7 @@
public SystemUiController getSystemUiController() {
if (mSystemUiController == null) {
- mSystemUiController = new SystemUiController(getWindow());
+ mSystemUiController = new SystemUiController(getWindow().getDecorView());
}
return mSystemUiController;
}
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index cc5baea..483f5f8 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -1056,6 +1056,7 @@
return mHotseatColumnSpan;
}
+ @VisibleForTesting
public int getHotseatWidthPx() {
return mHotseatWidthPx;
}
diff --git a/src/com/android/launcher3/DropTargetHandler.kt b/src/com/android/launcher3/DropTargetHandler.kt
index e022159..f1029b1 100644
--- a/src/com/android/launcher3/DropTargetHandler.kt
+++ b/src/com/android/launcher3/DropTargetHandler.kt
@@ -35,8 +35,7 @@
target?.let {
deferred.mPackageName = it.packageName
mLauncher.addEventCallback(EVENT_RESUMED) { deferred.onLauncherResume() }
- }
- ?: deferred.sendFailure()
+ } ?: deferred.sendFailure()
}
}
}
@@ -47,19 +46,10 @@
mLauncher.appWidgetHolder.startConfigActivity(
mLauncher,
widgetId,
- ActivityCodes.REQUEST_RECONFIGURE_APPWIDGET
+ ActivityCodes.REQUEST_RECONFIGURE_APPWIDGET,
)
}
- fun dismissPrediction(
- announcement: CharSequence,
- onActionClicked: Runnable,
- onDismiss: Runnable?
- ) {
- mLauncher.dragLayer.announceForAccessibility(announcement)
- Snackbar.show(mLauncher, R.string.item_removed, R.string.undo, onDismiss, onActionClicked)
- }
-
fun getViewUnderDrag(info: ItemInfo): View? {
return if (
info is LauncherAppWidgetInfo &&
@@ -95,7 +85,7 @@
R.string.item_removed,
R.string.undo,
mLauncher.modelWriter::commitDelete,
- onUndoClicked
+ onUndoClicked,
)
}
diff --git a/src/com/android/launcher3/FastScrollRecyclerView.java b/src/com/android/launcher3/FastScrollRecyclerView.java
index 6622e11..17084bb 100644
--- a/src/com/android/launcher3/FastScrollRecyclerView.java
+++ b/src/com/android/launcher3/FastScrollRecyclerView.java
@@ -60,6 +60,7 @@
mScrollbar = scrollbar;
mScrollbar.setRecyclerView(this);
mScrollbar.setFastScrollerLocation(location);
+ scrollToTop();
onUpdateScrollbar(0);
}
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 0d4ebe0..024dde4 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -33,15 +33,34 @@
import android.view.ViewGroup;
import android.widget.FrameLayout;
+import androidx.annotation.IntDef;
+
import com.android.launcher3.util.HorizontalInsettableView;
+import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
import com.android.launcher3.util.MultiTranslateDelegate;
+import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.views.ActivityContext;
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* View class that represents the bottom row of the home screen.
*/
public class Hotseat extends CellLayout implements Insettable {
+ public static final int ALPHA_CHANNEL_TASKBAR_ALIGNMENT = 0;
+ public static final int ALPHA_CHANNEL_PREVIEW_RENDERER = 1;
+ public static final int ALPHA_CHANNEL_TASKBAR_STASH = 2;
+ public static final int ALPHA_CHANNEL_CHANNELS_COUNT = 3;
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @IntDef({ALPHA_CHANNEL_TASKBAR_ALIGNMENT, ALPHA_CHANNEL_PREVIEW_RENDERER,
+ ALPHA_CHANNEL_TASKBAR_STASH})
+ public @interface HotseatQsbAlphaId {
+ }
+
// Ratio of empty space, qsb should take up to appear visually centered.
public static final float QSB_CENTER_FACTOR = .325f;
private static final int BUBBLE_BAR_ADJUSTMENT_ANIMATION_DURATION_MS = 250;
@@ -50,6 +69,8 @@
private boolean mHasVerticalHotseat;
private Workspace<?> mWorkspace;
private boolean mSendTouchToWorkspace;
+ private final MultiValueAlpha mIconsAlphaChannels;
+ private final MultiValueAlpha mQsbAlphaChannels;
private final View mQsb;
@@ -63,9 +84,11 @@
public Hotseat(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
-
mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false);
addView(mQsb);
+ mIconsAlphaChannels = new MultiValueAlpha(getShortcutsAndWidgets(),
+ ALPHA_CHANNEL_CHANNELS_COUNT);
+ mQsbAlphaChannels = new MultiValueAlpha(mQsb, ALPHA_CHANNEL_CHANNELS_COUNT);
}
/**
@@ -270,21 +293,27 @@
}
/**
- * Sets the alpha value of just our ShortcutAndWidgetContainer.
+ * Sets the alpha value of the specified alpha channel of just our ShortcutAndWidgetContainer.
*/
- public void setIconsAlpha(float alpha) {
- getShortcutsAndWidgets().setAlpha(alpha);
+ public void setIconsAlpha(float alpha, @HotseatQsbAlphaId int channelId) {
+ getIconsAlpha(channelId).setValue(alpha);
}
/**
* Sets the alpha value of just our QSB.
*/
- public void setQsbAlpha(float alpha) {
- mQsb.setAlpha(alpha);
+ public void setQsbAlpha(float alpha, @HotseatQsbAlphaId int channelId) {
+ getQsbAlpha(channelId).setValue(alpha);
}
- public float getIconsAlpha() {
- return getShortcutsAndWidgets().getAlpha();
+ /** Returns the alpha channel for ShortcutAndWidgetContainer */
+ public MultiProperty getIconsAlpha(@HotseatQsbAlphaId int channelId) {
+ return mIconsAlphaChannels.get(channelId);
+ }
+
+ /** Returns the alpha channel for Qsb */
+ public MultiProperty getQsbAlpha(@HotseatQsbAlphaId int channelId) {
+ return mQsbAlphaChannels.get(channelId);
}
/**
@@ -294,4 +323,24 @@
return mQsb;
}
+ /** Dumps the Hotseat internal state */
+ public void dump(String prefix, PrintWriter writer) {
+ writer.println(prefix + "Hotseat:");
+ mIconsAlphaChannels.dump(
+ prefix + "\t",
+ writer,
+ "mIconsAlphaChannels",
+ "ALPHA_CHANNEL_TASKBAR_ALIGNMENT",
+ "ALPHA_CHANNEL_PREVIEW_RENDERER",
+ "ALPHA_CHANNEL_TASKBAR_STASH");
+ mQsbAlphaChannels.dump(
+ prefix + "\t",
+ writer,
+ "mQsbAlphaChannels",
+ "ALPHA_CHANNEL_TASKBAR_ALIGNMENT",
+ "ALPHA_CHANNEL_PREVIEW_RENDERER",
+ "ALPHA_CHANNEL_TASKBAR_STASH"
+ );
+ }
+
}
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 54aea38..5ea7bd9 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -354,7 +354,7 @@
*/
@Deprecated
public void reset(Context context) {
- initGrid(context, getCurrentGridName(context));
+ initGrid(context, getDefaultGridName(context));
}
@VisibleForTesting
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 365e3d4..b0ec9b0 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -76,7 +76,6 @@
import static com.android.launcher3.logging.StatsLogManager.EventEnum;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_ENTRY;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_ENTRY_WITH_DEVICE_SEARCH;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_EXIT;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ONRESUME;
@@ -449,12 +448,10 @@
.logStart(LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION)
.logStart(LAUNCHER_LATENCY_STARTUP_ACTIVITY_ON_CREATE);
// Only use a hard-coded cookie since we only want to trace this once.
- if (Utilities.ATLEAST_S) {
- Trace.beginAsyncSection(
- DISPLAY_WORKSPACE_TRACE_METHOD_NAME, DISPLAY_WORKSPACE_TRACE_COOKIE);
- Trace.beginAsyncSection(DISPLAY_ALL_APPS_TRACE_METHOD_NAME,
- DISPLAY_ALL_APPS_TRACE_COOKIE);
- }
+ Trace.beginAsyncSection(
+ DISPLAY_WORKSPACE_TRACE_METHOD_NAME, DISPLAY_WORKSPACE_TRACE_COOKIE);
+ Trace.beginAsyncSection(DISPLAY_ALL_APPS_TRACE_METHOD_NAME,
+ DISPLAY_ALL_APPS_TRACE_COOKIE);
TraceHelper.INSTANCE.beginSection(ON_CREATE_EVT);
if (DEBUG_STRICT_MODE) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
@@ -733,13 +730,6 @@
public void onEnterAnimationComplete() {
super.onEnterAnimationComplete();
mRotationHelper.setCurrentTransitionRequest(REQUEST_NONE);
- // Starting with Android S, onEnterAnimationComplete is sent immediately
- // causing the surface to get removed before the animation completed (b/175345344).
- // Instead we rely on next user touch event to remove the view and optionally a callback
- // from system from Android T onwards.
- if (!Utilities.ATLEAST_S) {
- AbstractFloatingView.closeOpenViews(this, false, TYPE_ICON_SURFACE);
- }
}
@Override
@@ -1251,9 +1241,7 @@
* Returns {@link EventEnum} that should be logged when Launcher enters into AllApps state.
*/
protected Optional<EventEnum> getAllAppsEntryEvent() {
- return Optional.of(FeatureFlags.ENABLE_DEVICE_SEARCH.get()
- ? LAUNCHER_ALLAPPS_ENTRY_WITH_DEVICE_SEARCH
- : LAUNCHER_ALLAPPS_ENTRY);
+ return Optional.of(LAUNCHER_ALLAPPS_ENTRY_WITH_DEVICE_SEARCH);
}
@Override
@@ -2582,10 +2570,8 @@
public void bindAllApplications(AppInfo[] apps, int flags,
Map<PackageUserKey, Integer> packageUserKeytoUidMap) {
mModelCallbacks.bindAllApplications(apps, flags, packageUserKeytoUidMap);
- if (Utilities.ATLEAST_S) {
- Trace.endAsyncSection(DISPLAY_ALL_APPS_TRACE_METHOD_NAME,
- DISPLAY_ALL_APPS_TRACE_COOKIE);
- }
+ Trace.endAsyncSection(DISPLAY_ALL_APPS_TRACE_METHOD_NAME,
+ DISPLAY_ALL_APPS_TRACE_COOKIE);
}
/**
@@ -2678,6 +2664,7 @@
}
writer.println(prefix + " Hotseat");
+ mHotseat.dump(prefix, writer);
ViewGroup layout = mHotseat.getShortcutsAndWidgets();
for (int j = 0; j < layout.getChildCount(); j++) {
Object tag = layout.getChildAt(j).getTag();
diff --git a/src/com/android/launcher3/LauncherApplication.java b/src/com/android/launcher3/LauncherApplication.java
index 8969b60..4c82e56 100644
--- a/src/com/android/launcher3/LauncherApplication.java
+++ b/src/com/android/launcher3/LauncherApplication.java
@@ -18,6 +18,7 @@
import android.app.Application;
import com.android.launcher3.dagger.DaggerLauncherAppComponent;
+import com.android.launcher3.dagger.LauncherAppComponent;
import com.android.launcher3.dagger.LauncherBaseAppComponent;
/**
@@ -30,10 +31,18 @@
public void onCreate() {
super.onCreate();
MainProcessInitializer.initialize(this);
- mAppComponent = DaggerLauncherAppComponent.builder().build();
+ initDagger();
}
- public LauncherBaseAppComponent getAppComponent() {
- return mAppComponent;
+ public LauncherAppComponent getAppComponent() {
+ // Since supertype setters will return a supertype.builder and @Component.Builder types
+ // must not have any generic types.
+ // We need to cast mAppComponent to {@link LauncherAppComponent} since appContext()
+ // method is defined in the super class LauncherBaseComponent#Builder.
+ return (LauncherAppComponent) mAppComponent;
+ }
+
+ protected void initDagger() {
+ mAppComponent = DaggerLauncherAppComponent.builder().appContext(this).build();
}
}
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index ca1b2a9..7ad17d9 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -71,6 +71,7 @@
import com.android.launcher3.pm.PackageInstallInfo;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.shortcuts.ShortcutRequest;
+import com.android.launcher3.util.ApplicationInfoWrapper;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.PackageManagerHelper;
@@ -446,8 +447,8 @@
IconCache iconCache = mApp.getIconCache();
final IntSet removedIds = new IntSet();
HashSet<WorkspaceItemInfo> archivedWorkspaceItemsToCacheRefresh = new HashSet<>();
- boolean isAppArchived = PackageManagerHelper.INSTANCE.get(mApp.getContext())
- .isAppArchivedForUser(packageName, user);
+ boolean isAppArchived =
+ new ApplicationInfoWrapper(mApp.getContext(), packageName, user).isArchived();
synchronized (dataModel) {
if (isAppArchived) {
// Remove package icon cache entry for archived app in case of a session
diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java
index 7176733..d645734 100644
--- a/src/com/android/launcher3/LauncherRootView.java
+++ b/src/com/android/launcher3/LauncherRootView.java
@@ -11,6 +11,7 @@
import com.android.launcher3.graphics.SysUiScrim;
import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.statemanager.StatefulContainer;
import com.android.launcher3.util.window.WindowManagerProxy;
import java.util.Collections;
@@ -20,7 +21,7 @@
private final Rect mTempRect = new Rect();
- private final StatefulActivity mActivity;
+ private final StatefulContainer mStatefulContainer;
@ViewDebug.ExportedProperty(category = "launcher")
private static final List<Rect> SYSTEM_GESTURE_EXCLUSION_RECT =
@@ -36,24 +37,25 @@
public LauncherRootView(Context context, AttributeSet attrs) {
super(context, attrs);
- mActivity = StatefulActivity.fromContext(context);
+ mStatefulContainer = StatefulContainer.fromContext(context);
mSysUiScrim = new SysUiScrim(this);
}
private void handleSystemWindowInsets(Rect insets) {
// Update device profile before notifying the children.
- mActivity.getDeviceProfile().updateInsets(insets);
+ mStatefulContainer.getDeviceProfile().updateInsets(insets);
boolean resetState = !insets.equals(mInsets);
setInsets(insets);
if (resetState) {
- mActivity.getStateManager().reapplyState(true /* cancelCurrentAnimation */);
+ mStatefulContainer.getStateManager().reapplyState(true /* cancelCurrentAnimation */);
}
}
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- mActivity.handleConfigurationChanged(mActivity.getResources().getConfiguration());
+ mStatefulContainer.handleConfigurationChanged(
+ mStatefulContainer.getContext().getResources().getConfiguration());
insets = WindowManagerProxy.INSTANCE.get(getContext())
.normalizeWindowInsets(getContext(), insets, mTempRect);
@@ -72,7 +74,7 @@
}
public void dispatchInsets() {
- mActivity.getDeviceProfile().updateInsets(mInsets);
+ mStatefulContainer.getDeviceProfile().updateInsets(mInsets);
super.setInsets(mInsets);
}
diff --git a/src/com/android/launcher3/ModelCallbacks.kt b/src/com/android/launcher3/ModelCallbacks.kt
index d57f8a0..496d517 100644
--- a/src/com/android/launcher3/ModelCallbacks.kt
+++ b/src/com/android/launcher3/ModelCallbacks.kt
@@ -61,7 +61,7 @@
AbstractFloatingView.closeOpenViews(
launcher,
true,
- AbstractFloatingView.TYPE_ALL and AbstractFloatingView.TYPE_REBIND_SAFE.inv()
+ AbstractFloatingView.TYPE_ALL and AbstractFloatingView.TYPE_REBIND_SAFE.inv(),
)
workspaceLoading = true
@@ -76,7 +76,7 @@
TAG,
"startBinding: " +
"hotseat layout was vertical: ${launcher.hotseat?.isHasVerticalHotseat}" +
- " and is setting to ${launcher.deviceProfile.isVerticalBarLayout}"
+ " and is setting to ${launcher.deviceProfile.isVerticalBarLayout}",
)
launcher.hotseat?.resetLayout(launcher.deviceProfile.isVerticalBarLayout)
TraceHelper.INSTANCE.endSection()
@@ -88,14 +88,12 @@
pendingTasks: RunnableList,
onCompleteSignal: RunnableList,
workspaceItemCount: Int,
- isBindSync: Boolean
+ isBindSync: Boolean,
) {
- if (Utilities.ATLEAST_S) {
- Trace.endAsyncSection(
- TraceEvents.DISPLAY_WORKSPACE_TRACE_METHOD_NAME,
- TraceEvents.DISPLAY_WORKSPACE_TRACE_COOKIE
- )
- }
+ Trace.endAsyncSection(
+ TraceEvents.DISPLAY_WORKSPACE_TRACE_METHOD_NAME,
+ TraceEvents.DISPLAY_WORKSPACE_TRACE_COOKIE,
+ )
synchronouslyBoundPages = boundPages
pagesToBindSynchronously = LIntSet()
clearPendingBinds()
@@ -149,14 +147,14 @@
// Cache one page worth of icons
launcher.viewCache.setCacheSize(
R.layout.folder_application,
- deviceProfile.numFolderColumns * deviceProfile.numFolderRows
+ deviceProfile.numFolderColumns * deviceProfile.numFolderRows,
)
launcher.viewCache.setCacheSize(R.layout.folder_page, 2)
TraceHelper.INSTANCE.endSection()
launcher.workspace.removeExtraEmptyScreen(/* stripEmptyScreens= */ true)
launcher.workspace.pageIndicator.setPauseScroll(
/*pause=*/ false,
- deviceProfile.isTwoPanels
+ deviceProfile.isTwoPanels,
)
TestEventEmitter.INSTANCE.get(launcher).sendEvent(TestEvent.WORKSPACE_FINISH_LOADING)
}
@@ -182,7 +180,7 @@
val snackbar =
AbstractFloatingView.getOpenView<AbstractFloatingView>(
launcher,
- AbstractFloatingView.TYPE_SNACKBAR
+ AbstractFloatingView.TYPE_SNACKBAR,
)
snackbar?.post { snackbar.close(true) }
}
@@ -191,7 +189,7 @@
override fun bindAllApplications(
apps: Array<AppInfo?>?,
flags: Int,
- packageUserKeytoUidMap: Map<PackageUserKey?, Int?>?
+ packageUserKeytoUidMap: Map<PackageUserKey?, Int?>?,
) {
Preconditions.assertUIThread()
val hadWorkApps = launcher.appsView.shouldShowTabs()
@@ -312,7 +310,7 @@
val info =
PendingAddWidgetInfo(
widgetsListBaseEntry.mWidgets[0].widgetInfo,
- LauncherSettings.Favorites.CONTAINER_DESKTOP
+ LauncherSettings.Favorites.CONTAINER_DESKTOP,
)
launcher.addPendingItem(
info,
@@ -320,14 +318,14 @@
WorkspaceLayoutManager.FIRST_SCREEN_ID,
intArrayOf(0, 0),
info.spanX,
- info.spanY
+ info.spanY,
)
}
override fun bindScreens(orderedScreenIds: LIntArray) {
launcher.workspace.pageIndicator.setPauseScroll(
/*pause=*/ true,
- launcher.deviceProfile.isTwoPanels
+ launcher.deviceProfile.isTwoPanels,
)
val firstScreenPosition = 0
if (
@@ -354,7 +352,7 @@
override fun bindAppsAdded(
newScreens: LIntArray?,
addNotAnimated: java.util.ArrayList<ItemInfo?>?,
- addAnimated: java.util.ArrayList<ItemInfo?>?
+ addAnimated: java.util.ArrayList<ItemInfo?>?,
) {
// Add the new screens
if (newScreens != null) {
diff --git a/src/com/android/launcher3/MotionEventsUtils.java b/src/com/android/launcher3/MotionEventsUtils.java
index 3228ec6..fb244b0 100644
--- a/src/com/android/launcher3/MotionEventsUtils.java
+++ b/src/com/android/launcher3/MotionEventsUtils.java
@@ -18,8 +18,6 @@
import static android.view.MotionEvent.CLASSIFICATION_TWO_FINGER_SWIPE;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_TRACKPAD_GESTURE;
-
import android.annotation.TargetApi;
import android.os.Build;
import android.view.MotionEvent;
@@ -35,14 +33,12 @@
@TargetApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
public static boolean isTrackpadScroll(MotionEvent event) {
- return ENABLE_TRACKPAD_GESTURE.get()
- && event.getClassification() == CLASSIFICATION_TWO_FINGER_SWIPE;
+ return event.getClassification() == CLASSIFICATION_TWO_FINGER_SWIPE;
}
@TargetApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
public static boolean isTrackpadMultiFingerSwipe(MotionEvent event) {
- return ENABLE_TRACKPAD_GESTURE.get()
- && event.getClassification() == CLASSIFICATION_MULTI_FINGER_SWIPE;
+ return event.getClassification() == CLASSIFICATION_MULTI_FINGER_SWIPE;
}
public static boolean isTrackpadThreeFingerSwipe(MotionEvent event) {
diff --git a/src/com/android/launcher3/SecondaryDropTarget.java b/src/com/android/launcher3/SecondaryDropTarget.java
index 0a4fb73..b3cb948 100644
--- a/src/com/android/launcher3/SecondaryDropTarget.java
+++ b/src/com/android/launcher3/SecondaryDropTarget.java
@@ -7,7 +7,6 @@
import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.INVALID;
import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.RECONFIGURE;
import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.UNINSTALL;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_DISMISS_PREDICTION_UNDO;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_UNINSTALL;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_UNINSTALL_CANCELLED;
@@ -23,7 +22,6 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
-import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.UserHandle;
@@ -36,7 +34,6 @@
import androidx.annotation.Nullable;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.logging.InstanceId;
@@ -46,7 +43,7 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.pm.UserCache;
-import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.ApplicationInfoWrapper;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import java.net.URISyntaxException;
@@ -242,8 +239,7 @@
@Override
public void completeDrop(final DragObject d) {
- ComponentName target = performDropAction(getViewUnderDrag(d.dragInfo), d.dragInfo,
- d.logInstanceId);
+ ComponentName target = performDropAction(getViewUnderDrag(d.dragInfo), d.dragInfo);
mDropTargetHandler.onSecondaryTargetCompleteDrop(target, d);
}
@@ -275,7 +271,7 @@
* Performs the drop action and returns the target component for the dragObject or null if
* the action was not performed.
*/
- protected ComponentName performDropAction(View view, ItemInfo info, InstanceId instanceId) {
+ protected ComponentName performDropAction(View view, ItemInfo info) {
if (mCurrentAccessibilityAction == RECONFIGURE) {
int widgetId = getReconfigurableWidgetId(view);
if (widgetId != INVALID_APPWIDGET_ID) {
@@ -283,21 +279,6 @@
}
return null;
}
- if (mCurrentAccessibilityAction == DISMISS_PREDICTION) {
- if (FeatureFlags.ENABLE_DISMISS_PREDICTION_UNDO.get()) {
- CharSequence announcement = getContext().getString(R.string.item_removed);
- mDropTargetHandler
- .dismissPrediction(announcement, () -> {
- }, () -> {
- mStatsLogManager.logger()
- .withInstanceId(instanceId)
- .withItemInfo(info)
- .log(LAUNCHER_DISMISS_PREDICTION_UNDO);
- });
- }
- return null;
- }
-
return performUninstall(getContext(), getUninstallTarget(getContext(), info), info);
}
@@ -332,9 +313,8 @@
@Override
public void onAccessibilityDrop(View view, ItemInfo item) {
- InstanceId instanceId = new InstanceIdSequence().newInstanceId();
- doLog(instanceId, item);
- performDropAction(view, item, instanceId);
+ doLog(new InstanceIdSequence().newInstanceId(), item);
+ performDropAction(view, item);
}
/**
@@ -361,9 +341,8 @@
}
public void onLauncherResume() {
- // We use MATCH_UNINSTALLED_PACKAGES as the app can be on SD card as well.
- if (PackageManagerHelper.INSTANCE.get(mContext).getApplicationInfo(mPackageName,
- mDragObject.dragInfo.user, PackageManager.MATCH_UNINSTALLED_PACKAGES) == null) {
+ if (new ApplicationInfoWrapper(mContext, mPackageName, mDragObject.dragInfo.user)
+ .getInfo() == null) {
mDragObject.dragSource = mOriginal;
mOriginal.onDropCompleted(SecondaryDropTarget.this, mDragObject, true);
mStatsLogManager.logger().withInstanceId(mDragObject.logInstanceId)
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index fde7014..f8ac48a 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -122,9 +122,6 @@
public static final String[] EMPTY_STRING_ARRAY = new String[0];
public static final Person[] EMPTY_PERSON_ARRAY = new Person[0];
- @ChecksSdkIntAtLeast(api = VERSION_CODES.S)
- public static final boolean ATLEAST_S = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;
-
@ChecksSdkIntAtLeast(api = VERSION_CODES.TIRAMISU, codename = "T")
public static final boolean ATLEAST_T = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU;
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index cc4724c..1094768 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -22,8 +22,6 @@
import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_PRIVATE_SPACE_HEADER;
import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_WORK_DISABLED_CARD;
import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_WORK_EDU_CARD;
-import static com.android.launcher3.config.FeatureFlags.ALL_APPS_GONE_VISIBILITY;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_ALL_APPS_RV_PREINFLATION;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_COUNT;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_TAP_ON_PERSONAL_TAB;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_TAP_ON_WORK_TAB;
@@ -679,18 +677,13 @@
@NonNull AllAppsRecyclerView mainRecyclerView,
@Nullable AllAppsRecyclerView workRecyclerView,
@NonNull AllAppsRecyclerViewPool recycledViewPool) {
- if (!ENABLE_ALL_APPS_RV_PREINFLATION.get()) {
- return;
- }
final boolean hasWorkProfile = workRecyclerView != null;
recycledViewPool.setHasWorkProfile(hasWorkProfile);
mainRecyclerView.setRecycledViewPool(recycledViewPool);
if (workRecyclerView != null) {
workRecyclerView.setRecycledViewPool(recycledViewPool);
}
- if (ALL_APPS_GONE_VISIBILITY.get()) {
- mainRecyclerView.updatePoolSize(hasWorkProfile);
- }
+ mainRecyclerView.updatePoolSize(hasWorkProfile);
}
private void replaceAppsRVContainer(boolean showTabs) {
@@ -735,9 +728,7 @@
removeCustomRules(rvContainer);
removeCustomRules(getSearchRecyclerView());
- if (!isSearchSupported()) {
- layoutWithoutSearchContainer(rvContainer, showTabs);
- } else if (isSearchBarFloating()) {
+ if (isSearchBarFloating()) {
alignParentTop(rvContainer, showTabs);
alignParentTop(getSearchRecyclerView(), /* tabs= */ false);
} else {
@@ -768,9 +759,7 @@
});
removeCustomRules(mHeader);
- if (!isSearchSupported()) {
- layoutWithoutSearchContainer(mHeader, false /* includeTabsMargin */);
- } else if (isSearchBarFloating()) {
+ if (isSearchBarFloating()) {
alignParentTop(mHeader, false /* includeTabsMargin */);
} else {
layoutBelowSearchContainer(mHeader, false /* includeTabsMargin */);
@@ -925,23 +914,6 @@
mMainAdapterProvider);
}
- // TODO(b/216683257): Remove when Taskbar All Apps supports search.
- protected boolean isSearchSupported() {
- return true;
- }
-
- private void layoutWithoutSearchContainer(View v, boolean includeTabsMargin) {
- if (!(v.getLayoutParams() instanceof RelativeLayout.LayoutParams)) {
- return;
- }
-
- RelativeLayout.LayoutParams layoutParams = (LayoutParams) v.getLayoutParams();
- layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
- layoutParams.topMargin = getContext().getResources().getDimensionPixelSize(includeTabsMargin
- ? R.dimen.all_apps_header_pill_height
- : R.dimen.all_apps_header_top_margin);
- }
-
public boolean isInAllApps() {
// TODO: Make this abstract
return true;
diff --git a/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java b/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java
index 911612f..77a0fe3 100644
--- a/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java
+++ b/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.allapps;
+import static android.view.HapticFeedbackConstants.CLOCK_TICK;
+
import androidx.recyclerview.widget.LinearSmoothScroller;
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
@@ -71,6 +73,7 @@
@Override
protected int getVerticalSnapPreference() {
+ mRv.performHapticFeedback(CLOCK_TICK);
return SNAP_TO_ANY;
}
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index ae45a35..4e1e950 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -18,8 +18,6 @@
import static androidx.constraintlayout.widget.ConstraintSet.MATCH_CONSTRAINT;
import static androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT;
-import static com.android.launcher3.config.FeatureFlags.ALL_APPS_GONE_VISIBILITY;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_ALL_APPS_RV_PREINFLATION;
import static com.android.launcher3.logger.LauncherAtom.ContainerInfo;
import static com.android.launcher3.logger.LauncherAtom.SearchResultContainer;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_PERSONAL_SCROLLED_DOWN;
@@ -124,13 +122,11 @@
// all apps.
int maxPoolSizeForAppIcons = grid.getMaxAllAppsRowCount()
* grid.numShownAllAppsColumns;
- if (ALL_APPS_GONE_VISIBILITY.get() && ENABLE_ALL_APPS_RV_PREINFLATION.get()) {
- // If we set all apps' hidden visibility to GONE and enable pre-inflation, we want to
- // preinflate one page of all apps icons plus [PREINFLATE_ICONS_ROW_COUNT] rows +
- // [EXTRA_ICONS_COUNT]. Thus we need to bump the max pool size of app icons accordingly.
- maxPoolSizeForAppIcons +=
- PREINFLATE_ICONS_ROW_COUNT * grid.numShownAllAppsColumns + EXTRA_ICONS_COUNT;
- }
+ // If we set all apps' hidden visibility to GONE and enable pre-inflation, we want to
+ // preinflate one page of all apps icons plus [PREINFLATE_ICONS_ROW_COUNT] rows +
+ // [EXTRA_ICONS_COUNT]. Thus we need to bump the max pool size of app icons accordingly.
+ maxPoolSizeForAppIcons +=
+ PREINFLATE_ICONS_ROW_COUNT * grid.numShownAllAppsColumns + EXTRA_ICONS_COUNT;
if (hasWorkProfile) {
maxPoolSizeForAppIcons *= 2;
}
diff --git a/src/com/android/launcher3/allapps/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java
index a4f130a..29b9e77 100644
--- a/src/com/android/launcher3/allapps/AllAppsStore.java
+++ b/src/com/android/launcher3/allapps/AllAppsStore.java
@@ -15,7 +15,6 @@
*/
package com.android.launcher3.allapps;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_ALL_APPS_RV_PREINFLATION;
import static com.android.launcher3.model.data.AppInfo.COMPONENT_KEY_COMPARATOR;
import static com.android.launcher3.model.data.AppInfo.EMPTY_ARRAY;
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK;
@@ -109,7 +108,7 @@
mPackageUserKeytoUidMap = map;
// Preinflate all apps RV when apps has changed, which can happen after unlocking screen,
// rotating screen, or downloading/upgrading apps.
- if (shouldPreinflate && ENABLE_ALL_APPS_RV_PREINFLATION.get()) {
+ if (shouldPreinflate) {
mAllAppsRecyclerViewPool.preInflateAllAppsViewHolders(mContext);
}
}
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 742648e..c6852e0 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -54,7 +54,6 @@
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PropertySetter;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.touch.AllAppsSwipeController;
@@ -413,8 +412,7 @@
mAppsView = appsView;
mAppsView.setScrimView(scrimView);
- mAppsViewAlpha = new MultiValueAlpha(mAppsView, APPS_VIEW_INDEX_COUNT,
- FeatureFlags.ALL_APPS_GONE_VISIBILITY.get() ? View.GONE : View.INVISIBLE);
+ mAppsViewAlpha = new MultiValueAlpha(mAppsView, APPS_VIEW_INDEX_COUNT, View.GONE);
mAppsViewAlpha.setUpdateVisibility(true);
mAppsViewTranslationY = new MultiPropertyFactory<>(
mAppsView, VIEW_TRANSLATE_Y, APPS_VIEW_INDEX_COUNT, Float::sum);
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index a2bd5dd..ac06ab4 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -34,7 +34,6 @@
import com.android.launcher3.Insettable;
import com.android.launcher3.R;
import com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.PluginManagerWrapper;
import com.android.launcher3.views.ActivityContext;
import com.android.systemui.plugins.AllAppsRow;
@@ -220,15 +219,12 @@
@Override
public View getFocusedChild() {
- if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
- for (FloatingHeaderRow row : mAllRows) {
- if (row.hasVisibleContent() && row.isVisible()) {
- return row.getFocusedChild();
- }
+ for (FloatingHeaderRow row : mAllRows) {
+ if (row.hasVisibleContent() && row.isVisible()) {
+ return row.getFocusedChild();
}
- return null;
}
- return super.getFocusedChild();
+ return null;
}
void setup(AllAppsRecyclerView mainRV, AllAppsRecyclerView workRV, SearchRecyclerView searchRV,
diff --git a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
index ec45415..de3bb9e 100644
--- a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
@@ -22,8 +22,6 @@
import android.text.style.SuggestionSpan;
import android.util.Log;
import android.view.KeyEvent;
-import android.view.View;
-import android.view.View.OnFocusChangeListener;
import android.view.inputmethod.EditorInfo;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
@@ -31,7 +29,6 @@
import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.search.SearchAlgorithm;
import com.android.launcher3.search.SearchCallback;
import com.android.launcher3.views.ActivityContext;
@@ -40,8 +37,7 @@
* An interface to a search box that AllApps can command.
*/
public class AllAppsSearchBarController
- implements TextWatcher, OnEditorActionListener, ExtendedEditText.OnBackKeyListener,
- OnFocusChangeListener {
+ implements TextWatcher, OnEditorActionListener, ExtendedEditText.OnBackKeyListener {
private static final String TAG = "AllAppsSearchBarController";
protected ActivityContext mLauncher;
@@ -69,7 +65,6 @@
mInput.addTextChangedListener(this);
mInput.setOnEditorActionListener(this);
mInput.setOnBackKeyListener(this);
- mInput.addOnFocusChangeListener(this);
mSearchAlgorithm = searchAlgorithm;
}
@@ -142,13 +137,6 @@
return false;
}
- @Override
- public void onFocusChange(View view, boolean hasFocus) {
- if (!hasFocus && !FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
- mInput.hideKeyboard();
- }
- }
-
/**
* Resets the search bar state.
*/
@@ -157,7 +145,6 @@
mInput.reset();
mInput.clearFocus();
mQuery = null;
- mInput.removeOnFocusChangeListener(this);
}
/**
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 092b524..8fe1b34 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -63,12 +63,6 @@
* <p>
*/
// TODO(Block 3): Clean up flags
- public static final BooleanFlag ENABLE_DISMISS_PREDICTION_UNDO = getDebugFlag(270394476,
- "ENABLE_DISMISS_PREDICTION_UNDO", DISABLED,
- "Show an 'Undo' snackbar when users dismiss a predicted hotseat item");
- public static final BooleanFlag CONTINUOUS_VIEW_TREE_CAPTURE = getDebugFlag(270395171,
- "CONTINUOUS_VIEW_TREE_CAPTURE", ENABLED, "Capture View tree every frame");
-
public static final BooleanFlag ENABLE_WORKSPACE_LOADING_OPTIMIZATION = getDebugFlag(251502424,
"ENABLE_WORKSPACE_LOADING_OPTIMIZATION", DISABLED,
"load the current workspace screen visible to the user before the rest rather than "
@@ -79,16 +73,7 @@
"changes the timing of the loading and binding of delegate items during "
+ "data preparation for loading the home screen");
- // TODO(Block 4): Cleanup flags
- public static final BooleanFlag ENABLE_SHOW_KEYBOARD_OPTION_IN_ALL_APPS = getReleaseFlag(
- 270394468, "ENABLE_SHOW_KEYBOARD_OPTION_IN_ALL_APPS", ENABLED,
- "Enable option to show keyboard when going to all-apps");
-
// TODO(Block 6): Clean up flags
- public static final BooleanFlag ENABLE_ALL_APPS_SEARCH_IN_TASKBAR = getDebugFlag(270393900,
- "ENABLE_ALL_APPS_SEARCH_IN_TASKBAR", ENABLED,
- "Enables Search box in Taskbar All Apps.");
-
public static final BooleanFlag SECONDARY_DRAG_N_DROP_TO_PIN = getDebugFlag(270395140,
"SECONDARY_DRAG_N_DROP_TO_PIN", DISABLED,
"Enable dragging and dropping to pin apps within secondary display");
@@ -150,28 +135,11 @@
public static final BooleanFlag PROMISE_APPS_IN_ALL_APPS = getDebugFlag(270390012,
"PROMISE_APPS_IN_ALL_APPS", DISABLED, "Add promise icon in all-apps");
- public static final BooleanFlag ENABLE_DEVICE_SEARCH = getReleaseFlag(270390907,
- "ENABLE_DEVICE_SEARCH", ENABLED, "Allows on device search in all apps");
-
- public static final BooleanFlag ENABLE_HIDE_HEADER = getReleaseFlag(270390930,
- "ENABLE_HIDE_HEADER", ENABLED, "Hide header on keyboard before typing in all apps");
-
// Aconfig migration complete for ENABLE_EXPANDING_PAUSE_WORK_BUTTON.
public static final BooleanFlag ENABLE_EXPANDING_PAUSE_WORK_BUTTON = getDebugFlag(270390779,
"ENABLE_EXPANDING_PAUSE_WORK_BUTTON", DISABLED,
"Expand and collapse pause work button while scrolling");
- // Aconfig migration complete for ENABLE_TWOLINE_ALLAPPS.
- public static final BooleanFlag ENABLE_TWOLINE_ALLAPPS = getDebugFlag(270390937,
- "ENABLE_TWOLINE_ALLAPPS", DISABLED, "Enables two line label inside all apps.");
-
- public static final BooleanFlag IME_STICKY_SNACKBAR_EDU = getDebugFlag(270391693,
- "IME_STICKY_SNACKBAR_EDU", ENABLED, "Show sticky IME edu in AllApps");
-
- public static final BooleanFlag FOLDER_NAME_MAJORITY_RANKING = getDebugFlag(270391638,
- "FOLDER_NAME_MAJORITY_RANKING", ENABLED,
- "Suggests folder names based on majority based ranking.");
-
public static final BooleanFlag INJECT_FALLBACK_APP_CORPUS_RESULTS = getReleaseFlag(270391706,
"INJECT_FALLBACK_APP_CORPUS_RESULTS", DISABLED,
"Inject fallback app corpus result when AiAi fails to return it.");
@@ -196,24 +164,7 @@
return ENABLE_APP_PAIRS.get() || com.android.wm.shell.Flags.enableAppPairs();
}
- // TODO(Block 19): Clean up flags
- public static final BooleanFlag SCROLL_TOP_TO_RESET = getReleaseFlag(270395177,
- "SCROLL_TOP_TO_RESET", ENABLED,
- "Bring up IME and focus on input when scroll to top if 'Always show keyboard'"
- + " is enabled or in prefix state");
-
- public static final BooleanFlag ENABLE_SEARCH_UNINSTALLED_APPS = getReleaseFlag(270395269,
- "ENABLE_SEARCH_UNINSTALLED_APPS", ENABLED, "Search uninstalled app results.");
-
// TODO(Block 20): Clean up flags
- public static final BooleanFlag ENABLE_BACK_SWIPE_HOME_ANIMATION = getDebugFlag(270393426,
- "ENABLE_BACK_SWIPE_HOME_ANIMATION", ENABLED,
- "Enables home animation to icon when user swipes back.");
-
- public static final BooleanFlag ENABLE_DYNAMIC_TASKBAR_THRESHOLDS = getDebugFlag(294252473,
- "ENABLE_DYNAMIC_TASKBAR_THRESHOLDS", ENABLED,
- "Enables taskbar thresholds that scale based on screen size.");
-
// Aconfig migration complete for ENABLE_HOME_TRANSITION_LISTENER.
public static final BooleanFlag ENABLE_HOME_TRANSITION_LISTENER = getDebugFlag(306053414,
"ENABLE_HOME_TRANSITION_LISTENER", DISABLED,
@@ -232,18 +183,7 @@
"ENABLE_WIDGET_TRANSITION_FOR_RESIZING", DISABLED,
"Enable widget transition animation when resizing the widgets");
- public static final BooleanFlag PREEMPTIVE_UNFOLD_ANIMATION_START = getDebugFlag(270397209,
- "PREEMPTIVE_UNFOLD_ANIMATION_START", ENABLED,
- "Enables starting the unfold animation preemptively when unfolding, without"
- + "waiting for SystemUI and then merging the SystemUI progress whenever we "
- + "start receiving the events");
-
// TODO(Block 25): Clean up flags
- public static final BooleanFlag ENABLE_NEW_GESTURE_NAV_TUTORIAL = getDebugFlag(270396257,
- "ENABLE_NEW_GESTURE_NAV_TUTORIAL", ENABLED,
- "Enable the redesigned gesture navigation tutorial");
-
- // TODO(Block 26): Clean up flags
public static final BooleanFlag ENABLE_WIDGET_HOST_IN_BACKGROUND = getDebugFlag(270394384,
"ENABLE_WIDGET_HOST_IN_BACKGROUND", ENABLED,
"Enable background widget updates listening for widget holder");
@@ -268,10 +208,6 @@
"SEPARATE_RECENTS_ACTIVITY", DISABLED,
"Uses a separate recents activity instead of using the integrated recents+Launcher UI");
- public static final BooleanFlag ENABLE_ENFORCED_ROUNDED_CORNERS = getReleaseFlag(270393258,
- "ENABLE_ENFORCED_ROUNDED_CORNERS", ENABLED,
- "Enforce rounded corners on all App Widgets");
-
public static final BooleanFlag USE_LOCAL_ICON_OVERRIDES = getDebugFlag(270394973,
"USE_LOCAL_ICON_OVERRIDES", ENABLED,
"Use inbuilt monochrome icons if app doesn't provide one");
@@ -285,20 +221,15 @@
com.android.wm.shell.Flags.enableSplitContextual();
}
- public static final BooleanFlag ENABLE_TRACKPAD_GESTURE = getDebugFlag(271010401,
- "ENABLE_TRACKPAD_GESTURE", ENABLED, "Enables trackpad gesture.");
-
// TODO(Block 29): Clean up flags
+ // Aconfig migration complete for ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT.
public static final BooleanFlag ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT = getDebugFlag(270393897,
"ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT", DISABLED,
"Enables displaying the all apps button in the hotseat.");
- public static final BooleanFlag ENABLE_KEYBOARD_QUICK_SWITCH = getDebugFlag(270396844,
- "ENABLE_KEYBOARD_QUICK_SWITCH", ENABLED, "Enables keyboard quick switching");
-
- public static final BooleanFlag ENABLE_KEYBOARD_TASKBAR_TOGGLE = getDebugFlag(281726846,
- "ENABLE_KEYBOARD_TASKBAR_TOGGLE", ENABLED,
- "Enables keyboard taskbar stash toggling");
+ public static boolean enableAllAppsButtonInHotseat() {
+ return ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT.get() || Flags.enableAllAppsButtonInHotseat();
+ }
// TODO(Block 30): Clean up flags
public static final BooleanFlag USE_SEARCH_REQUEST_TIMEOUT_OVERRIDES = getDebugFlag(270395010,
@@ -317,14 +248,6 @@
return ENABLE_RESPONSIVE_WORKSPACE.get() || Flags.enableResponsiveWorkspace();
}
- // TODO(Block 33): Clean up flags
- public static final BooleanFlag ENABLE_ALL_APPS_RV_PREINFLATION = getDebugFlag(288161355,
- "ENABLE_ALL_APPS_RV_PREINFLATION", ENABLED,
- "Enables preinflating all apps icons to avoid scrolling jank.");
- public static final BooleanFlag ALL_APPS_GONE_VISIBILITY = getDebugFlag(291651514,
- "ALL_APPS_GONE_VISIBILITY", ENABLED,
- "Set all apps container view's hidden visibility to GONE instead of INVISIBLE.");
-
public static BooleanFlag getDebugFlag(
int bugId, String key, BooleanFlag flagState, String description) {
return flagState;
diff --git a/src/com/android/launcher3/dagger/ActivityContextScope.java b/src/com/android/launcher3/dagger/ActivityContextScope.java
new file mode 100644
index 0000000..887f15c
--- /dev/null
+++ b/src/com/android/launcher3/dagger/ActivityContextScope.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.dagger;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import javax.inject.Scope;
+
+/**
+ * Scope annotation for singletons associated with Launcher activity context.
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Scope
+public @interface ActivityContextScope {
+}
diff --git a/src/com/android/launcher3/dagger/ApplicationContext.java b/src/com/android/launcher3/dagger/ApplicationContext.java
new file mode 100644
index 0000000..9a5b08b
--- /dev/null
+++ b/src/com/android/launcher3/dagger/ApplicationContext.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.dagger;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import javax.inject.Qualifier;
+
+/**
+ * Qualifier for Launcher application context.
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Qualifier
+public @interface ApplicationContext {
+}
diff --git a/src/com/android/launcher3/dagger/LauncherAppSingleton.java b/src/com/android/launcher3/dagger/LauncherAppSingleton.java
new file mode 100644
index 0000000..92c00b6
--- /dev/null
+++ b/src/com/android/launcher3/dagger/LauncherAppSingleton.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.dagger;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import javax.inject.Scope;
+
+/**
+ * Scope annotation for singleton items within the LauncherAppComponent.
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Scope
+public @interface LauncherAppSingleton {
+}
diff --git a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
index 3488c95..0a50e8b 100644
--- a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
+++ b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
@@ -16,6 +16,12 @@
package com.android.launcher3.dagger;
+import android.content.Context;
+
+import com.android.launcher3.util.DaggerSingletonTracker;
+
+import dagger.BindsInstance;
+
/**
* Launcher base component for Dagger injection.
*
@@ -25,8 +31,10 @@
* See {@link LauncherAppComponent} for the one actually used by AOSP.
*/
public interface LauncherBaseAppComponent {
+ DaggerSingletonTracker getDaggerSingletonTracker();
/** Builder for LauncherBaseAppComponent. */
interface Builder {
+ @BindsInstance Builder appContext(@ApplicationContext Context context);
LauncherBaseAppComponent build();
}
}
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index 85eb39b..a3cfe5c 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -68,7 +68,7 @@
import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.pm.PinRequestHelper;
import com.android.launcher3.util.ApiWrapper;
-import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.ApplicationInfoWrapper;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.views.AbstractSlideInView;
import com.android.launcher3.views.BaseDragLayer;
@@ -164,8 +164,8 @@
finish();
return;
}
- ApplicationInfo info = PackageManagerHelper.INSTANCE.get(this)
- .getApplicationInfo(targetApp.packageName, targetApp.user, 0);
+ ApplicationInfo info = new ApplicationInfoWrapper(
+ this, targetApp.packageName, targetApp.user).getInfo();
if (info == null) {
finish();
return;
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 8b1f42b..a24f3ff 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -121,7 +121,7 @@
@Override
public void recreateControllers() {
- mControllers = mActivity.createTouchControllers();
+ mControllers = mContainer.createTouchControllers();
}
public ViewGroupFocusHelper getFocusIndicatorHelper() {
@@ -134,15 +134,15 @@
}
private boolean isEventOverAccessibleDropTargetBar(MotionEvent ev) {
- return isInAccessibleDrag() && isEventOverView(mActivity.getDropTargetBar(), ev);
+ return isInAccessibleDrag() && isEventOverView(mContainer.getDropTargetBar(), ev);
}
@Override
public boolean onInterceptHoverEvent(MotionEvent ev) {
- if (mActivity == null || mActivity.getWorkspace() == null) {
+ if (mContainer == null || mContainer.getWorkspace() == null) {
return false;
}
- AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
+ AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mContainer);
if (!(topView instanceof Folder)) {
return false;
} else {
@@ -197,7 +197,7 @@
private boolean isInAccessibleDrag() {
- return mActivity.getAccessibilityDelegate().isInAccessibleDrag();
+ return mContainer.getAccessibilityDelegate().isInAccessibleDrag();
}
@Override
@@ -210,12 +210,12 @@
@Override
public void addChildrenForAccessibility(ArrayList<View> childrenForAccessibility) {
- View topView = AbstractFloatingView.getTopOpenViewWithType(mActivity,
+ View topView = AbstractFloatingView.getTopOpenViewWithType(mContainer,
AbstractFloatingView.TYPE_ACCESSIBLE);
if (topView != null) {
addAccessibleChildToList(topView, childrenForAccessibility);
if (isInAccessibleDrag()) {
- addAccessibleChildToList(mActivity.getDropTargetBar(), childrenForAccessibility);
+ addAccessibleChildToList(mContainer.getDropTargetBar(), childrenForAccessibility);
}
} else {
super.addChildrenForAccessibility(childrenForAccessibility);
@@ -420,14 +420,14 @@
public void onViewAdded(View child) {
super.onViewAdded(child);
updateChildIndices();
- mActivity.onDragLayerHierarchyChanged();
+ mContainer.onDragLayerHierarchyChanged();
}
@Override
public void onViewRemoved(View child) {
super.onViewRemoved(child);
updateChildIndices();
- mActivity.onDragLayerHierarchyChanged();
+ mContainer.onDragLayerHierarchyChanged();
}
@Override
diff --git a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
index 374c07b..27ec838 100644
--- a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
+++ b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
@@ -16,11 +16,13 @@
package com.android.launcher3.graphics;
import static com.android.launcher3.LauncherPrefs.THEMED_ICONS;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.Themes.isThemedIconEnabled;
import android.content.ContentProvider;
import android.content.ContentValues;
+import android.content.Context;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.MatrixCursor;
@@ -28,19 +30,27 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
-import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.Message;
import android.os.Messenger;
import android.text.TextUtils;
-import android.util.ArrayMap;
import android.util.Log;
-import android.util.Pair;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.InvariantDeviceProfile.GridOption;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherPrefs;
+import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.util.Executors;
+import com.android.launcher3.util.Preconditions;
+import com.android.launcher3.util.RunnableList;
+import com.android.systemui.shared.Flags;
+
+import java.util.Collections;
+import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.concurrent.ExecutionException;
/**
* Exposes various launcher grid options and allows the caller to change them.
@@ -86,11 +96,9 @@
private static final int MESSAGE_ID_UPDATE_PREVIEW = 1337;
private static final int MESSAGE_ID_UPDATE_GRID = 7414;
- /**
- * Here we use the IBinder and the screen ID as the key of the active previews.
- */
- private final ArrayMap<Pair<IBinder, Integer>, PreviewLifecycleObserver> mActivePreviews =
- new ArrayMap<>();
+ // Set of all active previews used to track duplicate memory allocations
+ private final Set<PreviewLifecycleObserver> mActivePreviews =
+ Collections.newSetFromMap(new WeakHashMap<>());
@Override
public boolean onCreate() {
@@ -144,14 +152,20 @@
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
- switch (uri.getPath()) {
+ String path = uri.getPath();
+ Context context = getContext();
+ if (path == null || context == null) {
+ return 0;
+ }
+ switch (path) {
case KEY_DEFAULT_GRID: {
String gridName = values.getAsString(KEY_NAME);
- InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(getContext());
+ InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(context);
// Verify that this is a valid grid option
GridOption match = null;
- for (GridOption option : idp.parseAllGridOptions(getContext())) {
- if (option.name.equals(gridName)) {
+ for (GridOption option : idp.parseAllGridOptions(context)) {
+ String name = option.name;
+ if (name != null && name.equals(gridName)) {
match = option;
break;
}
@@ -160,15 +174,23 @@
return 0;
}
- idp.setCurrentGrid(getContext(), gridName);
- getContext().getContentResolver().notifyChange(uri, null);
+ idp.setCurrentGrid(context, gridName);
+ if (Flags.newCustomizationPickerUi()) {
+ try {
+ // Wait for device profile to be fully reloaded and applied to the launcher
+ loadModelSync(context);
+ } catch (ExecutionException | InterruptedException e) {
+ Log.e(TAG, "Fail to load model", e);
+ }
+ }
+ context.getContentResolver().notifyChange(uri, null);
return 1;
}
case ICON_THEMED:
case SET_ICON_THEMED: {
- LauncherPrefs.get(getContext())
+ LauncherPrefs.get(context)
.put(THEMED_ICONS, values.getAsBoolean(BOOLEAN_VALUE));
- getContext().getContentResolver().notifyChange(uri, null);
+ context.getContentResolver().notifyChange(uri, null);
return 1;
}
default:
@@ -176,6 +198,23 @@
}
}
+ /**
+ * Loads the model in memory synchronously
+ */
+ private void loadModelSync(Context context) throws ExecutionException, InterruptedException {
+ Preconditions.assertNonUiThread();
+ BgDataModel.Callbacks emptyCallbacks = new BgDataModel.Callbacks() { };
+ LauncherModel launcherModel = LauncherAppState.getInstance(context).getModel();
+ MAIN_EXECUTOR.submit(
+ () -> launcherModel.addCallbacksAndLoad(emptyCallbacks)
+ ).get();
+
+ Executors.MODEL_EXECUTOR.submit(() -> { }).get();
+ MAIN_EXECUTOR.submit(
+ () -> launcherModel.removeCallbacks(emptyCallbacks)
+ ).get();
+ }
+
@Override
public Bundle call(String method, String arg, Bundle extras) {
if (getContext().checkPermission("android.permission.BIND_WALLPAPER",
@@ -191,16 +230,19 @@
}
private synchronized Bundle getPreview(Bundle request) {
- PreviewLifecycleObserver observer = null;
+ RunnableList lifeCycleTracker = new RunnableList();
try {
- PreviewSurfaceRenderer renderer = new PreviewSurfaceRenderer(getContext(), request);
+ PreviewSurfaceRenderer renderer = new PreviewSurfaceRenderer(
+ getContext(), lifeCycleTracker, request);
+ PreviewLifecycleObserver observer =
+ new PreviewLifecycleObserver(lifeCycleTracker, renderer);
- observer = new PreviewLifecycleObserver(renderer);
- // Destroy previous
- destroyObserver(mActivePreviews.get(observer.getIdentifier()));
- mActivePreviews.put(observer.getIdentifier(), observer);
+ // Destroy previous renderers to avoid any duplicate memory
+ mActivePreviews.stream().filter(observer::isSameRenderer).forEach(o ->
+ MAIN_EXECUTOR.execute(o.lifeCycleTracker::executeAllAndDestroy));
renderer.loadAsync();
+ lifeCycleTracker.add(() -> renderer.getHostToken().unlinkToDeath(observer, 0));
renderer.getHostToken().linkToDeath(observer, 0);
Bundle result = new Bundle();
@@ -214,33 +256,21 @@
return result;
} catch (Exception e) {
Log.e(TAG, "Unable to generate preview", e);
- if (observer != null) {
- destroyObserver(observer);
- }
+ MAIN_EXECUTOR.execute(lifeCycleTracker::executeAllAndDestroy);
return null;
}
}
- private synchronized void destroyObserver(PreviewLifecycleObserver observer) {
- if (observer == null || observer.destroyed) {
- return;
- }
- observer.destroyed = true;
- observer.renderer.getHostToken().unlinkToDeath(observer, 0);
- Executors.MAIN_EXECUTOR.execute(observer.renderer::destroy);
- PreviewLifecycleObserver cached = mActivePreviews.get(observer.getIdentifier());
- if (cached == observer) {
- mActivePreviews.remove(observer.getIdentifier());
- }
- }
+ private static class PreviewLifecycleObserver implements Handler.Callback, DeathRecipient {
- private class PreviewLifecycleObserver implements Handler.Callback, DeathRecipient {
-
+ public final RunnableList lifeCycleTracker;
public final PreviewSurfaceRenderer renderer;
public boolean destroyed = false;
- PreviewLifecycleObserver(PreviewSurfaceRenderer renderer) {
+ PreviewLifecycleObserver(RunnableList lifeCycleTracker, PreviewSurfaceRenderer renderer) {
+ this.lifeCycleTracker = lifeCycleTracker;
this.renderer = renderer;
+ lifeCycleTracker.add(() -> destroyed = true);
}
@Override
@@ -260,7 +290,9 @@
}
break;
default:
- destroyObserver(this);
+ // Unknown command, destroy lifecycle
+ Log.d(TAG, "Unknown preview command: " + message.what + ", destroying preview");
+ MAIN_EXECUTOR.execute(lifeCycleTracker::executeAllAndDestroy);
break;
}
@@ -269,16 +301,16 @@
@Override
public void binderDied() {
- destroyObserver(this);
+ MAIN_EXECUTOR.execute(lifeCycleTracker::executeAllAndDestroy);
}
/**
- * Returns a key that should make the PreviewSurfaceRenderer unique and if two of them have
- * the same key they will be treated as the same PreviewSurfaceRenderer. Primary this is
- * used to prevent memory leaks by removing the old PreviewSurfaceRenderer.
+ * Two renderers are considered same if they have the same host token and display Id
*/
- public Pair<IBinder, Integer> getIdentifier() {
- return new Pair<>(renderer.getHostToken(), renderer.getDisplayId());
+ public boolean isSameRenderer(PreviewLifecycleObserver plo) {
+ return plo != null
+ && plo.renderer.getHostToken().equals(renderer.getHostToken())
+ && plo.renderer.getDisplayId() == renderer.getDisplayId();
}
}
}
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index 2408955..40c0cc6 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -23,6 +23,7 @@
import static com.android.launcher3.BubbleTextView.DISPLAY_TASKBAR;
import static com.android.launcher3.BubbleTextView.DISPLAY_WORKSPACE;
import static com.android.launcher3.DeviceProfile.DEFAULT_SCALE;
+import static com.android.launcher3.Hotseat.ALPHA_CHANNEL_PREVIEW_RENDERER;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
import static com.android.launcher3.Utilities.SHOULD_SHOW_FIRST_PAGE_WIDGET;
import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
@@ -68,7 +69,6 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.WorkspaceLayoutManager;
import com.android.launcher3.apppairs.AppPairIcon;
@@ -206,15 +206,12 @@
mWorkspaceScreens.put(Workspace.SECOND_SCREEN_ID, rightPanel);
}
- if (Utilities.ATLEAST_S) {
- WallpaperColors wallpaperColors = wallpaperColorsOverride != null
- ? wallpaperColorsOverride
- : WallpaperManager.getInstance(context).getWallpaperColors(FLAG_SYSTEM);
- mWallpaperColorResources = wallpaperColors != null ? LocalColorExtractor.newInstance(
- context).generateColorsOverride(wallpaperColors) : null;
- } else {
- mWallpaperColorResources = null;
- }
+ WallpaperColors wallpaperColors = wallpaperColorsOverride != null
+ ? wallpaperColorsOverride
+ : WallpaperManager.getInstance(context).getWallpaperColors(FLAG_SYSTEM);
+ mWallpaperColorResources = wallpaperColors != null
+ ? LocalColorExtractor.newInstance(context).generateColorsOverride(wallpaperColors)
+ : null;
mAppWidgetHost = new LauncherPreviewAppWidgetHost(context);
}
@@ -320,12 +317,12 @@
mUiHandler.post(() -> {
if (mDp.isTaskbarPresent) {
// hotseat icons on bottom
- mHotseat.setIconsAlpha(hide ? 0 : 1);
+ mHotseat.setIconsAlpha(hide ? 0 : 1, ALPHA_CHANNEL_PREVIEW_RENDERER);
if (mDp.isQsbInline) {
- mHotseat.setQsbAlpha(hide ? 0 : 1);
+ mHotseat.setQsbAlpha(hide ? 0 : 1, ALPHA_CHANNEL_PREVIEW_RENDERER);
}
} else {
- mHotseat.setQsbAlpha(hide ? 0 : 1);
+ mHotseat.setQsbAlpha(hide ? 0 : 1, ALPHA_CHANNEL_PREVIEW_RENDERER);
}
});
}
diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
index 56c4ca4..1b23d75 100644
--- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
+++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
@@ -91,7 +91,7 @@
private final int mDisplayId;
private final Display mDisplay;
private final WallpaperColors mWallpaperColors;
- private final RunnableList mOnDestroyCallbacks = new RunnableList();
+ private final RunnableList mLifeCycleTracker;
private final SurfaceControlViewHost mSurfaceControlViewHost;
@@ -100,8 +100,10 @@
private boolean mHideQsb;
@Nullable private FrameLayout mViewRoot = null;
- public PreviewSurfaceRenderer(Context context, Bundle bundle) throws Exception {
+ public PreviewSurfaceRenderer(
+ Context context, RunnableList lifecycleTracker, Bundle bundle) throws Exception {
mContext = context;
+ mLifeCycleTracker = lifecycleTracker;
mGridName = bundle.getString("name");
bundle.remove("name");
if (mGridName == null) {
@@ -120,11 +122,13 @@
throw new IllegalArgumentException("Display ID does not match any displays.");
}
- mSurfaceControlViewHost = MAIN_EXECUTOR.submit(() ->
- new SurfaceControlViewHost(mContext, context.getSystemService(DisplayManager.class)
- .getDisplay(DEFAULT_DISPLAY), mHostToken)
- ).get(5, TimeUnit.SECONDS);
- mOnDestroyCallbacks.add(mSurfaceControlViewHost::release);
+ mSurfaceControlViewHost = MAIN_EXECUTOR.submit(() -> new MySurfaceControlViewHost(
+ mContext,
+ context.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY),
+ mHostToken,
+ mLifeCycleTracker))
+ .get(5, TimeUnit.SECONDS);
+ mLifeCycleTracker.add(this::destroy);
}
public int getDisplayId() {
@@ -139,25 +143,18 @@
return mSurfaceControlViewHost.getSurfacePackage();
}
- /**
- * Destroys the preview and all associated data
- */
- @UiThread
- public void destroy() {
+ private void destroy() {
mDestroyed = true;
- mOnDestroyCallbacks.executeAllAndDestroy();
}
/**
* A function that queries for the launcher app widget span info
*
- * @param context The context to get the content resolver from, should be related to launcher
* @return A SparseArray with the app widget id being the key and the span info being the values
*/
@WorkerThread
@Nullable
- public SparseArray<Size> getLoadedLauncherWidgetInfo(
- @NonNull final Context context) {
+ public SparseArray<Size> getLoadedLauncherWidgetInfo() {
final SparseArray<Size> widgetInfo = new SparseArray<>();
final String query = LauncherSettings.Favorites.ITEM_TYPE + " = "
+ LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
@@ -276,13 +273,11 @@
}
loadWorkspace(new ArrayList<>(), query, null, null);
- final SparseArray<Size> spanInfo =
- getLoadedLauncherWidgetInfo(previewContext.getBaseContext());
-
+ final SparseArray<Size> spanInfo = getLoadedLauncherWidgetInfo();
MAIN_EXECUTOR.execute(() -> {
renderView(previewContext, mBgDataModel, mWidgetProvidersMap, spanInfo,
idp);
- mOnDestroyCallbacks.add(previewContext::onDestroy);
+ mLifeCycleTracker.add(previewContext::onDestroy);
});
}
}.run();
@@ -355,4 +350,24 @@
mViewRoot.addView(view);
}
}
+
+ private static class MySurfaceControlViewHost extends SurfaceControlViewHost {
+
+ private final RunnableList mLifecycleTracker;
+
+ MySurfaceControlViewHost(Context context, Display display, IBinder hostToken,
+ RunnableList lifeCycleTracker) {
+ super(context, display, hostToken);
+ mLifecycleTracker = lifeCycleTracker;
+ mLifecycleTracker.add(this::release);
+ }
+
+ @Override
+ public void release() {
+ super.release();
+ // RunnableList ensures that the callback is only called once
+ MAIN_EXECUTOR.execute(mLifecycleTracker::executeAllAndDestroy);
+ }
+ }
+
}
diff --git a/src/com/android/launcher3/graphics/SysUiScrim.java b/src/com/android/launcher3/graphics/SysUiScrim.java
index 077ddfc..d59fc19 100644
--- a/src/com/android/launcher3/graphics/SysUiScrim.java
+++ b/src/com/android/launcher3/graphics/SysUiScrim.java
@@ -32,10 +32,10 @@
import androidx.annotation.ColorInt;
import androidx.annotation.VisibleForTesting;
-import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatedFloat;
+import com.android.launcher3.statemanager.StatefulContainer;
import com.android.launcher3.testing.shared.ResourceUtils;
import com.android.launcher3.util.ScreenOnTracker;
import com.android.launcher3.util.ScreenOnTracker.ScreenOnListener;
@@ -84,7 +84,7 @@
private final int mBottomMaskHeight;
private final View mRoot;
- private final BaseDraggingActivity mActivity;
+ private final StatefulContainer mContainer;
private final boolean mHideSysUiScrim;
private boolean mSkipScrimAnimationForTest = false;
@@ -94,8 +94,8 @@
public SysUiScrim(View view) {
mRoot = view;
- mActivity = BaseDraggingActivity.fromContext(view.getContext());
- DisplayMetrics dm = mActivity.getResources().getDisplayMetrics();
+ mContainer = StatefulContainer.fromContext(view.getContext());
+ DisplayMetrics dm = mContainer.getContext().getResources().getDisplayMetrics();
mTopMaskHeight = ResourceUtils.pxFromDp(TOP_MASK_HEIGHT_DP, dm);
mBottomMaskHeight = ResourceUtils.pxFromDp(BOTTOM_MASK_HEIGHT_DP, dm);
@@ -130,7 +130,7 @@
ObjectAnimator oa = mSysUiAnimMultiplier.animateToValue(1);
oa.setDuration(600);
- oa.setStartDelay(mActivity.getWindow().getTransitionBackgroundFadeDuration());
+ oa.setStartDelay(mContainer.getWindow().getTransitionBackgroundFadeDuration());
oa.start();
mAnimateScrimOnNextDraw = false;
}
@@ -166,19 +166,19 @@
* horizontal
*/
public void onInsetsChanged(Rect insets) {
- DeviceProfile dp = mActivity.getDeviceProfile();
+ DeviceProfile dp = mContainer.getDeviceProfile();
mDrawTopScrim = insets.top > 0;
mDrawBottomScrim = !dp.isVerticalBarLayout() && !dp.isGestureMode && !dp.isTaskbarPresent;
}
@Override
public void onViewAttachedToWindow(View view) {
- ScreenOnTracker.INSTANCE.get(mActivity).addListener(mScreenOnListener);
+ ScreenOnTracker.INSTANCE.get(mContainer.getContext()).addListener(mScreenOnListener);
}
@Override
public void onViewDetachedFromWindow(View view) {
- ScreenOnTracker.INSTANCE.get(mActivity).removeListener(mScreenOnListener);
+ ScreenOnTracker.INSTANCE.get(mContainer.getContext()).removeListener(mScreenOnListener);
}
/**
@@ -213,7 +213,7 @@
}
private Bitmap createDitheredAlphaMask(int height, @ColorInt int[] colors, float[] positions) {
- DisplayMetrics dm = mActivity.getResources().getDisplayMetrics();
+ DisplayMetrics dm = mContainer.getContext().getResources().getDisplayMetrics();
int width = ResourceUtils.pxFromDp(ALPHA_MASK_BITMAP_WIDTH_DP, dm);
Bitmap dst = Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8);
Canvas c = new Canvas(dst);
diff --git a/src/com/android/launcher3/icons/ComponentWithLabel.java b/src/com/android/launcher3/icons/ComponentWithLabel.java
deleted file mode 100644
index 30575fc..0000000
--- a/src/com/android/launcher3/icons/ComponentWithLabel.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.icons;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.os.UserHandle;
-
-import androidx.annotation.NonNull;
-
-import com.android.launcher3.icons.cache.CachingLogic;
-
-public interface ComponentWithLabel {
-
- ComponentName getComponent();
-
- UserHandle getUser();
-
- CharSequence getLabel(PackageManager pm);
-
-
- class ComponentCachingLogic<T extends ComponentWithLabel> implements CachingLogic<T> {
-
- private final PackageManager mPackageManager;
- private final boolean mAddToMemCache;
-
- public ComponentCachingLogic(Context context, boolean addToMemCache) {
- mPackageManager = context.getPackageManager();
- mAddToMemCache = addToMemCache;
- }
-
- @Override
- @NonNull
- public ComponentName getComponent(@NonNull T object) {
- return object.getComponent();
- }
-
- @NonNull
- @Override
- public UserHandle getUser(@NonNull T object) {
- return object.getUser();
- }
-
- @NonNull
- @Override
- public CharSequence getLabel(@NonNull T object) {
- return object.getLabel(mPackageManager);
- }
-
- @NonNull
- @Override
- public BitmapInfo loadIcon(@NonNull Context context, @NonNull T object) {
- return BitmapInfo.LOW_RES_INFO;
- }
-
- @Override
- public boolean addToMemCache() {
- return mAddToMemCache;
- }
- }
-}
diff --git a/src/com/android/launcher3/icons/ComponentWithLabelAndIcon.java b/src/com/android/launcher3/icons/ComponentWithLabelAndIcon.java
deleted file mode 100644
index 0a52dd7..0000000
--- a/src/com/android/launcher3/icons/ComponentWithLabelAndIcon.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2020 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.icons;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-
-import androidx.annotation.NonNull;
-
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.icons.BaseIconFactory.IconOptions;
-
-/**
- * Extension of ComponentWithLabel to also support loading icons
- */
-public interface ComponentWithLabelAndIcon extends ComponentWithLabel {
-
- /**
- * Provide an icon for this object
- */
- Drawable getFullResIcon(IconCache cache);
-
- class ComponentWithIconCachingLogic extends ComponentCachingLogic<ComponentWithLabelAndIcon> {
-
- public ComponentWithIconCachingLogic(Context context, boolean addToMemCache) {
- super(context, addToMemCache);
- }
-
- @NonNull
- @Override
- public BitmapInfo loadIcon(@NonNull Context context,
- @NonNull ComponentWithLabelAndIcon object) {
- Drawable d = object.getFullResIcon(LauncherAppState.getInstance(context)
- .getIconCache());
- if (d == null) {
- return super.loadIcon(context, object);
- }
- try (LauncherIcons li = LauncherIcons.obtain(context)) {
- return li.createBadgedIconBitmap(d, new IconOptions().setUser(object.getUser()));
- }
- }
- }
-}
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index 44e448e..53a4039 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -54,9 +54,10 @@
import com.android.launcher3.Flags;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Utilities;
-import com.android.launcher3.icons.ComponentWithLabel.ComponentCachingLogic;
import com.android.launcher3.icons.cache.BaseIconCache;
+import com.android.launcher3.icons.cache.CachedObjectCachingLogic;
import com.android.launcher3.icons.cache.CachingLogic;
+import com.android.launcher3.icons.cache.LauncherActivityCachingLogic;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.IconRequestInfo;
@@ -102,7 +103,6 @@
private final LauncherApps mLauncherApps;
private final UserCache mUserManager;
private final InstantAppResolver mInstantAppResolver;
- private final IconProvider mIconProvider;
private final CancellableTask mCancelledTask;
private final SparseArray<BitmapInfo> mWidgetCategoryBitmapInfos;
@@ -112,14 +112,14 @@
public IconCache(Context context, InvariantDeviceProfile idp, String dbFileName,
IconProvider iconProvider) {
super(context, dbFileName, MODEL_EXECUTOR.getLooper(),
- idp.fillResIconDpi, idp.iconBitmapSize, true /* inMemoryCache */);
- mComponentWithLabelCachingLogic = new ComponentCachingLogic(context, false);
- mLauncherActivityInfoCachingLogic = LauncherActivityCachingLogic.newInstance(context);
+ idp.fillResIconDpi, idp.iconBitmapSize, true /* inMemoryCache */, iconProvider);
+ mComponentWithLabelCachingLogic = new CachedObjectCachingLogic(
+ context, false /* loadIcons */, false /* addToMemCache */);
+ mLauncherActivityInfoCachingLogic = LauncherActivityCachingLogic.INSTANCE;
mShortcutCachingLogic = new ShortcutCachingLogic();
mLauncherApps = mContext.getSystemService(LauncherApps.class);
mUserManager = UserCache.INSTANCE.get(mContext);
mInstantAppResolver = InstantAppResolver.newInstance(mContext);
- mIconProvider = iconProvider;
mWidgetCategoryBitmapInfos = new SparseArray<>();
mCancelledTask = new CancellableTask(() -> null, MAIN_EXECUTOR, c -> { });
@@ -224,22 +224,10 @@
* Updates {@param application} only if a valid entry is found.
*/
public synchronized void updateTitleAndIcon(AppInfo application) {
- boolean preferPackageIcon = application.isArchived();
CacheEntry entry = cacheLocked(application.componentName,
application.user, () -> null, mLauncherActivityInfoCachingLogic,
- false, application.usingLowResIcon());
- if (entry.bitmap == null || isDefaultIcon(entry.bitmap, application.user)) {
- return;
- }
-
- if (preferPackageIcon) {
- String packageName = application.getTargetPackage();
- CacheEntry packageEntry =
- cacheLocked(new ComponentName(packageName, packageName + EMPTY_CLASS_NAME),
- application.user, () -> null, mLauncherActivityInfoCachingLogic,
- true, application.usingLowResIcon());
- applyPackageEntry(packageEntry, application, entry);
- } else {
+ application.usingLowResIcon() ? LookupFlag.USE_LOW_RES : LookupFlag.DEFAULT);
+ if (entry.bitmap != null || !isDefaultIcon(entry.bitmap, application.user)) {
applyCacheEntry(entry, application);
}
}
@@ -253,8 +241,7 @@
boolean isAppArchived = Flags.enableSupportForArchiving() && activityInfo != null
&& activityInfo.getActivityInfo().isArchived;
// If we already have activity info, no need to use package icon
- getTitleAndIcon(info, () -> activityInfo, isAppArchived, useLowResIcon,
- isAppArchived);
+ getTitleAndIcon(info, () -> activityInfo, isAppArchived, useLowResIcon);
}
/**
@@ -271,7 +258,7 @@
public <T extends ItemInfoWithIcon> void getShortcutIcon(T info, ShortcutInfo si,
@NonNull Predicate<T> fallbackIconCheck) {
BitmapInfo bitmapInfo = cacheLocked(ShortcutKey.fromInfo(si).componentName,
- si.getUserHandle(), () -> si, mShortcutCachingLogic, false, false).bitmap;
+ si.getUserHandle(), () -> si, mShortcutCachingLogic, LookupFlag.DEFAULT).bitmap;
if (bitmapInfo.isNullOrLowRes()) {
bitmapInfo = getDefaultIcon(si.getUserHandle());
}
@@ -333,14 +320,16 @@
} else {
Intent intent = info.getIntent();
getTitleAndIcon(info, () -> mLauncherApps.resolveActivity(intent, info.user),
- true, useLowResIcon, info.isArchived());
+ true, useLowResIcon);
}
}
+ /**
+ * Loads and returns the icon for the provided object without adding it to memCache
+ */
public synchronized String getTitleNoCache(ComponentWithLabel info) {
CacheEntry entry = cacheLocked(info.getComponent(), info.getUser(), () -> info,
- mComponentWithLabelCachingLogic, false /* usePackageIcon */,
- true /* useLowResIcon */);
+ mComponentWithLabelCachingLogic, LookupFlag.USE_LOW_RES);
return Utilities.trim(entry.title);
}
@@ -351,40 +340,15 @@
@NonNull ItemInfoWithIcon infoInOut,
@NonNull Supplier<LauncherActivityInfo> activityInfoProvider,
boolean usePkgIcon, boolean useLowResIcon) {
+ int lookupFlags = LookupFlag.DEFAULT;
+ if (usePkgIcon) lookupFlags |= LookupFlag.USE_PACKAGE_ICON;
+ if (useLowResIcon) lookupFlags |= LookupFlag.USE_LOW_RES;
CacheEntry entry = cacheLocked(infoInOut.getTargetComponent(), infoInOut.user,
- activityInfoProvider, mLauncherActivityInfoCachingLogic, usePkgIcon,
- useLowResIcon);
+ activityInfoProvider, mLauncherActivityInfoCachingLogic, lookupFlags);
applyCacheEntry(entry, infoInOut);
}
/**
- * Fill in {@param mWorkspaceItemInfo} with the icon and label for {@param info}
- */
- public synchronized void getTitleAndIcon(
- @NonNull ItemInfoWithIcon infoInOut,
- @NonNull Supplier<LauncherActivityInfo> activityInfoProvider,
- boolean usePkgIcon, boolean useLowResIcon, boolean preferPackageEntry) {
- CacheEntry entry = cacheLocked(infoInOut.getTargetComponent(), infoInOut.user,
- activityInfoProvider, mLauncherActivityInfoCachingLogic, usePkgIcon,
- useLowResIcon);
- if (preferPackageEntry) {
- String packageName = infoInOut.getTargetPackage();
- CacheEntry packageEntry = cacheLocked(
- new ComponentName(packageName, packageName + EMPTY_CLASS_NAME),
- infoInOut.user, activityInfoProvider, mLauncherActivityInfoCachingLogic,
- usePkgIcon, useLowResIcon);
- applyPackageEntry(packageEntry, infoInOut, entry);
- } else if (useLowResIcon || !entry.bitmap.isNullOrLowRes()
- || infoInOut.bitmap.isNullOrLowRes()) {
- // Only use cache entry if it will not downgrade the current bitmap in infoInOut
- applyCacheEntry(entry, infoInOut);
- } else {
- Log.d(TAG, "getTitleAndIcon: Cache entry bitmap was a downgrade of existing bitmap"
- + " in ItemInfo. Skipping.");
- }
- }
-
- /**
* Creates an sql cursor for a query of a set of ItemInfoWithIcon icons and titles.
*
* @param iconRequestInfos List of IconRequestInfos representing titles and icons to query.
@@ -482,9 +446,8 @@
/* user = */ sectionKey.first,
() -> duplicateIconRequests.get(0).launcherActivityInfo,
mLauncherActivityInfoCachingLogic,
- c,
- /* usePackageIcon= */ false,
- /* useLowResIcons = */ sectionKey.second);
+ sectionKey.second ? LookupFlag.USE_LOW_RES : LookupFlag.DEFAULT,
+ c);
for (IconRequestInfo<T> iconRequest : duplicateIconRequests) {
applyCacheEntry(entry, iconRequest.itemInfo);
@@ -600,24 +563,30 @@
info.title = Utilities.trim(entry.title);
info.contentDescription = entry.contentDescription;
info.bitmap = entry.bitmap;
+ // Clear any previously set appTitle, if the packageOverride is no longer valid
+ info.appTitle = null;
if (entry.bitmap == null) {
// TODO: entry.bitmap can never be null, so this should not happen at all.
Log.wtf(TAG, "Cannot find bitmap from the cache, default icon was loaded.");
info.bitmap = getDefaultIcon(info.user);
}
- }
- protected void applyPackageEntry(@NonNull final CacheEntry packageEntry,
- @NonNull final ItemInfoWithIcon info, @NonNull final CacheEntry fallbackEntry) {
+ // apply package override
+ if (!Flags.enableSupportForArchiving() || !info.isArchived()) {
+ return;
+ }
+ String targetPackage = info.getTargetPackage();
+ if (targetPackage == null) {
+ return;
+ }
+ CacheEntry packageEntry = getInMemoryPackageEntryLocked(targetPackage, info.user);
+ if (packageEntry == null || packageEntry.bitmap.isLowRes()) {
+ return;
+ }
+ info.appTitle = Utilities.trim(info.title);
info.title = Utilities.trim(packageEntry.title);
- info.appTitle = Utilities.trim(fallbackEntry.title);
info.contentDescription = packageEntry.contentDescription;
info.bitmap = packageEntry.bitmap;
- if (packageEntry.bitmap == null) {
- // TODO: entry.bitmap can never be null, so this should not happen at all.
- Log.wtf(TAG, "Cannot find bitmap from the cache, default icon was loaded.");
- info.bitmap = getDefaultIcon(info.user);
- }
}
public Drawable getFullResIcon(LauncherActivityInfo info) {
@@ -629,12 +598,6 @@
info.getAppLabel());
}
- @Override
- @NonNull
- protected String getIconSystemState(String packageName) {
- return mIconProvider.getSystemStateForPackage(mSystemState, packageName);
- }
-
/**
* Interface for receiving itemInfo with high-res icon.
*/
diff --git a/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java b/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java
deleted file mode 100644
index de2269c..0000000
--- a/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.icons;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.LauncherActivityInfo;
-import android.os.Build;
-import android.os.UserHandle;
-
-import androidx.annotation.NonNull;
-
-import com.android.launcher3.Flags;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.R;
-import com.android.launcher3.icons.BaseIconFactory.IconOptions;
-import com.android.launcher3.icons.cache.CachingLogic;
-import com.android.launcher3.util.ResourceBasedOverride;
-
-/**
- * Caching logic for LauncherActivityInfo.
- */
-public class LauncherActivityCachingLogic
- implements CachingLogic<LauncherActivityInfo>, ResourceBasedOverride {
-
- /**
- * Creates and returns a new instance
- */
- public static LauncherActivityCachingLogic newInstance(Context context) {
- return Overrides.getObject(LauncherActivityCachingLogic.class, context,
- R.string.launcher_activity_logic_class);
- }
-
- @NonNull
- @Override
- public ComponentName getComponent(@NonNull LauncherActivityInfo object) {
- return object.getComponentName();
- }
-
- @NonNull
- @Override
- public UserHandle getUser(@NonNull LauncherActivityInfo object) {
- return object.getUser();
- }
-
- @NonNull
- @Override
- public CharSequence getLabel(@NonNull LauncherActivityInfo object) {
- return object.getLabel();
- }
-
- @NonNull
- @Override
- public BitmapInfo loadIcon(@NonNull Context context, @NonNull LauncherActivityInfo object) {
- try (LauncherIcons li = LauncherIcons.obtain(context)) {
- IconOptions iconOptions = new IconOptions().setUser(object.getUser());
- iconOptions.mIsArchived = Flags.useNewIconForArchivedApps()
- && Build.VERSION.SDK_INT >= 35
- && object.getActivityInfo().isArchived;
- return li.createBadgedIconBitmap(
- LauncherAppState.getInstance(context)
- .getIconProvider()
- .getIcon(object, li.mFillResIconDpi),
- iconOptions
- );
- }
- }
-}
diff --git a/src/com/android/launcher3/icons/Legacy.kt b/src/com/android/launcher3/icons/Legacy.kt
new file mode 100644
index 0000000..3bf3bb2
--- /dev/null
+++ b/src/com/android/launcher3/icons/Legacy.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.icons
+
+import com.android.launcher3.icons.cache.CachedObject
+
+/**
+ * This files contains some definitions used during refactoring to avoid breaking changes.
+ *
+ * TODO(b/366237794) remove this file once refactoring is complete
+ */
+
+/** Temporary interface to allow easier refactoring */
+interface ComponentWithLabel : CachedObject<IconCache>
+
+/** Temporary interface to allow easier refactoring */
+interface ComponentWithLabelAndIcon : ComponentWithLabel
diff --git a/src/com/android/launcher3/icons/ShortcutCachingLogic.java b/src/com/android/launcher3/icons/ShortcutCachingLogic.java
index f40eda6..7bb39e1 100644
--- a/src/com/android/launcher3/icons/ShortcutCachingLogic.java
+++ b/src/com/android/launcher3/icons/ShortcutCachingLogic.java
@@ -33,6 +33,7 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.icons.BaseIconFactory.IconOptions;
+import com.android.launcher3.icons.cache.BaseIconCache;
import com.android.launcher3.icons.cache.CachingLogic;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.Themes;
@@ -72,7 +73,8 @@
@NonNull
@Override
- public BitmapInfo loadIcon(@NonNull Context context, @NonNull ShortcutInfo info) {
+ public BitmapInfo loadIcon(@NonNull Context context, @NonNull BaseIconCache cache,
+ @NonNull ShortcutInfo info) {
try (LauncherIcons li = LauncherIcons.obtain(context)) {
Drawable unbadgedDrawable = ShortcutCachingLogic.getIcon(
context, info, LauncherAppState.getIDP(context).fillResIconDpi);
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index 427fb97..55bcb70 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -41,6 +41,7 @@
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.pm.PackageInstallInfo;
+import com.android.launcher3.util.ApplicationInfoWrapper;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.PackageManagerHelper;
@@ -103,8 +104,8 @@
}
// b/139663018 Short-circuit this logic if the icon is a system app
- if (PackageManagerHelper.isSystemApp(context,
- Objects.requireNonNull(item.getIntent()))) {
+ if (new ApplicationInfoWrapper(context,
+ Objects.requireNonNull(item.getIntent())).isSystem()) {
continue;
}
diff --git a/src/com/android/launcher3/model/AllAppsList.java b/src/com/android/launcher3/model/AllAppsList.java
index 64ebbf3..7bc9273 100644
--- a/src/com/android/launcher3/model/AllAppsList.java
+++ b/src/com/android/launcher3/model/AllAppsList.java
@@ -40,6 +40,7 @@
import com.android.launcher3.pm.PackageInstallInfo;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.util.ApiWrapper;
+import com.android.launcher3.util.ApplicationInfoWrapper;
import com.android.launcher3.util.FlagOp;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.SafeCloseable;
@@ -169,8 +170,8 @@
public AppInfo addPromiseApp(
Context context, PackageInstallInfo installInfo, boolean loadIcon) {
// only if not yet installed
- if (PackageManagerHelper.INSTANCE.get(context)
- .isAppInstalled(installInfo.packageName, installInfo.user)) {
+ if (new ApplicationInfoWrapper(context, installInfo.packageName, installInfo.user)
+ .isInstalled()) {
return null;
}
AppInfo promiseAppInfo = new AppInfo(installInfo);
@@ -223,7 +224,8 @@
if (DEBUG) {
Log.w(TAG, "updatePromiseInstallInfo: removing app due to install"
+ " failure and appInfo not startable."
- + " package=" + appInfo.getTargetPackage());
+ + " package=" + appInfo.getTargetPackage()
+ + ", user=" + user);
}
removeApp(i);
}
@@ -319,7 +321,8 @@
if (!findActivity(matches, applicationInfo.componentName)) {
if (DEBUG) {
Log.w(TAG, "Changing shortcut target due to app component name change."
- + " package=" + packageName);
+ + " component=" + applicationInfo.componentName
+ + ", user=" + user);
}
removeApp(i);
}
@@ -346,8 +349,9 @@
} else {
// Remove all data for this package.
if (DEBUG) {
- Log.w(TAG, "updatePromiseInstallInfo: no Activities matched updated package,"
- + " removing all apps from package=" + packageName);
+ Log.w(TAG, "updatePackage: no Activities matched updated package,"
+ + " removing any AppInfo with package=" + packageName
+ + ", user=" + user);
}
for (int i = data.size() - 1; i >= 0; i--) {
final AppInfo applicationInfo = data.get(i);
diff --git a/src/com/android/launcher3/model/GridSizeMigrationUtil.java b/src/com/android/launcher3/model/GridSizeMigrationUtil.java
index 8d2a7f9..4c017e9 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationUtil.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationUtil.java
@@ -17,6 +17,7 @@
package com.android.launcher3.model;
import static com.android.launcher3.Flags.enableSmartspaceRemovalToggle;
+import static com.android.launcher3.LauncherPrefs.IS_FIRST_LOAD_AFTER_RESTORE;
import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
import static com.android.launcher3.LauncherSettings.Favorites.TMP_TABLE;
import static com.android.launcher3.Utilities.SHOULD_SHOW_FIRST_PAGE_WIDGET;
@@ -28,8 +29,6 @@
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
@@ -47,7 +46,6 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
import com.android.launcher3.util.ContentWriter;
import com.android.launcher3.util.GridOccupancy;
@@ -100,7 +98,7 @@
@VisibleForTesting
public static List<DbEntry> readAllEntries(SQLiteDatabase db, String tableName,
Context context) {
- DbReader dbReader = new DbReader(db, tableName, context, getValidPackages(context));
+ DbReader dbReader = new DbReader(db, tableName, context);
List<DbEntry> result = dbReader.loadAllWorkspaceEntries();
result.addAll(dbReader.loadHotseatEntries());
return result;
@@ -132,7 +130,8 @@
return true;
}
- if (Flags.enableGridMigrationFix()
+ if (LauncherPrefs.get(context).get(IS_FIRST_LOAD_AFTER_RESTORE)
+ && Flags.enableGridMigrationFix()
&& srcDeviceState.getColumns().equals(destDeviceState.getColumns())
&& srcDeviceState.getRows() < destDeviceState.getRows()) {
Log.i("b/360462379", "Grid migration fix entry point.");
@@ -144,11 +143,10 @@
}
copyTable(source, TABLE_NAME, target.getWritableDatabase(), TMP_TABLE, context);
- HashSet<String> validPackages = getValidPackages(context);
long migrationStartTime = System.currentTimeMillis();
try (SQLiteTransaction t = new SQLiteTransaction(target.getWritableDatabase())) {
- DbReader srcReader = new DbReader(t.getDb(), TMP_TABLE, context, validPackages);
- DbReader destReader = new DbReader(t.getDb(), TABLE_NAME, context, validPackages);
+ DbReader srcReader = new DbReader(t.getDb(), TMP_TABLE, context);
+ DbReader destReader = new DbReader(t.getDb(), TABLE_NAME, context);
Point targetSize = new Point(destDeviceState.getColumns(), destDeviceState.getRows());
migrate(target, srcReader, destReader, destDeviceState.getNumHotseat(),
@@ -348,23 +346,6 @@
Utilities.createDbSelectionQuery(LauncherSettings.Favorites._ID, entryIds), null);
}
- private static HashSet<String> getValidPackages(Context context) {
- // Initialize list of valid packages. This contain all the packages which are already on
- // the device and packages which are being installed. Any item which doesn't belong to
- // this set is removed.
- // Since the loader removes such items anyway, removing these items here doesn't cause
- // any extra data loss and gives us more free space on the grid for better migration.
- HashSet<String> validPackages = new HashSet<>();
- for (PackageInfo info : context.getPackageManager()
- .getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES)) {
- validPackages.add(info.packageName);
- }
- InstallSessionHelper.INSTANCE.get(context)
- .getActiveSessions().keySet()
- .forEach(packageUserKey -> validPackages.add(packageUserKey.mPackageName));
- return validPackages;
- }
-
private static void solveGridPlacement(@NonNull final DatabaseHelper helper,
@NonNull final DbReader srcReader, @NonNull final DbReader destReader,
final int screenId, final int trgX, final int trgY,
@@ -461,18 +442,15 @@
private final SQLiteDatabase mDb;
private final String mTableName;
private final Context mContext;
- private final Set<String> mValidPackages;
private int mLastScreenId = -1;
private final Map<Integer, ArrayList<DbEntry>> mWorkspaceEntriesByScreenId =
new ArrayMap<>();
- public DbReader(SQLiteDatabase db, String tableName, Context context,
- Set<String> validPackages) {
+ public DbReader(SQLiteDatabase db, String tableName, Context context) {
mDb = db;
mTableName = tableName;
mContext = context;
- mValidPackages = validPackages;
}
protected List<DbEntry> loadHotseatEntries() {
@@ -504,7 +482,6 @@
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: {
entry.mIntent = c.getString(indexIntent);
- verifyIntent(c.getString(indexIntent));
break;
}
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: {
@@ -586,14 +563,12 @@
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: {
entry.mIntent = c.getString(indexIntent);
- verifyIntent(entry.mIntent);
break;
}
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: {
entry.mProvider = c.getString(indexAppWidgetProvider);
entry.appWidgetId = c.getInt(indexAppWidgetId);
ComponentName cn = ComponentName.unflattenFromString(entry.mProvider);
- verifyPackage(cn.getPackageName());
LauncherAppWidgetProviderInfo pInfo = widgetManagerHelper
.getLauncherAppWidgetInfo(entry.appWidgetId, cn);
@@ -656,7 +631,6 @@
try {
int id = c.getInt(0);
String intent = c.getString(1);
- verifyIntent(intent);
total++;
if (!entry.mFolderItems.containsKey(intent)) {
entry.mFolderItems.put(intent, new HashSet<>());
@@ -673,27 +647,6 @@
private Cursor queryWorkspace(String[] columns, String where) {
return mDb.query(mTableName, columns, where, null, null, null, null);
}
-
- /** Verifies if the mIntent should be restored. */
- private void verifyIntent(String intentStr)
- throws Exception {
- Intent intent = Intent.parseUri(intentStr, 0);
- if (intent.getComponent() != null) {
- verifyPackage(intent.getComponent().getPackageName());
- } else if (intent.getPackage() != null) {
- // Only verify package if the component was null.
- verifyPackage(intent.getPackage());
- }
- }
-
- /** Verifies if the package should be restored */
- private void verifyPackage(String packageName)
- throws Exception {
- if (!mValidPackages.contains(packageName)) {
- // TODO(b/151468819): Handle promise app icon restoration during grid migration.
- throw new Exception("Package not available");
- }
- }
}
public static class DbEntry extends ItemInfo implements Comparable<DbEntry> {
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 605accf..609846f 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -70,11 +70,11 @@
import com.android.launcher3.folder.FolderNameInfos;
import com.android.launcher3.folder.FolderNameProvider;
import com.android.launcher3.icons.ComponentWithLabelAndIcon;
-import com.android.launcher3.icons.ComponentWithLabelAndIcon.ComponentWithIconCachingLogic;
import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.icons.LauncherActivityCachingLogic;
import com.android.launcher3.icons.ShortcutCachingLogic;
+import com.android.launcher3.icons.cache.CachedObjectCachingLogic;
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.data.AppInfo;
import com.android.launcher3.model.data.AppPairInfo;
@@ -298,7 +298,7 @@
IconCacheUpdateHandler updateHandler = mIconCache.getUpdateHandler();
setIgnorePackages(updateHandler);
updateHandler.updateIcons(allActivityList,
- LauncherActivityCachingLogic.newInstance(mApp.getContext()),
+ LauncherActivityCachingLogic.INSTANCE,
mApp.getModel()::onPackageIconsUpdated);
logASplit("update icon cache");
@@ -360,7 +360,7 @@
}
updateHandler.updateIcons(allWidgetsList,
- new ComponentWithIconCachingLogic(mApp.getContext(), true),
+ new CachedObjectCachingLogic(mApp.getContext()),
mApp.getModel()::onWidgetLabelsUpdated);
logASplit("save widgets in icon cache");
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 2febb22..5464afe 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -119,7 +119,8 @@
final HashMap<String, List<LauncherActivityInfo>> activitiesLists = new HashMap<>();
if (DEBUG) {
Log.d(TAG, "Package updated: mOp=" + getOpString()
- + " packages=" + Arrays.toString(packages));
+ + " packages=" + Arrays.toString(packages)
+ + ", user=" + mUser);
}
switch (mOp) {
case OP_ADD: {
diff --git a/src/com/android/launcher3/model/SdCardAvailableReceiver.java b/src/com/android/launcher3/model/SdCardAvailableReceiver.java
index 5293316..9e3f0e1 100644
--- a/src/com/android/launcher3/model/SdCardAvailableReceiver.java
+++ b/src/com/android/launcher3/model/SdCardAvailableReceiver.java
@@ -24,7 +24,7 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
-import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.ApplicationInfoWrapper;
import com.android.launcher3.util.PackageUserKey;
import java.util.ArrayList;
@@ -52,7 +52,6 @@
@Override
public void onReceive(Context context, Intent intent) {
final LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
- final PackageManagerHelper pmHelper = PackageManagerHelper.INSTANCE.get(context);
for (PackageUserKey puk : mPackages) {
UserHandle user = puk.mUser;
@@ -60,7 +59,7 @@
final ArrayList<String> packagesUnavailable = new ArrayList<>();
if (!launcherApps.isPackageEnabled(puk.mPackageName, user)) {
- if (pmHelper.isAppOnSdcard(puk.mPackageName, user)) {
+ if (new ApplicationInfoWrapper(context, puk.mPackageName, user).isOnSdCard()) {
packagesUnavailable.add(puk.mPackageName);
} else {
packagesRemoved.add(puk.mPackageName);
diff --git a/src/com/android/launcher3/model/ShortcutsChangedTask.java b/src/com/android/launcher3/model/ShortcutsChangedTask.java
index 1916d23..55c4d30 100644
--- a/src/com/android/launcher3/model/ShortcutsChangedTask.java
+++ b/src/com/android/launcher3/model/ShortcutsChangedTask.java
@@ -27,8 +27,8 @@
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.shortcuts.ShortcutRequest;
+import com.android.launcher3.util.ApplicationInfoWrapper;
import com.android.launcher3.util.ItemInfoMatcher;
-import com.android.launcher3.util.PackageManagerHelper;
import java.util.ArrayList;
import java.util.HashSet;
@@ -80,11 +80,10 @@
if (!matchingWorkspaceItems.isEmpty()) {
if (mShortcuts.isEmpty()) {
- PackageManagerHelper packageManagerHelper =
- PackageManagerHelper.INSTANCE.get(context);
+ ApplicationInfoWrapper infoWrapper =
+ new ApplicationInfoWrapper(context, mPackageName, mUser);
// Verify that the app is indeed installed.
- if (!packageManagerHelper.isAppInstalled(mPackageName, mUser)
- && !packageManagerHelper.isAppArchivedForUser(mPackageName, mUser)) {
+ if (!infoWrapper.isInstalled() && !infoWrapper.isArchived()) {
// App is not installed or archived, ignoring package events
return;
}
diff --git a/src/com/android/launcher3/model/WidgetItem.java b/src/com/android/launcher3/model/WidgetItem.java
index 3f88717..ac9f2d6 100644
--- a/src/com/android/launcher3/model/WidgetItem.java
+++ b/src/com/android/launcher3/model/WidgetItem.java
@@ -4,8 +4,6 @@
import static android.appwidget.AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD;
import static android.appwidget.AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX;
-import static com.android.launcher3.Utilities.ATLEAST_S;
-
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.ActivityInfo;
@@ -48,7 +46,7 @@
super(info.provider, info.getProfile());
label = iconCache.getTitleNoCache(info);
- description = ATLEAST_S ? info.loadDescription(context) : null;
+ description = info.loadDescription(context);
widgetInfo = info;
activityInfo = null;
@@ -107,7 +105,7 @@
/** Returns whether this {@link WidgetItem} has a preview layout that can be used. */
@SuppressLint("NewApi") // Already added API check.
public boolean hasPreviewLayout() {
- return ATLEAST_S && widgetInfo != null && widgetInfo.previewLayout != Resources.ID_NULL;
+ return widgetInfo != null && widgetInfo.previewLayout != Resources.ID_NULL;
}
/** Returns whether this {@link WidgetItem} is for a shortcut rather than an app widget. */
diff --git a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
index 90e47d6..18c7f95 100644
--- a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
+++ b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
@@ -30,7 +30,6 @@
import com.android.launcher3.InvariantDeviceProfile
import com.android.launcher3.LauncherAppState
import com.android.launcher3.LauncherSettings.Favorites
-import com.android.launcher3.Utilities
import com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RestoreError
import com.android.launcher3.config.FeatureFlags
import com.android.launcher3.logging.FileLog
@@ -45,6 +44,7 @@
import com.android.launcher3.pm.UserCache
import com.android.launcher3.shortcuts.ShortcutKey
import com.android.launcher3.util.ApiWrapper
+import com.android.launcher3.util.ApplicationInfoWrapper
import com.android.launcher3.util.ComponentKey
import com.android.launcher3.util.PackageManagerHelper
import com.android.launcher3.util.PackageUserKey
@@ -76,7 +76,7 @@
private val pmHelper: PackageManagerHelper,
private val iconRequestInfos: MutableList<IconRequestInfo<WorkspaceItemInfo>>,
private val unlockedUsers: LongSparseArray<Boolean>,
- private val allDeepShortcuts: MutableList<ShortcutInfo>
+ private val allDeepShortcuts: MutableList<ShortcutInfo>,
) {
private val isSafeMode = app.isSafeModeEnabled
@@ -97,7 +97,7 @@
// User has been deleted, remove the item.
c.markDeleted(
"User has been deleted for item id=${c.id}",
- RestoreError.PROFILE_DELETED
+ RestoreError.PROFILE_DELETED,
)
return
}
@@ -153,6 +153,7 @@
c.markDeleted("No target package for item id=${c.id}", RestoreError.MISSING_INFO)
return
}
+ val appInfoWrapper = ApplicationInfoWrapper(app.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
@@ -168,7 +169,7 @@
FileLog.d(
TAG,
"Activity not enabled for id=${c.id}, component=$cn, user=${c.user}." +
- " Will attempt to find fallback Activity for targetPkg=$targetPkg."
+ " Will attempt to find fallback Activity for targetPkg=$targetPkg.",
)
intent = pmHelper.getAppLaunchIntent(targetPkg, c.user)
if (intent != null) {
@@ -178,7 +179,7 @@
c.markDeleted(
"No Activities found for id=${c.id}, targetPkg=$targetPkg, component=$cn." +
" Unable to create launch Intent.",
- RestoreError.MISSING_INFO
+ RestoreError.MISSING_INFO,
)
return
}
@@ -213,13 +214,13 @@
else -> {
c.markDeleted(
"removing app that is not restored and not installing. package: $targetPkg",
- RestoreError.APP_NOT_INSTALLED
+ RestoreError.APP_NOT_INSTALLED,
)
return
}
}
}
- pmHelper.isAppOnSdcard(targetPkg, c.user) -> {
+ appInfoWrapper.isOnSdCard() -> {
// Package is present but not available.
disabledState =
disabledState or WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE
@@ -238,7 +239,7 @@
// Do not wait for external media load anymore.
c.markDeleted(
"Invalid package removed: $targetPkg",
- RestoreError.APP_NOT_INSTALLED
+ RestoreError.APP_NOT_INSTALLED,
)
return
}
@@ -270,7 +271,7 @@
// The shortcut is no longer valid.
c.markDeleted(
"Pinned shortcut not found from request. package=${key.packageName}, user=${c.user}",
- RestoreError.SHORTCUT_NOT_FOUND
+ RestoreError.SHORTCUT_NOT_FOUND,
)
return
}
@@ -278,7 +279,7 @@
// If the pinned deep shortcut is no longer published,
// use the last saved icon instead of the default.
iconCache.getShortcutIcon(info, pinnedShortcut, c::loadIcon)
- if (pmHelper.isAppSuspended(pinnedShortcut.getPackage(), info.user)) {
+ if (appInfoWrapper.isSuspended()) {
info.runtimeStatusFlags =
info.runtimeStatusFlags or ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED
}
@@ -295,7 +296,7 @@
info = c.loadSimpleWorkspaceItem()
// Shortcuts are only available on the primary profile
- if (!TextUtils.isEmpty(targetPkg) && pmHelper.isAppSuspended(targetPkg, c.user)) {
+ if (appInfoWrapper.isSuspended()) {
disabledState = disabledState or ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED
}
info.options = c.options
@@ -326,7 +327,7 @@
info.spanX = 1
info.spanY = 1
info.runtimeStatusFlags = info.runtimeStatusFlags or disabledState
- if (isSafeMode && !PackageManagerHelper.isSystemApp(app.context, intent)) {
+ if (isSafeMode && !appInfoWrapper.isSystem()) {
info.runtimeStatusFlags =
info.runtimeStatusFlags or ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE
}
@@ -337,7 +338,7 @@
activityInfo,
userCache.getUserInfo(c.user),
ApiWrapper.INSTANCE[app.context],
- pmHelper
+ pmHelper,
)
}
if (
@@ -445,7 +446,7 @@
", id=${c.id}," +
", appWidgetId=${c.appWidgetId}," +
", component=${component}",
- RestoreError.INVALID_LOCATION
+ RestoreError.INVALID_LOCATION,
)
return
}
@@ -456,7 +457,7 @@
", appWidgetId=${c.appWidgetId}," +
", component=${component}," +
", container=${c.container}",
- RestoreError.INVALID_LOCATION
+ RestoreError.INVALID_LOCATION,
)
return
}
@@ -470,7 +471,7 @@
TAG,
"processWidget: id=${c.id}" +
", appWidgetId=${c.appWidgetId}" +
- ", inflationResult=$inflationResult"
+ ", inflationResult=$inflationResult",
)
when (inflationResult.type) {
WidgetInflater.TYPE_DELETE -> {
@@ -487,7 +488,8 @@
(si == null) &&
(lapi == null) &&
!(Flags.enableSupportForArchiving() &&
- pmHelper.isAppArchived(component.packageName))
+ ApplicationInfoWrapper(app.context, component.packageName, c.user)
+ .isArchived())
) {
// Restore never started
c.markDeleted(
@@ -496,7 +498,7 @@
", appWidgetId=${c.appWidgetId}" +
", component=${component}" +
", restoreFlag:=${c.restoreFlag}",
- RestoreError.APP_NOT_INSTALLED
+ RestoreError.APP_NOT_INSTALLED,
)
return
} else if (
@@ -512,7 +514,7 @@
WidgetsModel.newPendingItemInfo(
app.context,
appWidgetInfo.providerName,
- appWidgetInfo.user
+ appWidgetInfo.user,
)
iconCache.getTitleAndIconForApp(appWidgetInfo.pendingItemInfo, false)
}
@@ -522,7 +524,7 @@
lapi,
app.context,
appWidgetInfo.spanX,
- appWidgetInfo.spanY
+ appWidgetInfo.spanY,
)
}
@@ -541,7 +543,7 @@
" processWidget: Widget ${lapi.component} minSizes not met: span=${appWidgetInfo.spanX}x${appWidgetInfo.spanY} minSpan=${lapi.minSpanX}x${lapi.minSpanY}," +
" id: ${c.id}," +
" appWidgetId: ${c.appWidgetId}," +
- " component=${component}"
+ " component=${component}",
)
logWidgetInfo(app.invariantDeviceProfile, lapi)
}
@@ -554,7 +556,7 @@
private fun logWidgetInfo(
idp: InvariantDeviceProfile,
- widgetProviderInfo: LauncherAppWidgetProviderInfo
+ widgetProviderInfo: LauncherAppWidgetProviderInfo,
) {
val cellSize = Point()
for (deviceProfile in idp.supportedProfiles) {
@@ -565,7 +567,7 @@
" available height: ${deviceProfile.availableHeightPx}," +
" cellLayoutBorderSpacePx Horizontal: ${deviceProfile.cellLayoutBorderSpacePx.x}," +
" cellLayoutBorderSpacePx Vertical: ${deviceProfile.cellLayoutBorderSpacePx.y}," +
- " cellSize: $cellSize"
+ " cellSize: $cellSize",
)
}
val widgetDimension = StringBuilder()
@@ -583,21 +585,19 @@
.append("defaultHeight: ")
.append(widgetProviderInfo.minHeight)
.append("\n")
- if (Utilities.ATLEAST_S) {
- widgetDimension
- .append("targetCellWidth: ")
- .append(widgetProviderInfo.targetCellWidth)
- .append("\n")
- .append("targetCellHeight: ")
- .append(widgetProviderInfo.targetCellHeight)
- .append("\n")
- .append("maxResizeWidth: ")
- .append(widgetProviderInfo.maxResizeWidth)
- .append("\n")
- .append("maxResizeHeight: ")
- .append(widgetProviderInfo.maxResizeHeight)
- .append("\n")
- }
+ widgetDimension
+ .append("targetCellWidth: ")
+ .append(widgetProviderInfo.targetCellWidth)
+ .append("\n")
+ .append("targetCellHeight: ")
+ .append(widgetProviderInfo.targetCellHeight)
+ .append("\n")
+ .append("maxResizeWidth: ")
+ .append(widgetProviderInfo.maxResizeWidth)
+ .append("\n")
+ .append("maxResizeHeight: ")
+ .append(widgetProviderInfo.maxResizeHeight)
+ .append("\n")
FileLog.d(TAG, widgetDimension.toString())
}
}
diff --git a/src/com/android/launcher3/model/data/AppInfo.java b/src/com/android/launcher3/model/data/AppInfo.java
index a4281f8..97b62b4 100644
--- a/src/com/android/launcher3/model/data/AppInfo.java
+++ b/src/com/android/launcher3/model/data/AppInfo.java
@@ -21,7 +21,6 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
import android.os.UserHandle;
import android.os.UserManager;
@@ -36,6 +35,7 @@
import com.android.launcher3.pm.PackageInstallInfo;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.util.ApiWrapper;
+import com.android.launcher3.util.ApplicationInfoWrapper;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.UserIconInfo;
@@ -187,8 +187,8 @@
ApiWrapper apiWrapper, PackageManagerHelper pmHelper) {
final int oldProgressLevel = info.getProgressLevel();
final int oldRuntimeStatusFlags = info.runtimeStatusFlags;
- ApplicationInfo appInfo = lai.getApplicationInfo();
- if (PackageManagerHelper.isAppSuspended(appInfo)) {
+ ApplicationInfoWrapper appInfo = new ApplicationInfoWrapper(lai.getApplicationInfo());
+ if (appInfo.isSuspended()) {
info.runtimeStatusFlags |= FLAG_DISABLED_SUSPENDED;
} else {
info.runtimeStatusFlags &= ~FLAG_DISABLED_SUSPENDED;
@@ -200,8 +200,7 @@
info.runtimeStatusFlags &= ~FLAG_ARCHIVED;
}
}
- info.runtimeStatusFlags |= (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0
- ? FLAG_SYSTEM_NO : FLAG_SYSTEM_YES;
+ info.runtimeStatusFlags |= appInfo.isSystem() ? FLAG_SYSTEM_YES : FLAG_SYSTEM_NO;
if (Flags.privateSpaceRestrictAccessibilityDrag()) {
if (userIconInfo.isPrivate()) {
diff --git a/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java b/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
index f4dda55..361f09d 100644
--- a/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
@@ -21,7 +21,6 @@
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PIN_WIDGETS;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY;
-import static com.android.launcher3.Utilities.ATLEAST_S;
import android.appwidget.AppWidgetHostView;
import android.content.ComponentName;
@@ -233,16 +232,16 @@
if (providerInfo.isConfigurationOptional()) {
widgetFeatures |= FEATURE_OPTIONAL_CONFIGURATION;
}
- if (ATLEAST_S && providerInfo.previewLayout != Resources.ID_NULL) {
+ if (providerInfo.previewLayout != Resources.ID_NULL) {
widgetFeatures |= FEATURE_PREVIEW_LAYOUT;
}
- if (ATLEAST_S && providerInfo.targetCellWidth > 0 || providerInfo.targetCellHeight > 0) {
+ if (providerInfo.targetCellWidth > 0 || providerInfo.targetCellHeight > 0) {
widgetFeatures |= FEATURE_TARGET_CELL_SIZE;
}
if (providerInfo.minResizeWidth > 0 || providerInfo.minResizeHeight > 0) {
widgetFeatures |= FEATURE_MIN_SIZE;
}
- if (ATLEAST_S && providerInfo.maxResizeWidth > 0 || providerInfo.maxResizeHeight > 0) {
+ if (providerInfo.maxResizeWidth > 0 || providerInfo.maxResizeHeight > 0) {
widgetFeatures |= FEATURE_MAX_SIZE;
}
if (hostView instanceof LauncherAppWidgetHostView &&
diff --git a/src/com/android/launcher3/pm/InstallSessionHelper.java b/src/com/android/launcher3/pm/InstallSessionHelper.java
index e66f496..124907f 100644
--- a/src/com/android/launcher3/pm/InstallSessionHelper.java
+++ b/src/com/android/launcher3/pm/InstallSessionHelper.java
@@ -17,7 +17,6 @@
package com.android.launcher3.pm;
import android.content.Context;
-import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherApps;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
@@ -34,10 +33,10 @@
import com.android.launcher3.SessionCommitReceiver;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.ItemInstallQueue;
+import com.android.launcher3.util.ApplicationInfoWrapper;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.MainThreadInitializedObject;
-import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.SafeCloseable;
@@ -171,8 +170,7 @@
synchronized (mSessionVerifiedMap) {
if (!mSessionVerifiedMap.containsKey(pkg)) {
boolean hasSystemFlag = DEBUG || mAppContext.getPackageName().equals(pkg)
- || PackageManagerHelper.INSTANCE.get(mAppContext)
- .getApplicationInfo(pkg, user, ApplicationInfo.FLAG_SYSTEM) != null;
+ || new ApplicationInfoWrapper(mAppContext, pkg, user).isSystem();
mSessionVerifiedMap.put(pkg, hasSystemFlag);
}
}
@@ -245,8 +243,8 @@
&& sessionInfo.getInstallReason() == PackageManager.INSTALL_REASON_USER
&& sessionInfo.getAppIcon() != null
&& !TextUtils.isEmpty(sessionInfo.getAppLabel())
- && !PackageManagerHelper.INSTANCE.get(mAppContext).isAppInstalled(
- sessionInfo.getAppPackageName(), getUserHandle(sessionInfo));
+ && !new ApplicationInfoWrapper(mAppContext, sessionInfo.getAppPackageName(),
+ getUserHandle(sessionInfo)).isInstalled();
}
public InstallSessionTracker registerInstallTracker(
diff --git a/src/com/android/launcher3/pm/PinRequestHelper.java b/src/com/android/launcher3/pm/PinRequestHelper.java
index 667136a..47afeef 100644
--- a/src/com/android/launcher3/pm/PinRequestHelper.java
+++ b/src/com/android/launcher3/pm/PinRequestHelper.java
@@ -77,8 +77,9 @@
WorkspaceItemInfo info = new WorkspaceItemInfo(si, context);
// Apply the unbadged icon synchronously using the caching logic directly and
// fetch the actual icon asynchronously.
- info.bitmap = new ShortcutCachingLogic().loadIcon(context, si);
- LauncherAppState.getInstance(context).getModel().updateAndBindWorkspaceItem(info, si);
+ LauncherAppState app = LauncherAppState.getInstance(context);
+ info.bitmap = new ShortcutCachingLogic().loadIcon(context, app.getIconCache(), si);
+ app.getModel().updateAndBindWorkspaceItem(info, si);
return info;
} else {
return null;
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index 1b245ab..63c9d94 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -1,5 +1,6 @@
package com.android.launcher3.popup;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_DISMISS_PREDICTION_UNDO;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_INSTALL_SYSTEM_SHORTCUT_TAP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_UNINSTALL_SYSTEM_SHORTCUT_TAP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_APP_INFO_TAP;
@@ -41,6 +42,7 @@
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.views.Snackbar;
import com.android.launcher3.widget.WidgetsBottomSheet;
import com.android.launcher3.widget.picker.model.data.WidgetPickerData;
@@ -336,6 +338,14 @@
mTarget.getStatsLogManager().logger()
.withItemInfo(mItemInfo)
.log(LAUNCHER_SYSTEM_SHORTCUT_DONT_SUGGEST_APP_TAP);
+ if (Flags.enableDismissPredictionUndo()) {
+ Snackbar.show(mTarget,
+ view.getContext().getString(R.string.item_removed), R.string.undo,
+ () -> { }, () ->
+ mTarget.getStatsLogManager().logger()
+ .withItemInfo(mItemInfo)
+ .log(LAUNCHER_DISMISS_PREDICTION_UNDO));
+ }
}
}
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index 21897bf..775d248 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -75,7 +75,6 @@
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.LogConfig;
-import java.io.File;
import java.io.InvalidObjectException;
import java.util.Arrays;
import java.util.Collection;
@@ -127,12 +126,12 @@
if (Flags.enableNarrowGridRestore()) {
String oldPhoneFileName = idp.dbFile;
- List<String> previousDbs = existingDbs();
+ List<String> previousDbs = existingDbs(context);
removeOldDBs(context, oldPhoneFileName);
// The idp before this contains data about the old phone, after this it becomes the idp
// of the current phone.
idp.reset(context);
- trySettingPreviousGidAsCurrent(context, idp, oldPhoneFileName, previousDbs);
+ trySettingPreviousGridAsCurrent(context, idp, oldPhoneFileName, previousDbs);
} else {
idp.reinitializeAfterRestore(context);
}
@@ -143,7 +142,7 @@
* Try setting the gird used in the previous phone to the new one. If the current device doesn't
* support the previous grid option it will not be set.
*/
- private static void trySettingPreviousGidAsCurrent(Context context, InvariantDeviceProfile idp,
+ private static void trySettingPreviousGridAsCurrent(Context context, InvariantDeviceProfile idp,
String oldPhoneDbFileName, List<String> previousDbs) {
InvariantDeviceProfile.GridOption oldPhoneGridOption = idp.getGridOptionFromFileName(
context, oldPhoneDbFileName);
@@ -166,17 +165,19 @@
/**
* Returns a list of paths of the existing launcher dbs.
*/
- private static List<String> existingDbs() {
+ @VisibleForTesting
+ public static List<String> existingDbs(Context context) {
// At this point idp.dbFile contains the name of the dbFile from the previous phone
return LauncherFiles.GRID_DB_FILES.stream()
- .filter(dbName -> new File(dbName).exists())
+ .filter(dbName -> context.getDatabasePath(dbName).exists())
.toList();
}
/**
* Only keep the last database used on the previous device.
*/
- private static void removeOldDBs(Context context, String oldPhoneDbFileName) {
+ @VisibleForTesting
+ public static void removeOldDBs(Context context, String oldPhoneDbFileName) {
// At this point idp.dbFile contains the name of the dbFile from the previous phone
LauncherFiles.GRID_DB_FILES.stream()
.filter(dbName -> !dbName.equals(oldPhoneDbFileName))
diff --git a/src/com/android/launcher3/recyclerview/AllAppsRecyclerViewPool.kt b/src/com/android/launcher3/recyclerview/AllAppsRecyclerViewPool.kt
index 6ff51ca..82229f8 100644
--- a/src/com/android/launcher3/recyclerview/AllAppsRecyclerViewPool.kt
+++ b/src/com/android/launcher3/recyclerview/AllAppsRecyclerViewPool.kt
@@ -24,7 +24,6 @@
import com.android.launcher3.BubbleTextView
import com.android.launcher3.BuildConfig
import com.android.launcher3.allapps.BaseAllAppsAdapter
-import com.android.launcher3.config.FeatureFlags
import com.android.launcher3.util.CancellableTask
import com.android.launcher3.util.Executors.MAIN_EXECUTOR
import com.android.launcher3.util.Executors.VIEW_PREINFLATION_EXECUTOR
@@ -78,7 +77,7 @@
ActivityContextDelegate(
context.createConfigurationContext(context.resources.configuration),
Themes.getActivityThemeRes(context),
- context
+ context,
)
// Because we perform onCreateViewHolder() on worker thread, we need a separate
@@ -91,7 +90,7 @@
context,
context.appsView.layoutInflater.cloneInContext(allAppsPreInflationContext),
null,
- null
+ null,
) {
override fun setAppsPerRow(appsPerRow: Int) = Unit
@@ -124,7 +123,7 @@
for (i in 0 until minOf(viewHolders.size, getPreinflateCount(context))) {
putRecycledView(viewHolders[i])
}
- }
+ },
)
mCancellableTask = task
VIEW_PREINFLATION_EXECUTOR.submit(mCancellableTask)
@@ -144,18 +143,15 @@
* app icons plus [EXTRA_ICONS_COUNT] is the magic minimal count of app icons to preinflate to
* suffice fast scrolling.
*
- * Note that if [FeatureFlags.ALL_APPS_GONE_VISIBILITY] is enabled, we need to preinfate extra
- * app icons in size of one all apps pages, so that opening all apps don't need to inflate app
- * icons.
+ * Note that we need to preinfate extra app icons in size of one all apps pages, so that opening
+ * all apps don't need to inflate app icons.
*/
fun <T> getPreinflateCount(context: T): Int where T : Context, T : ActivityContext {
var targetPreinflateCount =
PREINFLATE_ICONS_ROW_COUNT * context.deviceProfile.numShownAllAppsColumns +
EXTRA_ICONS_COUNT
- if (FeatureFlags.ALL_APPS_GONE_VISIBILITY.get()) {
- val grid = ActivityContext.lookupContext<T>(context).deviceProfile
- targetPreinflateCount += grid.maxAllAppsRowCount * grid.numShownAllAppsColumns
- }
+ val grid = ActivityContext.lookupContext<T>(context).deviceProfile
+ targetPreinflateCount += grid.maxAllAppsRowCount * grid.numShownAllAppsColumns
if (hasWorkProfile) {
targetPreinflateCount *= 2
}
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
index 6e697d9..d5c87f4 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
@@ -65,7 +65,7 @@
@Override
public void recreateControllers() {
mControllers = new TouchController[]{new CloseAllAppsTouchController(),
- mActivity.getDragController()};
+ mContainer.getDragController()};
}
/**
@@ -79,10 +79,10 @@
mAppsView = findViewById(R.id.apps_view);
// Setup workspace
mWorkspace = findViewById(R.id.workspace_grid);
- mPinnedAppsAdapter = new PinnedAppsAdapter(mActivity, mAppsView.getAppsStore(),
+ mPinnedAppsAdapter = new PinnedAppsAdapter(mContainer, mAppsView.getAppsStore(),
this::onIconLongClicked);
mWorkspace.setAdapter(mPinnedAppsAdapter);
- mWorkspace.setNumColumns(mActivity.getDeviceProfile().inv.numColumns);
+ mWorkspace.setNumColumns(mContainer.getDeviceProfile().inv.numColumns);
}
/**
@@ -112,7 +112,7 @@
int height = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(width, height);
- DeviceProfile grid = mActivity.getDeviceProfile();
+ DeviceProfile grid = mContainer.getDeviceProfile();
int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
@@ -153,17 +153,17 @@
@Override
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
- if (!mActivity.isAppDrawerShown()) {
+ if (!mContainer.isAppDrawerShown()) {
return false;
}
- if (AbstractFloatingView.getTopOpenView(mActivity) != null) {
+ if (AbstractFloatingView.getTopOpenView(mContainer) != null) {
return false;
}
if (ev.getAction() == MotionEvent.ACTION_DOWN
- && !isEventOverView(mActivity.getAppsView(), ev)) {
- mActivity.showAppDrawer(false);
+ && !isEventOverView(mContainer.getAppsView(), ev)) {
+ mContainer.showAppDrawer(false);
return true;
}
return false;
@@ -178,7 +178,7 @@
if (!(v instanceof BubbleTextView)) {
return false;
}
- if (PopupContainerWithArrow.getOpen(mActivity) != null) {
+ if (PopupContainerWithArrow.getOpen(mContainer) != null) {
// There is already an items container open, so don't open this one.
v.clearFocus();
return false;
@@ -187,32 +187,32 @@
if (!ShortcutUtil.supportsShortcuts(item)) {
return false;
}
- PopupDataProvider popupDataProvider = mActivity.getPopupDataProvider();
+ PopupDataProvider popupDataProvider = mContainer.getPopupDataProvider();
if (popupDataProvider == null) {
return false;
}
// order of this list will reflect in the popup
List<SystemShortcut> systemShortcuts = new ArrayList<>();
- systemShortcuts.add(APP_INFO.getShortcut(mActivity, item, v));
+ systemShortcuts.add(APP_INFO.getShortcut(mContainer, item, v));
// Hide redundant pin shortcut for app drawer icons if drag-n-drop is enabled.
- if (!FeatureFlags.SECONDARY_DRAG_N_DROP_TO_PIN.get() || !mActivity.isAppDrawerShown()) {
+ if (!FeatureFlags.SECONDARY_DRAG_N_DROP_TO_PIN.get() || !mContainer.isAppDrawerShown()) {
systemShortcuts.add(mPinnedAppsAdapter.getSystemShortcut(item, v));
}
int deepShortcutCount = popupDataProvider.getShortcutCountForItem(item);
final PopupContainerWithArrow<SecondaryDisplayLauncher> container;
- container = (PopupContainerWithArrow) mActivity.getLayoutInflater().inflate(
- R.layout.popup_container, mActivity.getDragLayer(), false);
+ container = (PopupContainerWithArrow) mContainer.getLayoutInflater().inflate(
+ R.layout.popup_container, mContainer.getDragLayer(), false);
container.populateAndShowRows((BubbleTextView) v, deepShortcutCount,
systemShortcuts);
container.requestFocus();
- if (!FeatureFlags.SECONDARY_DRAG_N_DROP_TO_PIN.get() || !mActivity.isAppDrawerShown()) {
+ if (!FeatureFlags.SECONDARY_DRAG_N_DROP_TO_PIN.get() || !mContainer.isAppDrawerShown()) {
return true;
}
DragOptions options = new DragOptions();
- DeviceProfile grid = mActivity.getDeviceProfile();
+ DeviceProfile grid = mContainer.getDeviceProfile();
options.intrinsicIconScaleFactor = (float) grid.allAppsIconSizePx / grid.iconSizePx;
options.preDragCondition = container.createPreDragCondition(false);
if (options.preDragCondition == null) {
@@ -229,7 +229,7 @@
mDragView = dragObject.dragView;
if (!shouldStartDrag(0)) {
mDragView.setOnScaleAnimEndCallback(() ->
- mActivity.beginDragShared(v, mActivity.getAppsView(), options));
+ mContainer.beginDragShared(v, mContainer.getAppsView(), options));
}
}
@@ -239,7 +239,7 @@
}
};
}
- mActivity.beginDragShared(v, mActivity.getAppsView(), options);
+ mContainer.beginDragShared(v, mContainer.getAppsView(), options);
return true;
}
}
diff --git a/src/com/android/launcher3/statemanager/StatefulActivity.java b/src/com/android/launcher3/statemanager/StatefulActivity.java
index 28f2def..54b2eae 100644
--- a/src/com/android/launcher3/statemanager/StatefulActivity.java
+++ b/src/com/android/launcher3/statemanager/StatefulActivity.java
@@ -20,6 +20,7 @@
import static com.android.launcher3.LauncherState.FLAG_NON_INTERACTIVE;
+import android.content.Context;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Handler;
@@ -195,15 +196,15 @@
mOldRotation = rotation;
}
+ @Override
+ public Context getContext() {
+ return this;
+ }
+
/**
* Logic for when device configuration changes (rotation, screen size change, multi-window,
* etc.)
*/
protected abstract void onHandleConfigurationChanged();
- /**
- * Enter staged split directly from the current running app.
- * @param leftOrTop if the staged split will be positioned left or top.
- */
- public void enterStageSplitFromRunningApp(boolean leftOrTop) { }
}
diff --git a/src/com/android/launcher3/statemanager/StatefulContainer.java b/src/com/android/launcher3/statemanager/StatefulContainer.java
index 0cf0a27..b10af0a 100644
--- a/src/com/android/launcher3/statemanager/StatefulContainer.java
+++ b/src/com/android/launcher3/statemanager/StatefulContainer.java
@@ -20,6 +20,10 @@
import static com.android.launcher3.LauncherState.FLAG_CLOSE_POPUPS;
import static com.android.launcher3.statemanager.BaseState.FLAG_NON_INTERACTIVE;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.res.Configuration;
+
import androidx.annotation.CallSuper;
import com.android.launcher3.AbstractFloatingView;
@@ -36,6 +40,23 @@
ActivityContext {
/**
+ * Returns an instance of an implementation of StatefulContainer
+ *
+ * @param context will find instance of StatefulContainer from given context.
+ */
+ static <T extends StatefulContainer> T fromContext(Context context) {
+ if (context instanceof StatefulContainer) {
+ return (T) context;
+ } else if (context instanceof ContextWrapper) {
+ return fromContext(((ContextWrapper) context).getBaseContext());
+ } else {
+ throw new IllegalArgumentException("Cannot find StatefulContainer in parent tree");
+ }
+ }
+
+ Context getContext();
+
+ /**
* Creates a factory for atomic state animations
*/
default StateManager.AtomicAnimationFactory<STATE_TYPE> createAtomicAnimationFactory() {
@@ -54,12 +75,15 @@
/**
* Called when transition to state ends
+ *
* @param state current state of State_Type
*/
- default void onStateSetEnd(STATE_TYPE state) { }
+ default void onStateSetEnd(STATE_TYPE state) {
+ }
/**
* Called when transition to state starts
+ *
* @param state current state of State_Type
*/
@CallSuper
@@ -71,6 +95,7 @@
/**
* Returns true if the activity is in the provided state
+ *
* @param state current state of State_Type
*/
default boolean isInState(STATE_TYPE state) {
@@ -81,4 +106,8 @@
* Returns true if state change should transition with animation
*/
boolean shouldAnimateStateChange();
+
+ default void handleConfigurationChanged(Configuration configuration){
+ //no op
+ }
}
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index 6d9b891..3a93981 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -168,21 +168,14 @@
}
case TestProtocol.REQUEST_TARGET_INSETS: {
- return getUIProperty(Bundle::putParcelable, activity -> {
- WindowInsets insets = activity.getWindow()
- .getDecorView().getRootWindowInsets();
- return Insets.max(
- insets.getSystemGestureInsets(),
- insets.getSystemWindowInsets());
- }, this::getCurrentActivity);
+ return getUIProperty(Bundle::putParcelable, insets -> Insets.max(
+ insets.getSystemGestureInsets(),
+ insets.getSystemWindowInsets()), this::getWindowInsets);
}
case TestProtocol.REQUEST_WINDOW_INSETS: {
- return getUIProperty(Bundle::putParcelable, activity -> {
- WindowInsets insets = activity.getWindow()
- .getDecorView().getRootWindowInsets();
- return insets.getSystemWindowInsets();
- }, this::getCurrentActivity);
+ return getUIProperty(Bundle::putParcelable,
+ WindowInsets::getSystemWindowInsets, this::getWindowInsets);
}
case TestProtocol.REQUEST_CELL_LAYOUT_BOARDER_HEIGHT: {
@@ -192,13 +185,13 @@
}
case TestProtocol.REQUEST_SYSTEM_GESTURE_REGION: {
- return getUIProperty(Bundle::putParcelable, activity -> {
- WindowInsetsCompat insets = WindowInsetsCompat.toWindowInsetsCompat(
- activity.getWindow().getDecorView().getRootWindowInsets());
+ return getUIProperty(Bundle::putParcelable, windowInsets -> {
+ WindowInsetsCompat insets =
+ WindowInsetsCompat.toWindowInsetsCompat(windowInsets);
return insets.getInsets(WindowInsetsCompat.Type.ime()
| WindowInsetsCompat.Type.systemGestures())
.toPlatformInsets();
- }, this::getCurrentActivity);
+ }, this::getWindowInsets);
}
case TestProtocol.REQUEST_ICON_HEIGHT: {
@@ -486,8 +479,9 @@
|| LauncherAppState.getInstance(mContext).getModel().isModelLoaded();
}
- protected Activity getCurrentActivity() {
- return Launcher.ACTIVITY_TRACKER.getCreatedActivity();
+ protected WindowInsets getWindowInsets(){
+ return Launcher.ACTIVITY_TRACKER.getCreatedActivity().getWindow().getDecorView()
+ .getRootWindowInsets();
}
/**
diff --git a/src/com/android/launcher3/util/ApplicationInfoWrapper.kt b/src/com/android/launcher3/util/ApplicationInfoWrapper.kt
new file mode 100644
index 0000000..e75b3bc
--- /dev/null
+++ b/src/com/android/launcher3/util/ApplicationInfoWrapper.kt
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util
+
+import android.content.Context
+import android.content.Intent
+import android.content.pm.ApplicationInfo
+import android.content.pm.ApplicationInfo.FLAG_EXTERNAL_STORAGE
+import android.content.pm.ApplicationInfo.FLAG_INSTALLED
+import android.content.pm.ApplicationInfo.FLAG_SUSPENDED
+import android.content.pm.ApplicationInfo.FLAG_SYSTEM
+import android.content.pm.LauncherApps
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.NameNotFoundException
+import android.os.UserHandle
+import com.android.launcher3.Flags.enableSupportForArchiving
+import com.android.launcher3.Utilities.ATLEAST_V
+import kotlin.LazyThreadSafetyMode.NONE
+
+/**
+ * A set of utility methods around ApplicationInfo with support for fetching the actual info lazily
+ */
+class ApplicationInfoWrapper private constructor(provider: () -> ApplicationInfo?) {
+
+ constructor(appInfo: ApplicationInfo?) : this({ appInfo })
+
+ constructor(
+ ctx: Context,
+ pkg: String,
+ user: UserHandle,
+ ) : this({
+ try {
+ ctx.getSystemService(LauncherApps::class.java)
+ ?.getApplicationInfo(pkg, PackageManager.MATCH_UNINSTALLED_PACKAGES, user)
+ ?.let { ai ->
+ // its enabled and (either installed or archived)
+ if (
+ ai.enabled &&
+ (ai.flags.and(FLAG_INSTALLED) != 0 ||
+ (ATLEAST_V && enableSupportForArchiving() && ai.isArchived))
+ ) {
+ ai
+ } else {
+ null
+ }
+ }
+ } catch (e: NameNotFoundException) {
+ null
+ }
+ })
+
+ constructor(
+ ctx: Context,
+ intent: Intent,
+ ) : this(
+ provider@{
+ try {
+ val pm = ctx.packageManager
+ val packageName: String =
+ intent.component?.packageName
+ ?: intent.getPackage()
+ ?: return@provider pm.resolveActivity(
+ intent,
+ PackageManager.MATCH_DEFAULT_ONLY,
+ )
+ ?.activityInfo
+ ?.applicationInfo
+ pm.getApplicationInfo(packageName, 0)
+ } catch (e: NameNotFoundException) {
+ null
+ }
+ }
+ )
+
+ private val appInfo: ApplicationInfo? by lazy(NONE, provider)
+
+ private fun hasFlag(flag: Int) = appInfo?.let { it.flags.and(flag) != 0 } ?: false
+
+ /**
+ * Returns true if the app can possibly be on the SDCard. This is just a workaround and doesn't
+ * guarantee that the app is on SD card.
+ */
+ fun isOnSdCard() = hasFlag(FLAG_EXTERNAL_STORAGE)
+
+ /** Returns whether the target app is installed for a given user */
+ fun isInstalled() = hasFlag(FLAG_INSTALLED)
+
+ /**
+ * Returns whether the target app is suspended for a given user as per
+ * [android.app.admin.DevicePolicyManager.isPackageSuspended].
+ */
+ fun isSuspended() = hasFlag(FLAG_INSTALLED) && hasFlag(FLAG_SUSPENDED)
+
+ /** Returns whether the target app is archived for a given user */
+ fun isArchived() = ATLEAST_V && enableSupportForArchiving() && appInfo?.isArchived ?: false
+
+ /** Returns whether the target app is a system app */
+ fun isSystem() = hasFlag(FLAG_SYSTEM)
+
+ fun getInfo(): ApplicationInfo? = appInfo
+}
diff --git a/src/com/android/launcher3/util/DaggerSingletonObject.java b/src/com/android/launcher3/util/DaggerSingletonObject.java
new file mode 100644
index 0000000..b8cf2ae
--- /dev/null
+++ b/src/com/android/launcher3/util/DaggerSingletonObject.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util;
+
+import android.content.Context;
+
+import com.android.launcher3.LauncherApplication;
+import com.android.launcher3.dagger.LauncherAppComponent;
+
+import java.util.function.Function;
+
+/**
+ * A class to provide DaggerSingleton objects in a traditional way for
+ * {@link MainThreadInitializedObject}.
+ * We should delete this class at the end and use @Inject to get dagger provided singletons.
+ */
+
+public class DaggerSingletonObject<T extends SafeCloseable> {
+ private final Function<LauncherAppComponent, T> mFunction;
+
+ public DaggerSingletonObject(Function<LauncherAppComponent, T> function) {
+ mFunction = function;
+ }
+
+ public T get(Context context) {
+ LauncherAppComponent component =
+ ((LauncherApplication) context.getApplicationContext()).getAppComponent();
+ return mFunction.apply(component);
+ }
+}
diff --git a/src/com/android/launcher3/util/DaggerSingletonTracker.java b/src/com/android/launcher3/util/DaggerSingletonTracker.java
new file mode 100644
index 0000000..2946da1
--- /dev/null
+++ b/src/com/android/launcher3/util/DaggerSingletonTracker.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util;
+
+import com.android.launcher3.dagger.LauncherAppSingleton;
+
+import java.util.ArrayList;
+
+import javax.inject.Inject;
+
+/**
+ * A tracker class for keeping track of Dagger created singletons.
+ * Dagger will take care of creating singletons. But we should take care of unregistering callbacks
+ * if at all registered during singleton construction.
+ * All singletons should be declared as SafeCloseable so that we can call close() method.
+ */
+@LauncherAppSingleton
+public class DaggerSingletonTracker implements SafeCloseable {
+
+ private final ArrayList<SafeCloseable> mLauncherAppSingletons = new ArrayList<>();
+
+ @Inject
+ DaggerSingletonTracker() {
+ }
+
+ /**
+ * Adds the SafeCloseable Singletons to the mLauncherAppSingletons list.
+ * This helps to track the singletons and close them appropriately.
+ * See {@link DaggerSingletonTracker#close()} and
+ * {@link MainThreadInitializedObject.SandboxContext#onDestroy()}
+ */
+ public void addCloseable(SafeCloseable closeable) {
+ mLauncherAppSingletons.add(closeable);
+ }
+
+ @Override
+ public void close() {
+ // Destroy in reverse order
+ for (int i = mLauncherAppSingletons.size() - 1; i >= 0; i--) {
+ mLauncherAppSingletons.get(i).close();
+ }
+ }
+}
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index 072bcdf..c59cc81 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -15,7 +15,6 @@
*/
package com.android.launcher3.util;
-import static android.content.Intent.ACTION_CONFIGURATION_CHANGED;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
@@ -33,7 +32,6 @@
import static com.android.launcher3.util.window.WindowManagerProxy.MIN_TABLET_WIDTH;
import android.annotation.SuppressLint;
-import android.annotation.TargetApi;
import android.content.ComponentCallbacks;
import android.content.Context;
import android.content.Intent;
@@ -42,7 +40,6 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
-import android.os.Build;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -132,21 +129,15 @@
}
Display display = mDM.getDisplay(DEFAULT_DISPLAY);
- if (Utilities.ATLEAST_S) {
- mWindowContext = mContext.createWindowContext(display, TYPE_APPLICATION, null);
- mWindowContext.registerComponentCallbacks(this);
- } else {
- mWindowContext = null;
- mReceiver.register(mContext, ACTION_CONFIGURATION_CHANGED);
- }
+ mWindowContext = mContext.createWindowContext(display, TYPE_APPLICATION, null);
+ mWindowContext.registerComponentCallbacks(this);
// Initialize navigation mode change listener
mReceiver.registerPkgActions(mContext, TARGET_OVERLAY_PACKAGE, ACTION_OVERLAY_CHANGED);
WindowManagerProxy wmProxy = WindowManagerProxy.INSTANCE.get(context);
- Context displayInfoContext = getDisplayInfoContext(display);
- mInfo = new Info(displayInfoContext, wmProxy,
- wmProxy.estimateInternalDisplayBounds(displayInfoContext));
+ mInfo = new Info(mWindowContext, wmProxy,
+ wmProxy.estimateInternalDisplayBounds(mWindowContext));
FileLog.i(TAG, "(CTOR) perDisplayBounds: " + mInfo.mPerDisplayBounds);
}
@@ -161,7 +152,7 @@
&& mInfo.mIsTaskbarPinnedInDesktopMode != prefs.get(
TASKBAR_PINNING_IN_DESKTOP_MODE);
if (isTaskbarPinningChanged || isTaskbarPinningDesktopModeChanged) {
- handleInfoChange(mWindowContext.getDisplay());
+ notifyConfigChange();
}
};
@@ -188,13 +179,6 @@
}
/**
- * Handles info change for desktop mode.
- */
- public static void handleInfoChangeForDesktopMode(Context context) {
- INSTANCE.get(context).handleInfoChange(context.getDisplay());
- }
-
- /**
* Enables transient taskbar status for tests.
*/
@VisibleForTesting
@@ -217,6 +201,13 @@
return INSTANCE.get(context).getInfo().isPinnedTaskbar();
}
+ /**
+ * Returns whether the taskbar is forced to be pinned when home is visible.
+ */
+ public static boolean showLockedTaskbarOnHome(Context context) {
+ return INSTANCE.get(context).getInfo().showLockedTaskbarOnHome();
+ }
+
@Override
public void close() {
mDestroyed = true;
@@ -252,36 +243,22 @@
if (mDestroyed) {
return;
}
- boolean reconfigure = false;
if (ACTION_OVERLAY_CHANGED.equals(intent.getAction())) {
- reconfigure = true;
- } else if (ACTION_CONFIGURATION_CHANGED.equals(intent.getAction())) {
- Configuration config = mContext.getResources().getConfiguration();
- reconfigure = mInfo.fontScale != config.fontScale
- || mInfo.densityDpi != config.densityDpi;
- }
-
- if (reconfigure) {
- Log.d(TAG, "Configuration changed, notifying listeners");
- Display display = mDM.getDisplay(DEFAULT_DISPLAY);
- if (display != null) {
- handleInfoChange(display);
- }
+ Log.d(TAG, "Overlay changed, notifying listeners");
+ notifyConfigChange();
}
}
@UiThread
@Override
- @TargetApi(Build.VERSION_CODES.S)
public final void onConfigurationChanged(Configuration config) {
Log.d(TASKBAR_NOT_DESTROYED_TAG, "DisplayController#onConfigurationChanged: " + config);
- Display display = mWindowContext.getDisplay();
if (config.densityDpi != mInfo.densityDpi
|| config.fontScale != mInfo.fontScale
- || display.getRotation() != mInfo.rotation
|| !mInfo.mScreenSizeDp.equals(
- new PortraitSize(config.screenHeightDp, config.screenWidthDp))) {
- handleInfoChange(display);
+ new PortraitSize(config.screenHeightDp, config.screenWidthDp))
+ || mWindowContext.getDisplay().getRotation() != mInfo.rotation) {
+ notifyConfigChange();
}
}
@@ -304,17 +281,12 @@
return mInfo;
}
- private Context getDisplayInfoContext(Display display) {
- return Utilities.ATLEAST_S ? mWindowContext : mContext.createDisplayContext(display);
- }
-
@AnyThread
- @VisibleForTesting
- public void handleInfoChange(Display display) {
+ public void notifyConfigChange() {
WindowManagerProxy wmProxy = WindowManagerProxy.INSTANCE.get(mContext);
Info oldInfo = mInfo;
- Context displayInfoContext = getDisplayInfoContext(display);
+ Context displayInfoContext = mWindowContext;
Info newInfo = new Info(displayInfoContext, wmProxy, oldInfo.mPerDisplayBounds);
if (newInfo.densityDpi != oldInfo.densityDpi || newInfo.fontScale != oldInfo.fontScale
@@ -345,7 +317,8 @@
}
if ((newInfo.mIsTaskbarPinned != oldInfo.mIsTaskbarPinned)
|| (newInfo.mIsTaskbarPinnedInDesktopMode
- != oldInfo.mIsTaskbarPinnedInDesktopMode)) {
+ != oldInfo.mIsTaskbarPinnedInDesktopMode)
+ || newInfo.isPinnedTaskbar() != oldInfo.isPinnedTaskbar()) {
change |= CHANGE_TASKBAR_PINNING;
}
if (newInfo.mIsInDesktopMode != oldInfo.mIsInDesktopMode) {
@@ -399,6 +372,9 @@
private final boolean mIsInDesktopMode;
+ private final boolean mShowLockedTaskbarOnHome;
+ private final boolean mIsHomeVisible;
+
public Info(Context displayInfoContext) {
/* don't need system overrides for external displays */
this(displayInfoContext, new WindowManagerProxy(), new ArrayMap<>());
@@ -460,6 +436,8 @@
mIsTaskbarPinnedInDesktopMode = LauncherPrefs.get(displayInfoContext).get(
TASKBAR_PINNING_IN_DESKTOP_MODE);
mIsInDesktopMode = wmProxy.isInDesktopMode();
+ mShowLockedTaskbarOnHome = wmProxy.showLockedTaskbarOnHome(displayInfoContext);
+ mIsHomeVisible = wmProxy.isHomeVisible(displayInfoContext);
}
/**
@@ -476,6 +454,10 @@
return sTransientTaskbarStatusForTests;
}
if (enableTaskbarPinning()) {
+ // If Launcher is visible on the freeform display, ensure the taskbar is pinned.
+ if (mShowLockedTaskbarOnHome && mIsHomeVisible) {
+ return false;
+ }
if (mIsInDesktopMode) {
return !mIsTaskbarPinnedInDesktopMode;
}
@@ -543,6 +525,13 @@
return TYPE_PHONE;
}
}
+
+ /**
+ * Returns whether the taskbar is forced to be pinned when home is visible.
+ */
+ public boolean showLockedTaskbarOnHome() {
+ return mShowLockedTaskbarOnHome;
+ }
}
/**
diff --git a/src/com/android/launcher3/util/EdgeEffectCompat.java b/src/com/android/launcher3/util/EdgeEffectCompat.java
index ca37259..a949f50 100644
--- a/src/com/android/launcher3/util/EdgeEffectCompat.java
+++ b/src/com/android/launcher3/util/EdgeEffectCompat.java
@@ -19,8 +19,6 @@
import android.view.MotionEvent;
import android.widget.EdgeEffect;
-import com.android.launcher3.Utilities;
-
/**
* Extension of {@link EdgeEffect} to allow backwards compatibility
*/
@@ -30,21 +28,6 @@
super(context);
}
- @Override
- public float getDistance() {
- return Utilities.ATLEAST_S ? super.getDistance() : 0;
- }
-
- @Override
- public float onPullDistance(float deltaDistance, float displacement) {
- if (Utilities.ATLEAST_S) {
- return super.onPullDistance(deltaDistance, displacement);
- } else {
- onPull(deltaDistance, displacement);
- return deltaDistance;
- }
- }
-
public float onPullDistance(float deltaDistance, float displacement, MotionEvent ev) {
return onPullDistance(deltaDistance, displacement);
}
diff --git a/src/com/android/launcher3/util/ExecutorUtil.java b/src/com/android/launcher3/util/ExecutorUtil.java
new file mode 100644
index 0000000..efc0eec
--- /dev/null
+++ b/src/com/android/launcher3/util/ExecutorUtil.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util;
+
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
+import android.os.Looper;
+
+import java.util.concurrent.ExecutionException;
+
+public final class ExecutorUtil {
+
+ /**
+ * Executes runnable on {@link Looper#getMainLooper()}, otherwise fails with an exception.
+ */
+ public static void executeSyncOnMainOrFail(Runnable runnable) {
+ try {
+ MAIN_EXECUTOR.submit(runnable).get();
+ } catch (InterruptedException | ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/util/MainThreadInitializedObject.java b/src/com/android/launcher3/util/MainThreadInitializedObject.java
index 1a0f9a0..a7d5c13 100644
--- a/src/com/android/launcher3/util/MainThreadInitializedObject.java
+++ b/src/com/android/launcher3/util/MainThreadInitializedObject.java
@@ -18,13 +18,13 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import android.content.Context;
-import android.content.ContextWrapper;
import android.os.Looper;
import android.util.Log;
import androidx.annotation.UiThread;
import androidx.annotation.VisibleForTesting;
+import com.android.launcher3.LauncherApplication;
import com.android.launcher3.util.ResourceBasedOverride.Overrides;
import java.util.ArrayList;
@@ -35,6 +35,9 @@
/**
* Utility class for defining singletons which are initiated on main thread.
+ *
+ * TODO(b/361850561): Do not delete MainThreadInitializedObject until we find a way to
+ * unregister and understand how singleton objects are destroyed in dagger graph.
*/
public class MainThreadInitializedObject<T extends SafeCloseable> {
@@ -105,6 +108,25 @@
*/
<T extends SafeCloseable> T getObject(MainThreadInitializedObject<T> object);
+
+ /**
+ * Put a value into cache, can be used to put mocked MainThreadInitializedObject
+ * instances.
+ */
+ <T extends SafeCloseable> void putObject(MainThreadInitializedObject<T> object, T value);
+
+ /**
+ * Returns whether this context should cleanup all objects when its destroyed or leave it
+ * to the GC.
+ * These objects can have listeners attached to the system server and mey not be able to get
+ * GCed themselves when running on a device.
+ * Some environments like Robolectric tear down the whole system at the end of the test,
+ * so manual cleanup may not be required.
+ */
+ default boolean shouldCleanUpOnDestroy() {
+ return true;
+ }
+
@UiThread
default <T extends SafeCloseable> T createObject(MainThreadInitializedObject<T> object) {
return object.mProvider.get((Context) this);
@@ -115,7 +137,7 @@
* Abstract Context which allows custom implementations for
* {@link MainThreadInitializedObject} providers
*/
- public static class SandboxContext extends ContextWrapper implements SandboxApplication {
+ public static class SandboxContext extends LauncherApplication implements SandboxApplication {
private static final String TAG = "SandboxContext";
@@ -126,7 +148,8 @@
private boolean mDestroyed = false;
public SandboxContext(Context base) {
- super(base);
+ attachBaseContext(base);
+ initDagger();
}
@Override
@@ -134,7 +157,20 @@
return this;
}
+ @Override
+ public boolean shouldCleanUpOnDestroy() {
+ return (getBaseContext().getApplicationContext() instanceof SandboxApplication sa)
+ ? sa.shouldCleanUpOnDestroy() : true;
+ }
+
public void onDestroy() {
+ if (shouldCleanUpOnDestroy()) {
+ cleanUpObjects();
+ }
+ }
+
+ protected void cleanUpObjects() {
+ getAppComponent().getDaggerSingletonTracker().close();
synchronized (mDestroyLock) {
// Destroy in reverse order
for (int i = mOrderedObjects.size() - 1; i >= 0; i--) {
@@ -169,10 +205,7 @@
}
}
- /**
- * Put a value into mObjectMap, can be used to put mocked MainThreadInitializedObject
- * instances into SandboxContext.
- */
+ @Override
public <T extends SafeCloseable> void putObject(
MainThreadInitializedObject<T> object, T value) {
mObjectMap.put(object, value);
diff --git a/src/com/android/launcher3/util/MultiTranslateDelegate.java b/src/com/android/launcher3/util/MultiTranslateDelegate.java
index 84ef445..38c87c8 100644
--- a/src/com/android/launcher3/util/MultiTranslateDelegate.java
+++ b/src/com/android/launcher3/util/MultiTranslateDelegate.java
@@ -37,6 +37,7 @@
public static final int INDEX_TASKBAR_ALIGNMENT_ANIM = 3;
public static final int INDEX_TASKBAR_REVEAL_ANIM = 4;
public static final int INDEX_TASKBAR_PINNING_ANIM = 5;
+ public static final int INDEX_NAV_BAR_ANIM = 6;
// Affect all items inside of a MultipageCellLayout
public static final int INDEX_CELLAYOUT_MULTIPAGE_SPACING = 3;
@@ -47,7 +48,7 @@
// Specific for hotseat items when adjusting for bubbles
public static final int INDEX_BUBBLE_ADJUSTMENT_ANIM = 3;
- public static final int COUNT = 6;
+ public static final int COUNT = 7;
private final MultiPropertyFactory<View> mTranslationX;
private final MultiPropertyFactory<View> mTranslationY;
diff --git a/src/com/android/launcher3/util/OverlayEdgeEffect.java b/src/com/android/launcher3/util/OverlayEdgeEffect.java
index d09d801..0623af7 100644
--- a/src/com/android/launcher3/util/OverlayEdgeEffect.java
+++ b/src/com/android/launcher3/util/OverlayEdgeEffect.java
@@ -46,6 +46,7 @@
return mDistance;
}
+ @Override
public float onPullDistance(float deltaDistance, float displacement) {
// Fallback implementation, will never actually get called
if (BuildConfig.IS_DEBUG_DEVICE) {
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index 469e363..e51609a 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -24,13 +24,10 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Process;
@@ -42,7 +39,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.launcher3.Flags;
import com.android.launcher3.PendingAddItemInfo;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -89,69 +85,6 @@
public void close() { }
/**
- * Returns true if the app can possibly be on the SDCard. This is just a workaround and doesn't
- * guarantee that the app is on SD card.
- */
- public boolean isAppOnSdcard(@NonNull final String packageName,
- @NonNull final UserHandle user) {
- final ApplicationInfo info = getApplicationInfo(
- packageName, user, PackageManager.MATCH_UNINSTALLED_PACKAGES);
- return info != null && (info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
- }
-
- /**
- * Returns whether the target app is suspended for a given user as per
- * {@link android.app.admin.DevicePolicyManager#isPackageSuspended}.
- */
- public boolean isAppSuspended(@NonNull final String packageName,
- @NonNull final UserHandle user) {
- final ApplicationInfo info = getApplicationInfo(packageName, user, 0);
- return info != null && isAppSuspended(info);
- }
-
- /**
- * Returns whether the target app is installed for a given user
- */
- public boolean isAppInstalled(@NonNull final String packageName,
- @NonNull final UserHandle user) {
- final ApplicationInfo info = getApplicationInfo(packageName, user, 0);
- return info != null;
- }
-
- /**
- * Returns whether the target app is archived for a given user
- */
- @SuppressWarnings("NewApi")
- public boolean isAppArchivedForUser(@NonNull final String packageName,
- @NonNull final UserHandle user) {
- if (!Flags.enableSupportForArchiving()) {
- return false;
- }
- final ApplicationInfo info = getApplicationInfo(
- // LauncherApps does not support long flags currently. Since archived apps are
- // subset of uninstalled apps, this filter also includes archived apps.
- packageName, user, PackageManager.MATCH_UNINSTALLED_PACKAGES);
- return info != null && info.isArchived;
- }
-
- /**
- * Returns whether the target app is in archived state
- */
- @SuppressWarnings("NewApi")
- public boolean isAppArchived(@NonNull final String packageName) {
- final ApplicationInfo info;
- try {
- info = mPm.getPackageInfo(packageName,
- PackageManager.PackageInfoFlags.of(
- PackageManager.MATCH_ARCHIVED_PACKAGES)).applicationInfo;
- return info.isArchived;
- } catch (NameNotFoundException e) {
- Log.e(TAG, "Failed to get applicationInfo for package: " + packageName, e);
- return false;
- }
- }
-
- /**
* Returns the installing app package for the given package
*/
public String getAppInstallerPackage(@NonNull final String packageName) {
@@ -164,20 +97,6 @@
}
/**
- * Returns the application info for the provided package or null
- */
- @Nullable
- public ApplicationInfo getApplicationInfo(@NonNull final String packageName,
- @NonNull final UserHandle user, final int flags) {
- try {
- ApplicationInfo info = mLauncherApps.getApplicationInfo(packageName, flags, user);
- return !isPackageInstalledOrArchived(info) || !info.enabled ? null : info;
- } catch (PackageManager.NameNotFoundException e) {
- return null;
- }
- }
-
- /**
* Returns the preferred launch activity intent for a given package.
*/
@Nullable
@@ -197,14 +116,6 @@
}
/**
- * Returns whether an application is suspended as per
- * {@link android.app.admin.DevicePolicyManager#isPackageSuspended}.
- */
- public static boolean isAppSuspended(ApplicationInfo info) {
- return (info.flags & ApplicationInfo.FLAG_SUSPENDED) != 0;
- }
-
- /**
* Starts the details activity for {@code info}
*/
public static void startDetailsActivityForInfo(Context context, ItemInfo info,
@@ -236,35 +147,6 @@
}
}
- public static boolean isSystemApp(@NonNull final Context context,
- @NonNull final Intent intent) {
- PackageManager pm = context.getPackageManager();
- ComponentName cn = intent.getComponent();
- String packageName = null;
- if (cn == null) {
- ResolveInfo info = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
- if ((info != null) && (info.activityInfo != null)) {
- packageName = info.activityInfo.packageName;
- }
- } else {
- packageName = cn.getPackageName();
- }
- if (packageName == null) {
- packageName = intent.getPackage();
- }
- if (packageName != null) {
- try {
- PackageInfo info = pm.getPackageInfo(packageName, 0);
- return (info != null) && (info.applicationInfo != null) &&
- ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
- } catch (NameNotFoundException e) {
- return false;
- }
- } else {
- return false;
- }
- }
-
/**
* Returns true if the intent is a valid launch intent for a launcher activity of an app.
* This is used to identify shortcuts which are different from the ones exposed by the
@@ -303,17 +185,7 @@
/** Returns the incremental download progress for the given shortcut's app. */
public static int getLoadingProgress(LauncherActivityInfo info) {
- if (Utilities.ATLEAST_S) {
- return (int) (100 * info.getLoadingProgress());
- }
- return 100;
- }
-
- /** Returns true in case app is installed on the device or in archived state. */
- @SuppressWarnings("NewApi")
- private boolean isPackageInstalledOrArchived(ApplicationInfo info) {
- return (info.flags & ApplicationInfo.FLAG_INSTALLED) != 0 || (
- Flags.enableSupportForArchiving() && info.isArchived);
+ return (int) (100 * info.getLoadingProgress());
}
/**
diff --git a/src/com/android/launcher3/util/SystemUiController.java b/src/com/android/launcher3/util/SystemUiController.java
index df54fd7..368b267 100644
--- a/src/com/android/launcher3/util/SystemUiController.java
+++ b/src/com/android/launcher3/util/SystemUiController.java
@@ -17,7 +17,6 @@
package com.android.launcher3.util;
import android.view.View;
-import android.view.Window;
import androidx.annotation.IntDef;
@@ -54,11 +53,11 @@
})
public @interface SystemUiControllerFlags {}
- private final Window mWindow;
+ private final View mView;
private final int[] mStates = new int[5];
- public SystemUiController(Window window) {
- mWindow = window;
+ public SystemUiController(View view) {
+ mView = view;
}
public void updateUiState(int uiState, boolean isLight) {
@@ -72,14 +71,14 @@
}
mStates[uiState] = flags;
- int oldFlags = mWindow.getDecorView().getSystemUiVisibility();
+ int oldFlags = mView.getSystemUiVisibility();
// Apply the state flags in priority order
int newFlags = oldFlags;
for (int stateFlag : mStates) {
newFlags = getSysUiVisibilityFlags(stateFlag, newFlags);
}
if (newFlags != oldFlags) {
- mWindow.getDecorView().setSystemUiVisibility(newFlags);
+ mView.setSystemUiVisibility(newFlags);
}
}
@@ -88,7 +87,7 @@
*/
public int getBaseSysuiVisibility() {
return getSysUiVisibilityFlags(
- mStates[UI_STATE_BASE_WINDOW], mWindow.getDecorView().getSystemUiVisibility());
+ mStates[UI_STATE_BASE_WINDOW], mView.getSystemUiVisibility());
}
private int getSysUiVisibilityFlags(int stateFlag, int currentVisibility) {
diff --git a/src/com/android/launcher3/util/Themes.java b/src/com/android/launcher3/util/Themes.java
index 60951ba..104040a 100644
--- a/src/com/android/launcher3/util/Themes.java
+++ b/src/com/android/launcher3/util/Themes.java
@@ -52,10 +52,8 @@
}
public static int getActivityThemeRes(Context context, int wallpaperColorHints) {
- boolean supportsDarkText = Utilities.ATLEAST_S
- && (wallpaperColorHints & HINT_SUPPORTS_DARK_TEXT) != 0;
- boolean isMainColorDark = Utilities.ATLEAST_S
- && (wallpaperColorHints & HINT_SUPPORTS_DARK_THEME) != 0;
+ boolean supportsDarkText = (wallpaperColorHints & HINT_SUPPORTS_DARK_TEXT) != 0;
+ boolean isMainColorDark = (wallpaperColorHints & HINT_SUPPORTS_DARK_THEME) != 0;
if (Utilities.isDarkTheme(context)) {
return supportsDarkText ? R.style.AppTheme_Dark_DarkText
diff --git a/src/com/android/launcher3/util/VibratorWrapper.java b/src/com/android/launcher3/util/VibratorWrapper.java
index a4b8eb0..adb8f9d 100644
--- a/src/com/android/launcher3/util/VibratorWrapper.java
+++ b/src/com/android/launcher3/util/VibratorWrapper.java
@@ -31,8 +31,6 @@
import androidx.annotation.VisibleForTesting;
-import com.android.launcher3.Utilities;
-
/**
* Wrapper around {@link Vibrator} to easily perform haptic feedback where necessary.
*/
@@ -129,7 +127,7 @@
/** Indicates that Taskbar has been invoked. */
public void vibrateForTaskbarUnstash() {
- if (Utilities.ATLEAST_S && mVibrator.areAllPrimitivesSupported(PRIMITIVE_LOW_TICK)) {
+ if (mVibrator.areAllPrimitivesSupported(PRIMITIVE_LOW_TICK)) {
VibrationEffect primitiveLowTickEffect = VibrationEffect
.startComposition()
.addPrimitive(PRIMITIVE_LOW_TICK, LOW_TICK_SCALE)
diff --git a/src/com/android/launcher3/util/WallpaperColorHints.kt b/src/com/android/launcher3/util/WallpaperColorHints.kt
index 1361c1e..11d4c25 100644
--- a/src/com/android/launcher3/util/WallpaperColorHints.kt
+++ b/src/com/android/launcher3/util/WallpaperColorHints.kt
@@ -23,7 +23,6 @@
import android.content.Context
import androidx.annotation.MainThread
import androidx.annotation.VisibleForTesting
-import com.android.launcher3.Utilities
import com.android.launcher3.util.Executors.MAIN_EXECUTOR
import com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR
@@ -34,36 +33,34 @@
class WallpaperColorHints(private val context: Context) : SafeCloseable {
var hints: Int = 0
private set
+
private val wallpaperManager
get() = context.getSystemService(WallpaperManager::class.java)!!
+
private val onColorHintsChangedListeners = mutableListOf<OnColorHintListener>()
private val onClose: SafeCloseable
init {
- if (Utilities.ATLEAST_S) {
- hints = wallpaperManager.getWallpaperColors(FLAG_SYSTEM)?.colorHints ?: 0
- val onColorsChangedListener = OnColorsChangedListener { colors, which ->
- onColorsChanged(colors, which)
- }
+ hints = wallpaperManager.getWallpaperColors(FLAG_SYSTEM)?.colorHints ?: 0
+ val onColorsChangedListener = OnColorsChangedListener { colors, which ->
+ onColorsChanged(colors, which)
+ }
+ UI_HELPER_EXECUTOR.execute {
+ wallpaperManager.addOnColorsChangedListener(
+ onColorsChangedListener,
+ MAIN_EXECUTOR.handler,
+ )
+ }
+ onClose = SafeCloseable {
UI_HELPER_EXECUTOR.execute {
- wallpaperManager.addOnColorsChangedListener(
- onColorsChangedListener,
- MAIN_EXECUTOR.handler
- )
+ wallpaperManager.removeOnColorsChangedListener(onColorsChangedListener)
}
- onClose = SafeCloseable {
- UI_HELPER_EXECUTOR.execute {
- wallpaperManager.removeOnColorsChangedListener(onColorsChangedListener)
- }
- }
- } else {
- onClose = SafeCloseable {}
}
}
@MainThread
private fun onColorsChanged(colors: WallpaperColors?, which: Int) {
- if ((which and FLAG_SYSTEM) != 0 && Utilities.ATLEAST_S) {
+ if ((which and FLAG_SYSTEM) != 0) {
val newHints = colors?.colorHints ?: 0
if (newHints != hints) {
hints = newHints
@@ -86,6 +83,7 @@
@VisibleForTesting
@JvmField
val INSTANCE = MainThreadInitializedObject { WallpaperColorHints(it) }
+
@JvmStatic fun get(context: Context): WallpaperColorHints = INSTANCE.get(context)
}
}
diff --git a/src/com/android/launcher3/util/window/WindowManagerProxy.java b/src/com/android/launcher3/util/window/WindowManagerProxy.java
index 0817c0a..84b4a36 100644
--- a/src/com/android/launcher3/util/window/WindowManagerProxy.java
+++ b/src/com/android/launcher3/util/window/WindowManagerProxy.java
@@ -32,7 +32,6 @@
import static com.android.launcher3.util.RotationUtils.rotateRect;
import static com.android.launcher3.util.RotationUtils.rotateSize;
-import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -40,7 +39,6 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
-import android.os.Build;
import android.util.ArrayMap;
import android.util.Log;
import android.view.Display;
@@ -54,7 +52,6 @@
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
import com.android.launcher3.testing.shared.ResourceUtils;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.NavigationMode;
@@ -122,6 +119,20 @@
}
/**
+ * Returns if the pinned taskbar should be shown when home is visible.
+ */
+ public boolean showLockedTaskbarOnHome(Context displayInfoContext) {
+ return false;
+ }
+
+ /**
+ * Returns if the home is visible.
+ */
+ public boolean isHomeVisible(Context context) {
+ return false;
+ }
+
+ /**
* Returns the real bounds for the provided display after applying any insets normalization
*/
public WindowBounds getRealBounds(Context displayInfoContext, CachedDisplayInfo info) {
@@ -216,7 +227,7 @@
int screenWidthPx,
@NonNull WindowInsets windowInsets,
@NonNull WindowInsets.Builder insetsBuilder) {
- if (!isLargeScreen || !Utilities.ATLEAST_S) {
+ if (!isLargeScreen) {
return;
}
@@ -391,25 +402,16 @@
/**
* Returns a CachedDisplayInfo initialized for the current display
*/
- @TargetApi(Build.VERSION_CODES.S)
public CachedDisplayInfo getDisplayInfo(Context displayInfoContext) {
int rotation = getRotation(displayInfoContext);
- if (Utilities.ATLEAST_S) {
- WindowMetrics windowMetrics = displayInfoContext.getSystemService(WindowManager.class)
- .getMaximumWindowMetrics();
- return getDisplayInfo(windowMetrics, rotation);
- } else {
- Point size = new Point();
- Display display = getDisplay(displayInfoContext);
- display.getRealSize(size);
- return new CachedDisplayInfo(size, rotation);
- }
+ WindowMetrics windowMetrics = displayInfoContext.getSystemService(WindowManager.class)
+ .getMaximumWindowMetrics();
+ return getDisplayInfo(windowMetrics, rotation);
}
/**
* Returns a CachedDisplayInfo initialized for the current display
*/
- @TargetApi(Build.VERSION_CODES.S)
protected CachedDisplayInfo getDisplayInfo(WindowMetrics windowMetrics, int rotation) {
Point size = new Point(windowMetrics.getBounds().right, windowMetrics.getBounds().bottom);
return new CachedDisplayInfo(size, rotation,
@@ -478,8 +480,7 @@
}
}
}
- return Utilities.ATLEAST_S ? NavigationMode.NO_BUTTON :
- NavigationMode.THREE_BUTTONS;
+ return NavigationMode.NO_BUTTON;
}
@Override
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index d3160e0..b8481c5 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -26,9 +26,11 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import android.app.Activity;
import android.app.ActivityOptions;
import android.app.PendingIntent;
import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
@@ -46,6 +48,7 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.AccessibilityDelegate;
+import android.view.Window;
import android.view.WindowInsets;
import android.view.WindowInsetsController;
import android.view.inputmethod.InputMethodManager;
@@ -76,10 +79,11 @@
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.util.ActivityOptionsWrapper;
-import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.ApplicationInfoWrapper;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.SplitConfigurationOptions;
+import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.ViewCache;
import com.android.launcher3.widget.picker.model.WidgetPickerDataProvider;
@@ -175,6 +179,23 @@
BaseDragLayer getDragLayer();
/**
+ * @see Activity#getWindow()
+ * @return Window
+ */
+ @Nullable
+ default Window getWindow() {
+ return null;
+ }
+
+ /**
+ * @see Activity#getComponentName()
+ * @return ComponentName
+ */
+ default ComponentName getComponentName() {
+ return null;
+ }
+
+ /**
* The all apps container, if it exists in this context.
*/
default ActivityAllAppsContainerView<?> getAppsView() {
@@ -216,6 +237,11 @@
return null;
}
+ @Nullable
+ default SystemUiController getSystemUiController() {
+ return null;
+ }
+
/**
* Handler for actions taken on drop targets that require launcher
*/
@@ -391,7 +417,7 @@
View v, Intent intent, @Nullable ItemInfo item) {
Preconditions.assertUIThread();
Context context = (Context) this;
- if (isAppBlockedForSafeMode() && !PackageManagerHelper.isSystemApp(context, intent)) {
+ if (isAppBlockedForSafeMode() && !new ApplicationInfoWrapper(context, intent).isSystem()) {
Toast.makeText(context, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
return null;
}
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index 5d2d3f4..ea3fb3f 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -107,7 +107,7 @@
protected final RectF mSystemGestureRegion = new RectF();
private int mTouchDispatchState = 0;
- protected final T mActivity;
+ protected final T mContainer;
private final MultiValueAlpha mMultiValueAlpha;
// All the touch controllers for the view
@@ -121,7 +121,7 @@
public BaseDragLayer(Context context, AttributeSet attrs, int alphaChannelCount) {
super(context, attrs);
- mActivity = ActivityContext.lookupContext(context);
+ mContainer = ActivityContext.lookupContext(context);
mMultiValueAlpha = new MultiValueAlpha(this, alphaChannelCount);
}
@@ -159,7 +159,7 @@
}
mTouchCompleteListener = null;
} else if (action == MotionEvent.ACTION_DOWN) {
- mActivity.finishAutoCancelActionMode();
+ mContainer.finishAutoCancelActionMode();
}
return findActiveController(ev);
}
@@ -173,7 +173,7 @@
}
private TouchController findControllerToHandleTouch(MotionEvent ev) {
- AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
+ AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mContainer);
if (topView != null
&& (isEventWithinSystemGestureRegion(ev)
|| topView.canInterceptEventsInSystemGestureRegion())
@@ -207,7 +207,7 @@
@Override
public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
// Shortcuts can appear above folder
- View topView = AbstractFloatingView.getTopOpenViewWithType(mActivity,
+ View topView = AbstractFloatingView.getTopOpenViewWithType(mContainer,
AbstractFloatingView.TYPE_ACCESSIBLE);
if (topView != null) {
if (child == topView) {
@@ -222,7 +222,7 @@
@Override
public void addChildrenForAccessibility(ArrayList<View> childrenForAccessibility) {
- View topView = AbstractFloatingView.getTopOpenViewWithType(mActivity,
+ View topView = AbstractFloatingView.getTopOpenViewWithType(mContainer,
AbstractFloatingView.TYPE_ACCESSIBLE);
if (topView != null) {
// Only add the top view as a child for accessibility when it is open
@@ -458,7 +458,7 @@
@Override
protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
- View topView = AbstractFloatingView.getTopOpenView(mActivity);
+ View topView = AbstractFloatingView.getTopOpenView(mContainer);
if (topView != null) {
return topView.requestFocus(direction, previouslyFocusedRect);
} else {
@@ -468,7 +468,7 @@
@Override
public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
- View topView = AbstractFloatingView.getTopOpenView(mActivity);
+ View topView = AbstractFloatingView.getTopOpenView(mContainer);
if (topView != null) {
topView.addFocusables(views, direction);
} else {
@@ -555,7 +555,7 @@
Insets gestureInsets = insets.getMandatorySystemGestureInsets();
int gestureInsetBottom = gestureInsets.bottom;
Insets imeInset = insets.getInsets(WindowInsets.Type.ime());
- DeviceProfile dp = mActivity.getDeviceProfile();
+ DeviceProfile dp = mContainer.getDeviceProfile();
if (dp.isTaskbarPresent) {
// Ignore taskbar gesture insets to avoid interfering with TouchControllers.
gestureInsetBottom = ResourceUtils.getNavbarSize(
diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
index 63648dd..6fd18be 100644
--- a/src/com/android/launcher3/views/RecyclerViewFastScroller.java
+++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
@@ -355,7 +355,10 @@
if (!sectionName.equals(mPopupSectionName)) {
mPopupSectionName = sectionName;
mPopupView.setText(sectionName);
- performHapticFeedback(CLOCK_TICK);
+ // AllApps haptics are taken care of by AllAppsFastScrollHelper.
+ if (mFastScrollerLocation != ALL_APPS_SCROLLER) {
+ performHapticFeedback(CLOCK_TICK);
+ }
}
animatePopupVisibility(!TextUtils.isEmpty(sectionName));
mLastTouchY = boundedY;
diff --git a/src/com/android/launcher3/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java
index f6c4984..ce58de1 100644
--- a/src/com/android/launcher3/views/ScrimView.java
+++ b/src/com/android/launcher3/views/ScrimView.java
@@ -29,7 +29,6 @@
import androidx.annotation.Px;
import androidx.core.graphics.ColorUtils;
-import com.android.launcher3.BaseActivity;
import com.android.launcher3.Insettable;
import com.android.launcher3.util.SystemUiController;
@@ -143,7 +142,8 @@
private SystemUiController getSystemUiController() {
if (mSystemUiController == null) {
- mSystemUiController = BaseActivity.fromContext(getContext()).getSystemUiController();
+ mSystemUiController =
+ ActivityContext.lookupContext(getContext()).getSystemUiController();
}
return mSystemUiController;
}
diff --git a/src/com/android/launcher3/views/SpringRelativeLayout.java b/src/com/android/launcher3/views/SpringRelativeLayout.java
index 923eb19..a13152e 100644
--- a/src/com/android/launcher3/views/SpringRelativeLayout.java
+++ b/src/com/android/launcher3/views/SpringRelativeLayout.java
@@ -25,8 +25,6 @@
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.EdgeEffectFactory;
-import com.android.launcher3.Utilities;
-
/**
* View group to allow rendering overscroll effect in a child at the parent level
*/
@@ -46,10 +44,8 @@
public SpringRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- mEdgeGlowTop = Utilities.ATLEAST_S
- ? new EdgeEffect(context, attrs) : new EdgeEffect(context);
- mEdgeGlowBottom = Utilities.ATLEAST_S
- ? new EdgeEffect(context, attrs) : new EdgeEffect(context);
+ mEdgeGlowTop = new EdgeEffect(context, attrs);
+ mEdgeGlowBottom = new EdgeEffect(context, attrs);
setWillNotDraw(false);
}
diff --git a/src/com/android/launcher3/widget/BaseLauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/BaseLauncherAppWidgetHostView.java
index 856f4b3..12a14c2 100644
--- a/src/com/android/launcher3/widget/BaseLauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/BaseLauncherAppWidgetHostView.java
@@ -104,7 +104,7 @@
@UiThread
private void enforceRoundedCorners() {
- if (mEnforcedCornerRadius <= 0 || !RoundedCornerEnforcement.isRoundedCornerEnabled()) {
+ if (mEnforcedCornerRadius <= 0) {
resetRoundedCorners();
return;
}
diff --git a/src/com/android/launcher3/widget/DatabaseWidgetPreviewLoader.java b/src/com/android/launcher3/widget/DatabaseWidgetPreviewLoader.java
index 2817299..ab42839 100644
--- a/src/com/android/launcher3/widget/DatabaseWidgetPreviewLoader.java
+++ b/src/com/android/launcher3/widget/DatabaseWidgetPreviewLoader.java
@@ -183,19 +183,14 @@
// Draw horizontal and vertical lines to represent individual columns.
final Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
+ boxRect = new RectF(/* left= */ 0, /* top= */ 0, /* right= */
+ previewWidthF, /* bottom= */ previewHeightF);
- if (Utilities.ATLEAST_S) {
- boxRect = new RectF(/* left= */ 0, /* top= */ 0, /* right= */
- previewWidthF, /* bottom= */ previewHeightF);
-
- p.setStyle(Paint.Style.FILL);
- p.setColor(Color.WHITE);
- float roundedCorner = mContext.getResources().getDimension(
- android.R.dimen.system_app_widget_background_radius);
- c.drawRoundRect(boxRect, roundedCorner, roundedCorner, p);
- } else {
- boxRect = drawBoxWithShadow(c, previewWidthF, previewHeightF);
- }
+ p.setStyle(Paint.Style.FILL);
+ p.setColor(Color.WHITE);
+ float roundedCorner = mContext.getResources().getDimension(
+ android.R.dimen.system_app_widget_background_radius);
+ c.drawRoundRect(boxRect, roundedCorner, roundedCorner, p);
p.setStyle(Paint.Style.STROKE);
p.setStrokeWidth(mContext.getResources()
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
index 3e4fd8c..e77ba24 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
@@ -1,7 +1,5 @@
package com.android.launcher3.widget;
-import static com.android.launcher3.Utilities.ATLEAST_S;
-
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.content.Context;
@@ -116,15 +114,13 @@
getSpanY(widgetPadding, minResizeHeight, dp.cellLayoutBorderSpacePx.y,
cellSize.y));
- if (ATLEAST_S) {
- if (maxResizeWidth > 0) {
- maxSpanX = Math.min(maxSpanX, getSpanX(widgetPadding, maxResizeWidth,
- dp.cellLayoutBorderSpacePx.x, cellSize.x));
- }
- if (maxResizeHeight > 0) {
- maxSpanY = Math.min(maxSpanY, getSpanY(widgetPadding, maxResizeHeight,
- dp.cellLayoutBorderSpacePx.y, cellSize.y));
- }
+ if (maxResizeWidth > 0) {
+ maxSpanX = Math.min(maxSpanX, getSpanX(widgetPadding, maxResizeWidth,
+ dp.cellLayoutBorderSpacePx.x, cellSize.x));
+ }
+ if (maxResizeHeight > 0) {
+ maxSpanY = Math.min(maxSpanY, getSpanY(widgetPadding, maxResizeHeight,
+ dp.cellLayoutBorderSpacePx.y, cellSize.y));
}
spanX = Math.max(spanX,
@@ -135,18 +131,16 @@
cellSize.y));
}
- if (ATLEAST_S) {
- // Ensures maxSpan >= minSpan
- maxSpanX = Math.max(maxSpanX, minSpanX);
- maxSpanY = Math.max(maxSpanY, minSpanY);
+ // Ensures maxSpan >= minSpan
+ maxSpanX = Math.max(maxSpanX, minSpanX);
+ maxSpanY = Math.max(maxSpanY, minSpanY);
- // Use targetCellWidth/Height if it is within the min/max ranges.
- // Otherwise, use the span of minWidth/Height.
- if (targetCellWidth >= minSpanX && targetCellWidth <= maxSpanX
- && targetCellHeight >= minSpanY && targetCellHeight <= maxSpanY) {
- spanX = targetCellWidth;
- spanY = targetCellHeight;
- }
+ // Use targetCellWidth/Height if it is within the min/max ranges.
+ // Otherwise, use the span of minWidth/Height.
+ if (targetCellWidth >= minSpanX && targetCellWidth <= maxSpanX
+ && targetCellHeight >= minSpanY && targetCellHeight <= maxSpanY) {
+ spanX = targetCellWidth;
+ spanY = targetCellHeight;
}
// If minSpanX/Y > spanX/Y, ignore the minSpanX/Y to match the behavior described in
@@ -213,8 +207,7 @@
}
public boolean isConfigurationOptional() {
- return ATLEAST_S
- && isReconfigurable()
+ return isReconfigurable()
&& (getWidgetFeatures() & WIDGET_FEATURE_CONFIGURATION_OPTIONAL) != 0;
}
diff --git a/src/com/android/launcher3/widget/PendingItemDragHelper.java b/src/com/android/launcher3/widget/PendingItemDragHelper.java
index 8857774..130d533 100644
--- a/src/com/android/launcher3/widget/PendingItemDragHelper.java
+++ b/src/com/android/launcher3/widget/PendingItemDragHelper.java
@@ -136,9 +136,7 @@
Drawable p = new FastBitmapDrawable(new DatabaseWidgetPreviewLoader(launcher)
.generateWidgetPreview(
createWidgetInfo.info, maxWidth, previewSizeBeforeScale));
- if (RoundedCornerEnforcement.isRoundedCornerEnabled()) {
- p = new RoundDrawableWrapper(p, mEnforcedRoundedCornersForWidget);
- }
+ p = new RoundDrawableWrapper(p, mEnforcedRoundedCornersForWidget);
preview = p;
}
diff --git a/src/com/android/launcher3/widget/RoundedCornerEnforcement.java b/src/com/android/launcher3/widget/RoundedCornerEnforcement.java
index 2e5e251..cadaf89 100644
--- a/src/com/android/launcher3/widget/RoundedCornerEnforcement.java
+++ b/src/com/android/launcher3/widget/RoundedCornerEnforcement.java
@@ -28,8 +28,6 @@
import androidx.annotation.Nullable;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
import java.util.ArrayList;
import java.util.List;
@@ -71,11 +69,6 @@
return background.getId() == android.R.id.background && background.getClipToOutline();
}
- /** Check if the app widget is in the deny list. */
- public static boolean isRoundedCornerEnabled() {
- return Utilities.ATLEAST_S && FeatureFlags.ENABLE_ENFORCED_ROUNDED_CORNERS.get();
- }
-
/**
* Computes the rounded rectangle needed for this app widget.
*
@@ -102,9 +95,6 @@
* in the given context.
*/
public static float computeEnforcedRadius(@NonNull Context context) {
- if (!Utilities.ATLEAST_S) {
- return 0;
- }
Resources res = context.getResources();
float systemRadius = res.getDimension(android.R.dimen.system_app_widget_background_radius);
float defaultRadius = res.getDimension(R.dimen.enforced_rounded_corner_max_radius);
diff --git a/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProvider.java b/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProvider.java
index 9253b37..f8dc6b0 100644
--- a/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProvider.java
+++ b/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProvider.java
@@ -24,7 +24,7 @@
import com.android.launcher3.R;
import com.android.launcher3.model.WidgetItem;
-import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.ApplicationInfoWrapper;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.ResourceBasedOverride;
@@ -62,14 +62,14 @@
// via the overridden WidgetRecommendationCategoryProvider resource.
Preconditions.assertWorkerThread();
- try (PackageManagerHelper pmHelper = new PackageManagerHelper(context)) {
- if (item.widgetInfo != null && item.widgetInfo.getComponent() != null) {
- ApplicationInfo applicationInfo = pmHelper.getApplicationInfo(
- item.widgetInfo.getComponent().getPackageName(), item.widgetInfo.getUser(),
- 0 /* flags */);
- if (applicationInfo != null) {
- return getCategoryFromApplicationCategory(applicationInfo.category);
- }
+ if (item.widgetInfo != null && item.widgetInfo.getComponent() != null) {
+ ApplicationInfo applicationInfo = new ApplicationInfoWrapper(
+ context,
+ item.widgetInfo.getComponent().getPackageName(),
+ item.widgetInfo.getUser())
+ .getInfo();
+ if (applicationInfo != null) {
+ return getCategoryFromApplicationCategory(applicationInfo.category);
}
}
return null;
diff --git a/src_no_quickstep/com/android/launcher3/dagger/LauncherAppComponent.java b/src_no_quickstep/com/android/launcher3/dagger/LauncherAppComponent.java
index 4d7f937..63d87e8 100644
--- a/src_no_quickstep/com/android/launcher3/dagger/LauncherAppComponent.java
+++ b/src_no_quickstep/com/android/launcher3/dagger/LauncherAppComponent.java
@@ -18,12 +18,10 @@
import dagger.Component;
-import javax.inject.Singleton;
-
/**
* Root component for Dagger injection for Launcher AOSP.
*/
-@Singleton
+@LauncherAppSingleton
@Component
public interface LauncherAppComponent extends LauncherBaseAppComponent {
/** Builder for aosp LauncherAppComponent. */
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index 4b926a8..2553cf9 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -183,6 +183,7 @@
</activity>
<activity-alias android:name="Activity2"
android:label="TestActivity2"
+ android:icon="@drawable/test_icon"
android:exported="true"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter>
@@ -192,6 +193,7 @@
</activity-alias>
<activity-alias android:name="Activity3"
android:label="TestActivity3"
+ android:icon="@drawable/test_icon"
android:exported="true"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter>
@@ -201,6 +203,7 @@
</activity-alias>
<activity-alias android:name="Activity4"
android:label="TestActivity4"
+ android:icon="@drawable/test_icon"
android:exported="true"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter>
@@ -210,6 +213,7 @@
</activity-alias>
<activity-alias android:name="Activity5"
android:label="TestActivity5"
+ android:icon="@drawable/test_icon"
android:exported="true"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter>
@@ -219,6 +223,7 @@
</activity-alias>
<activity-alias android:name="Activity6"
android:label="TestActivity6"
+ android:icon="@drawable/test_icon"
android:exported="true"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter>
@@ -228,6 +233,7 @@
</activity-alias>
<activity-alias android:name="Activity7"
android:label="TestActivity7"
+ android:icon="@drawable/test_icon"
android:exported="true"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter>
@@ -237,6 +243,7 @@
</activity-alias>
<activity-alias android:name="Activity8"
android:label="TestActivity8"
+ android:icon="@drawable/test_icon"
android:exported="true"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter>
@@ -246,6 +253,7 @@
</activity-alias>
<activity-alias android:name="Activity9" android:exported="true"
android:label="TestActivity9"
+ android:icon="@drawable/test_icon"
android:targetActivity="com.android.launcher3.testcomponent.OtherBaseTestingActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -254,6 +262,7 @@
</activity-alias>
<activity-alias android:name="Activity10" android:exported="true"
android:label="TestActivity10"
+ android:icon="@drawable/test_icon"
android:targetActivity="com.android.launcher3.testcomponent.OtherBaseTestingActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -262,6 +271,7 @@
</activity-alias>
<activity-alias android:name="Activity11" android:exported="true"
android:label="TestActivity11"
+ android:icon="@drawable/test_icon"
android:targetActivity="com.android.launcher3.testcomponent.OtherBaseTestingActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -270,6 +280,7 @@
</activity-alias>
<activity-alias android:name="Activity12" android:exported="true"
android:label="TestActivity12"
+ android:icon="@drawable/test_icon"
android:targetActivity="com.android.launcher3.testcomponent.OtherBaseTestingActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -278,6 +289,7 @@
</activity-alias>
<activity-alias android:name="Activity13" android:exported="true"
android:label="TestActivity13"
+ android:icon="@drawable/test_icon"
android:targetActivity="com.android.launcher3.testcomponent.OtherBaseTestingActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -286,6 +298,7 @@
</activity-alias>
<activity-alias android:name="Activity14" android:exported="true"
android:label="TestActivity14"
+ android:icon="@drawable/test_icon"
android:targetActivity="com.android.launcher3.testcomponent.OtherBaseTestingActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -363,7 +376,7 @@
</activity>
<activity android:name="com.android.launcher3.testcomponent.ImeTestActivity"
android:label="ImeTestActivity"
- android:icon="@drawable/test_theme_icon"
+ android:icon="@drawable/test_icon"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java b/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
index ea58136..d7dd40b 100644
--- a/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
+++ b/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
@@ -170,7 +170,7 @@
public static final String UIOBJECT_STALE_ELEMENT = "b/319501259";
public static final String TEST_DRAG_APP_ICON_TO_MULTIPLE_WORKSPACES_FAILURE = "b/326908466";
public static final String WIDGET_CONFIG_NULL_EXTRA_INTENT = "b/324419890";
- public static final String OVERVIEW_SELECT_TOOLTIP_MISALIGNED = "b/332485341";
+
public static final String REQUEST_FLAG_ENABLE_GRID_ONLY_OVERVIEW = "enable-grid-only-overview";
public static final String REQUEST_FLAG_ENABLE_APP_PAIRS = "enable-app-pairs";
diff --git a/tests/multivalentTests/src/com/android/launcher3/RoboObjectInitializer.kt b/tests/multivalentTests/src/com/android/launcher3/RoboObjectInitializer.kt
deleted file mode 100644
index c5f9f86..0000000
--- a/tests/multivalentTests/src/com/android/launcher3/RoboObjectInitializer.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3
-
-import com.android.launcher3.util.MainThreadInitializedObject
-import com.android.launcher3.util.MainThreadInitializedObject.SandboxApplication
-import com.android.launcher3.util.SafeCloseable
-
-/**
- * Initializes [MainThreadInitializedObject] instances for Robolectric tests.
- *
- * Unlike instrumentation tests, Robolectric creates a new application instance for each test, which
- * could cause the various static objects defined in [MainThreadInitializedObject] to leak. Thus, a
- * [SandboxApplication] for Robolectric tests can implement this interface to limit the lifecycle of
- * these objects to a single test.
- */
-interface RoboObjectInitializer {
-
- /** Overrides an object with [type] to [value]. */
- fun <T : SafeCloseable> initializeObject(type: MainThreadInitializedObject<T>, value: T)
-}
diff --git a/tests/multivalentTests/src/com/android/launcher3/allapps/FloatingHeaderViewTests.kt b/tests/multivalentTests/src/com/android/launcher3/allapps/FloatingHeaderViewTest.kt
similarity index 98%
rename from tests/multivalentTests/src/com/android/launcher3/allapps/FloatingHeaderViewTests.kt
rename to tests/multivalentTests/src/com/android/launcher3/allapps/FloatingHeaderViewTest.kt
index ac2c553..d2103ae 100644
--- a/tests/multivalentTests/src/com/android/launcher3/allapps/FloatingHeaderViewTests.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/allapps/FloatingHeaderViewTest.kt
@@ -31,7 +31,7 @@
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
-class FloatingHeaderViewTests {
+class FloatingHeaderViewTest {
@get:Rule val mSetFlagsRule = SetFlagsRule()
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/FolderIconLoadTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/FolderIconLoadTest.kt
index d002493..371bac2 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/FolderIconLoadTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/model/FolderIconLoadTest.kt
@@ -18,6 +18,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.launcher3.LauncherAppState
+import com.android.launcher3.icons.BitmapInfo
import com.android.launcher3.model.data.WorkspaceItemInfo
import com.android.launcher3.util.Executors
import com.android.launcher3.util.LauncherLayoutBuilder
@@ -58,7 +59,7 @@
TEST_ACTIVITY11,
TEST_ACTIVITY12,
TEST_ACTIVITY13,
- TEST_ACTIVITY14
+ TEST_ACTIVITY14,
)
@Before
@@ -169,6 +170,9 @@
assertWithMessage("Index $index was not highRes")
.that(items[index].bitmap.isNullOrLowRes)
.isFalse()
+ assertWithMessage("Index $index was the default icon")
+ .that(isDefaultIcon(items[index].bitmap))
+ .isFalse()
}
}
@@ -177,9 +181,17 @@
assertWithMessage("Index $index was not lowRes")
.that(items[index].bitmap.isNullOrLowRes)
.isTrue()
+ assertWithMessage("Index $index was the default icon")
+ .that(isDefaultIcon(items[index].bitmap))
+ .isFalse()
}
}
+ private fun isDefaultIcon(bitmap: BitmapInfo) =
+ LauncherAppState.getInstance(modelHelper.sandboxContext)
+ .iconCache
+ .isDefaultIcon(bitmap, modelHelper.sandboxContext.user)
+
/** Recreate DeviceProfiles after changing InvariantDeviceProfile */
private fun recreateSupportedDeviceProfiles() {
LauncherAppState.getIDP(modelHelper.sandboxContext).supportedProfiles =
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt
index 761f06d..f57e8a1 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt
@@ -45,7 +45,6 @@
private lateinit var modelHelper: LauncherModelHelper
private lateinit var context: Context
- private lateinit var validPackages: Set<String>
private lateinit var idp: InvariantDeviceProfile
private lateinit var dbHelper: DatabaseHelper
private lateinit var db: SQLiteDatabase
@@ -68,24 +67,10 @@
DatabaseHelper(
context,
null,
- UserCache.INSTANCE.get(context)::getSerialNumberForUser
+ UserCache.INSTANCE.get(context)::getSerialNumberForUser,
) {}
db = dbHelper.writableDatabase
- validPackages =
- setOf(
- testPackage1,
- testPackage2,
- testPackage3,
- testPackage4,
- testPackage5,
- testPackage6,
- testPackage7,
- testPackage8,
- testPackage9,
- testPackage10
- )
-
idp = InvariantDeviceProfile.INSTANCE[context]
val userSerial = UserCache.INSTANCE[context].getSerialNumberForUser(Process.myUserHandle())
LauncherDbUtils.dropTable(db, TMP_TABLE)
@@ -126,8 +111,8 @@
idp.numDatabaseHotseatIcons = 4
idp.numColumns = 4
idp.numRows = 4
- val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
- val destReader = DbReader(db, TABLE_NAME, context, validPackages)
+ val srcReader = DbReader(db, TMP_TABLE, context)
+ val destReader = DbReader(db, TABLE_NAME, context)
GridSizeMigrationUtil.migrate(
dbHelper,
srcReader,
@@ -135,7 +120,7 @@
idp.numDatabaseHotseatIcons,
Point(idp.numColumns, idp.numRows),
DeviceGridState(context),
- DeviceGridState(idp)
+ DeviceGridState(idp),
)
// Check hotseat items
@@ -147,9 +132,8 @@
null,
SCREEN,
null,
- null
- )
- ?: throw IllegalStateException()
+ null,
+ ) ?: throw IllegalStateException()
assertThat(c.count).isEqualTo(idp.numDatabaseHotseatIcons)
@@ -178,9 +162,8 @@
null,
null,
null,
- null
- )
- ?: throw IllegalStateException()
+ null,
+ ) ?: throw IllegalStateException()
intentIndex = c.getColumnIndex(INTENT)
val cellXIndex = c.getColumnIndex(CELLX)
@@ -238,8 +221,8 @@
idp.numDatabaseHotseatIcons = 4
idp.numColumns = 4
idp.numRows = 4
- val readerGridA = DbReader(db, TMP_TABLE, context, validPackages)
- val readerGridB = DbReader(db, TABLE_NAME, context, validPackages)
+ val readerGridA = DbReader(db, TMP_TABLE, context)
+ val readerGridB = DbReader(db, TABLE_NAME, context)
// migrate from A -> B
GridSizeMigrationUtil.migrate(
dbHelper,
@@ -248,7 +231,7 @@
idp.numDatabaseHotseatIcons,
Point(idp.numColumns, idp.numRows),
DeviceGridState(context),
- DeviceGridState(idp)
+ DeviceGridState(idp),
)
// Check hotseat items in grid B
@@ -260,15 +243,14 @@
null,
SCREEN,
null,
- null
- )
- ?: throw IllegalStateException()
+ null,
+ ) ?: throw IllegalStateException()
// Expected hotseat items in grid B
// 2 1 3 4
verifyHotseat(
c,
idp,
- mutableListOf(testPackage2, testPackage1, testPackage3, testPackage4).toList()
+ mutableListOf(testPackage2, testPackage1, testPackage3, testPackage4).toList(),
)
// Check workspace items in grid B
@@ -280,9 +262,8 @@
null,
null,
null,
- null
- )
- ?: throw IllegalStateException()
+ null,
+ ) ?: throw IllegalStateException()
var locMap = parseLocMap(c)
// Expected items in grid B
// _ _ _ _
@@ -306,7 +287,7 @@
5,
Point(5, 5),
DeviceGridState(idp),
- DeviceGridState(context)
+ DeviceGridState(context),
)
// Check hotseat items in grid A
c =
@@ -317,15 +298,14 @@
null,
SCREEN,
null,
- null
- )
- ?: throw IllegalStateException()
+ null,
+ ) ?: throw IllegalStateException()
// Expected hotseat items in grid A
// 1 2 _ 3 4
verifyHotseat(
c,
idp,
- mutableListOf(testPackage1, testPackage2, null, testPackage3, testPackage4).toList()
+ mutableListOf(testPackage1, testPackage2, null, testPackage3, testPackage4).toList(),
)
// Check workspace items in grid A
@@ -337,9 +317,8 @@
null,
null,
null,
- null
- )
- ?: throw IllegalStateException()
+ null,
+ ) ?: throw IllegalStateException()
locMap = parseLocMap(c)
// Expected workspace items in grid A
// _ _ _ _ _
@@ -367,7 +346,7 @@
idp.numDatabaseHotseatIcons,
Point(idp.numColumns, idp.numRows),
DeviceGridState(context),
- DeviceGridState(idp)
+ DeviceGridState(idp),
)
// Check hotseat items in grid B
@@ -379,15 +358,14 @@
null,
SCREEN,
null,
- null
- )
- ?: throw IllegalStateException()
+ null,
+ ) ?: throw IllegalStateException()
// Expected hotseat items in grid B
// 2 1 3 4
verifyHotseat(
c,
idp,
- mutableListOf(testPackage2, testPackage1, testPackage3, testPackage4).toList()
+ mutableListOf(testPackage2, testPackage1, testPackage3, testPackage4).toList(),
)
// Check workspace items in grid B
@@ -399,9 +377,8 @@
null,
null,
null,
- null
- )
- ?: throw IllegalStateException()
+ null,
+ ) ?: throw IllegalStateException()
locMap = parseLocMap(c)
// Expected workspace items in grid B
// _ _ _ _
@@ -455,7 +432,7 @@
0,
testPackage1,
1,
- TMP_TABLE
+ TMP_TABLE,
),
addItem(
ITEM_TYPE_DEEP_SHORTCUT,
@@ -465,7 +442,7 @@
0,
testPackage2,
2,
- TMP_TABLE
+ TMP_TABLE,
),
addItem(
ITEM_TYPE_APPLICATION,
@@ -475,7 +452,7 @@
0,
testPackage3,
3,
- TMP_TABLE
+ TMP_TABLE,
),
addItem(
ITEM_TYPE_DEEP_SHORTCUT,
@@ -485,15 +462,15 @@
0,
testPackage4,
4,
- TMP_TABLE
- )
+ TMP_TABLE,
+ ),
)
val numSrcDatabaseHotseatIcons = srcHotseatItems.size
idp.numDatabaseHotseatIcons = 6
idp.numColumns = 4
idp.numRows = 4
- val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
- val destReader = DbReader(db, TABLE_NAME, context, validPackages)
+ val srcReader = DbReader(db, TMP_TABLE, context)
+ val destReader = DbReader(db, TABLE_NAME, context)
GridSizeMigrationUtil.migrate(
dbHelper,
srcReader,
@@ -501,7 +478,7 @@
idp.numDatabaseHotseatIcons,
Point(idp.numColumns, idp.numRows),
DeviceGridState(context),
- DeviceGridState(idp)
+ DeviceGridState(idp),
)
// Check hotseat items
@@ -513,9 +490,8 @@
null,
SCREEN,
null,
- null
- )
- ?: throw IllegalStateException()
+ null,
+ ) ?: throw IllegalStateException()
assertThat(c.count.toLong()).isEqualTo(numSrcDatabaseHotseatIcons.toLong())
val screenIndex = c.getColumnIndex(SCREEN)
@@ -550,8 +526,8 @@
idp.numDatabaseHotseatIcons = 4
idp.numColumns = 4
idp.numRows = 4
- val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
- val destReader = DbReader(db, TABLE_NAME, context, validPackages)
+ val srcReader = DbReader(db, TMP_TABLE, context)
+ val destReader = DbReader(db, TABLE_NAME, context)
GridSizeMigrationUtil.migrate(
dbHelper,
srcReader,
@@ -559,7 +535,7 @@
idp.numDatabaseHotseatIcons,
Point(idp.numColumns, idp.numRows),
DeviceGridState(context),
- DeviceGridState(idp)
+ DeviceGridState(idp),
)
// Check hotseat items
@@ -571,9 +547,8 @@
null,
SCREEN,
null,
- null
- )
- ?: throw IllegalStateException()
+ null,
+ ) ?: throw IllegalStateException()
assertThat(c.count.toLong()).isEqualTo(idp.numDatabaseHotseatIcons.toLong())
val screenIndex = c.getColumnIndex(SCREEN)
@@ -617,8 +592,8 @@
idp.numDatabaseHotseatIcons = 4
idp.numColumns = 5
idp.numRows = 5
- val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
- val destReader = DbReader(db, TABLE_NAME, context, validPackages)
+ val srcReader = DbReader(db, TMP_TABLE, context)
+ val destReader = DbReader(db, TABLE_NAME, context)
GridSizeMigrationUtil.migrate(
dbHelper,
srcReader,
@@ -626,7 +601,7 @@
idp.numDatabaseHotseatIcons,
Point(idp.numColumns, idp.numRows),
DeviceGridState(context),
- DeviceGridState(idp)
+ DeviceGridState(idp),
)
// Get workspace items
@@ -638,9 +613,8 @@
null,
null,
null,
- null
- )
- ?: throw IllegalStateException()
+ null,
+ ) ?: throw IllegalStateException()
val intentIndex = c.getColumnIndex(INTENT)
val screenIndex = c.getColumnIndex(SCREEN)
@@ -678,8 +652,8 @@
idp.numDatabaseHotseatIcons = 4
idp.numColumns = 4
idp.numRows = 4
- val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
- val destReader = DbReader(db, TABLE_NAME, context, validPackages)
+ val srcReader = DbReader(db, TMP_TABLE, context)
+ val destReader = DbReader(db, TABLE_NAME, context)
GridSizeMigrationUtil.migrate(
dbHelper,
srcReader,
@@ -687,7 +661,7 @@
idp.numDatabaseHotseatIcons,
Point(idp.numColumns, idp.numRows),
DeviceGridState(context),
- DeviceGridState(idp)
+ DeviceGridState(idp),
)
// Get workspace items
@@ -699,9 +673,8 @@
null,
null,
null,
- null
- )
- ?: throw IllegalStateException()
+ null,
+ ) ?: throw IllegalStateException()
val intentIndex = c.getColumnIndex(INTENT)
val screenIndex = c.getColumnIndex(SCREEN)
@@ -732,7 +705,7 @@
container: Int,
x: Int,
y: Int,
- packageName: String?
+ packageName: String?,
): Int {
return addItem(
type,
@@ -742,7 +715,7 @@
y,
packageName,
dbHelper.generateNewItemId(),
- TABLE_NAME
+ TABLE_NAME,
)
}
@@ -754,7 +727,7 @@
y: Int,
packageName: String?,
id: Int,
- tableName: String
+ tableName: String,
): Int {
val values = ContentValues()
values.put(_ID, id)
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt
index 1d9c161..7529ba9 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt
@@ -118,11 +118,17 @@
`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 {
@@ -135,11 +141,6 @@
whenever(getAppLaunchIntent(mComponentName.packageName, mUserHandle))
.thenReturn(intent)
}
- mockLauncherApps =
- mock<LauncherApps>().apply {
- whenever(isPackageEnabled("package", mUserHandle)).thenReturn(true)
- whenever(isActivityEnabled(mComponentName, mUserHandle)).thenReturn(true)
- }
mockCursor =
mock(LoaderCursor::class.java, RETURNS_DEEP_STUBS).apply {
user = mUserHandle
@@ -193,7 +194,7 @@
pendingPackages: MutableSet<PackageUserKey> = mPendingPackages,
unlockedUsers: LongSparseArray<Boolean> = mUnlockedUsersArray,
installingPkgs: HashMap<PackageUserKey, PackageInstaller.SessionInfo> = mInstallingPkgs,
- allDeepShortcuts: MutableList<ShortcutInfo> = mAllDeepShortcuts
+ allDeepShortcuts: MutableList<ShortcutInfo> = mAllDeepShortcuts,
) =
WorkspaceItemProcessor(
c = cursor,
@@ -212,7 +213,7 @@
isSdCardReady = isSdCardReady,
shortcutKeyToPinnedShortcuts = shortcutKeyToPinnedShortcuts,
installingPkgs = installingPkgs,
- allDeepShortcuts = allDeepShortcuts
+ allDeepShortcuts = allDeepShortcuts,
)
@Test
@@ -351,7 +352,7 @@
" targetPkg=package," +
" component=ComponentInfo{package/class}." +
" Unable to create launch Intent.",
- MISSING_INFO
+ MISSING_INFO,
)
verify(mockCursor, times(0)).checkAndAddItem(any(), any(), anyOrNull())
}
@@ -412,7 +413,7 @@
verify(mockCursor)
.markDeleted(
"Pinned shortcut not found from request. package=pkg, user=UserHandle{0}",
- "shortcut_not_found"
+ "shortcut_not_found",
)
}
@@ -549,7 +550,7 @@
val inflationResult =
WidgetInflater.InflationResult(
type = WidgetInflater.TYPE_REAL,
- widgetInfo = expectedWidgetProviderInfo
+ widgetInfo = expectedWidgetProviderInfo,
)
mockWidgetInflater =
mock<WidgetInflater>().apply {
@@ -607,7 +608,7 @@
val inflationResult =
WidgetInflater.InflationResult(
type = WidgetInflater.TYPE_PENDING,
- widgetInfo = mockProviderInfo
+ widgetInfo = mockProviderInfo,
)
mockWidgetInflater =
mock<WidgetInflater>().apply {
@@ -662,7 +663,7 @@
verify(mockCursor)
.markDeleted(
"processWidget: Unrestored Pending widget removed: id=1, appWidgetId=0, component=$expectedComponentName, restoreFlag:=4",
- LauncherRestoreEventLogger.RestoreError.APP_NOT_INSTALLED
+ LauncherRestoreEventLogger.RestoreError.APP_NOT_INSTALLED,
)
}
@@ -670,12 +671,6 @@
fun `When widget inflation result is TYPE_DELETE then mark deleted`() {
// Given
val expectedProvider = "com.google.android.testApp/com.android.testApp.testAppProvider"
- val expectedComponentName = ComponentName.unflattenFromString(expectedProvider)
- val expectedPackage = expectedComponentName!!.packageName
- mockPmHelper =
- mock<PackageManagerHelper>().apply {
- whenever(isAppArchived(expectedPackage)).thenReturn(true)
- }
mockCursor =
mock<LoaderCursor>().apply {
itemType = ITEM_TYPE_APPWIDGET
@@ -694,7 +689,7 @@
type = WidgetInflater.TYPE_DELETE,
widgetInfo = null,
reason = "test_delete_reason",
- restoreErrorType = MISSING_WIDGET_PROVIDER
+ restoreErrorType = MISSING_WIDGET_PROVIDER,
)
mockWidgetInflater =
mock<WidgetInflater>().apply {
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/ApplicationInfoWrapperTest.kt b/tests/multivalentTests/src/com/android/launcher3/util/ApplicationInfoWrapperTest.kt
new file mode 100644
index 0000000..86c3fd8
--- /dev/null
+++ b/tests/multivalentTests/src/com/android/launcher3/util/ApplicationInfoWrapperTest.kt
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util
+
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.content.pm.ApplicationInfo.FLAG_EXTERNAL_STORAGE
+import android.content.pm.ApplicationInfo.FLAG_INSTALLED
+import android.content.pm.ApplicationInfo.FLAG_SUSPENDED
+import android.content.pm.ApplicationInfo.FLAG_SYSTEM
+import android.content.pm.LauncherApps
+import android.os.UserHandle
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.Flags.FLAG_ENABLE_SUPPORT_FOR_ARCHIVING
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertNull
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.whenever
+
+/** Unit tests for {@link ApplicationInfoWrapper}. */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ApplicationInfoWrapperTest {
+
+ @get:Rule val setFlagsRule = SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT)
+
+ private lateinit var context: Context
+ private lateinit var launcherApps: LauncherApps
+
+ @Before
+ fun setup() {
+ context = Mockito.mock(Context::class.java)
+ launcherApps = Mockito.mock(LauncherApps::class.java)
+ whenever(context.getSystemService(eq(LauncherApps::class.java))).thenReturn(launcherApps)
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_SUPPORT_FOR_ARCHIVING)
+ fun archivedApp_appInfoIsNotNull() {
+ val applicationInfo = ApplicationInfo()
+ applicationInfo.isArchived = true
+ whenever(launcherApps.getApplicationInfo(eq(TEST_PACKAGE), any(), eq(TEST_USER)))
+ .thenReturn(applicationInfo)
+
+ val wrapper = ApplicationInfoWrapper(context, TEST_PACKAGE, TEST_USER)
+ assertNotNull(wrapper.getInfo())
+ assertTrue(wrapper.isArchived())
+ assertFalse(wrapper.isInstalled())
+ }
+
+ @Test
+ fun notInstalledApp_nullAppInfo() {
+ val applicationInfo = ApplicationInfo()
+ whenever(launcherApps.getApplicationInfo(eq(TEST_PACKAGE), any(), eq(TEST_USER)))
+ .thenReturn(applicationInfo)
+
+ val wrapper = ApplicationInfoWrapper(context, TEST_PACKAGE, TEST_USER)
+ assertNull(wrapper.getInfo())
+ assertFalse(wrapper.isInstalled())
+ }
+
+ @Test
+ fun appInfo_suspended() {
+ val wrapper =
+ ApplicationInfoWrapper(
+ ApplicationInfo().apply { flags = FLAG_INSTALLED.or(FLAG_SUSPENDED) }
+ )
+ assertTrue(wrapper.isSuspended())
+ }
+
+ @Test
+ fun appInfo_notSuspended() {
+ val wrapper = ApplicationInfoWrapper(ApplicationInfo())
+ assertFalse(wrapper.isSuspended())
+ }
+
+ @Test
+ fun appInfo_system() {
+ val wrapper = ApplicationInfoWrapper(ApplicationInfo().apply { flags = FLAG_SYSTEM })
+ assertTrue(wrapper.isSystem())
+ }
+
+ @Test
+ fun appInfo_notSystem() {
+ val wrapper = ApplicationInfoWrapper(ApplicationInfo())
+ assertFalse(wrapper.isSystem())
+ }
+
+ @Test
+ fun appInfo_onSDCard() {
+ val wrapper =
+ ApplicationInfoWrapper(ApplicationInfo().apply { flags = FLAG_EXTERNAL_STORAGE })
+ assertTrue(wrapper.isOnSdCard())
+ }
+
+ @Test
+ fun appInfo_notOnSDCard() {
+ val wrapper = ApplicationInfoWrapper(ApplicationInfo())
+ assertFalse(wrapper.isOnSdCard())
+ }
+
+ companion object {
+ const val TEST_PACKAGE = "com.android.test.package"
+ private val TEST_USER = UserHandle.of(3)
+ }
+}
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/DisplayControllerTest.kt b/tests/multivalentTests/src/com/android/launcher3/util/DisplayControllerTest.kt
index 41effa2..308f200 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/DisplayControllerTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/util/DisplayControllerTest.kt
@@ -38,7 +38,10 @@
import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext
import com.android.launcher3.util.window.CachedDisplayInfo
import com.android.launcher3.util.window.WindowManagerProxy
+import junit.framework.Assert.assertFalse
+import junit.framework.Assert.assertTrue
import kotlin.math.min
+import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -79,7 +82,7 @@
WindowBounds(Rect(0, 0, width, height), Rect(0, inset, 0, 0), Surface.ROTATION_0),
WindowBounds(Rect(0, 0, height, width), Rect(0, inset, 0, 0), Surface.ROTATION_90),
WindowBounds(Rect(0, 0, width, height), Rect(0, inset, 0, 0), Surface.ROTATION_180),
- WindowBounds(Rect(0, 0, height, width), Rect(0, inset, 0, 0), Surface.ROTATION_270)
+ WindowBounds(Rect(0, 0, height, width), Rect(0, inset, 0, 0), Surface.ROTATION_270),
)
private val configuration =
Configuration(appContext.resources.configuration).apply {
@@ -111,6 +114,7 @@
whenever(windowManagerProxy.getRealBounds(any(), any())).thenAnswer { i ->
bounds[i.getArgument<CachedDisplayInfo>(1).rotation]
}
+ whenever(windowManagerProxy.showLockedTaskbarOnHome(any())).thenReturn(false)
whenever(windowManagerProxy.getNavigationMode(any())).thenReturn(NavigationMode.NO_BUTTON)
// Mock context
@@ -134,6 +138,13 @@
displayController.addChangeListener(displayInfoChangeListener)
}
+ @After
+ fun tearDown() {
+ // We need to reset the taskbar mode preference override even if a test throws an exception.
+ // Otherwise, it may break the following tests' assumptions.
+ DisplayController.enableTaskbarModePreferenceForTests(false)
+ }
+
@Test
@UiThreadTest
fun testRotation() {
@@ -167,7 +178,7 @@
@UiThreadTest
fun testTaskbarPinning() {
whenever(launcherPrefs.get(TASKBAR_PINNING)).thenReturn(true)
- displayController.handleInfoChange(display)
+ displayController.notifyConfigChange()
verify(displayInfoChangeListener)
.onDisplayInfoChanged(any(), any(), eq(CHANGE_TASKBAR_PINNING))
}
@@ -176,8 +187,24 @@
@UiThreadTest
fun testTaskbarPinningChangeInDesktopMode() {
whenever(launcherPrefs.get(TASKBAR_PINNING_IN_DESKTOP_MODE)).thenReturn(false)
- displayController.handleInfoChange(display)
+ displayController.notifyConfigChange()
verify(displayInfoChangeListener)
.onDisplayInfoChanged(any(), any(), eq(CHANGE_TASKBAR_PINNING))
}
+
+ @Test
+ @UiThreadTest
+ fun testTaskbarPinningChangeInLockedTaskbarChange() {
+ whenever(windowManagerProxy.showLockedTaskbarOnHome(any())).thenReturn(true)
+ whenever(windowManagerProxy.isHomeVisible(any())).thenReturn(true)
+ whenever(windowManagerProxy.isInDesktopMode()).thenReturn(false)
+ whenever(launcherPrefs.get(TASKBAR_PINNING)).thenReturn(false)
+ DisplayController.enableTaskbarModePreferenceForTests(true)
+
+ assertTrue(displayController.getInfo().isTransientTaskbar())
+ displayController.notifyConfigChange()
+ verify(displayInfoChangeListener)
+ .onDisplayInfoChanged(any(), any(), eq(CHANGE_TASKBAR_PINNING))
+ assertFalse(displayController.getInfo().isTransientTaskbar())
+ }
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/LauncherModelHelper.java b/tests/multivalentTests/src/com/android/launcher3/util/LauncherModelHelper.java
index 2d53e29..748d376 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/LauncherModelHelper.java
+++ b/tests/multivalentTests/src/com/android/launcher3/util/LauncherModelHelper.java
@@ -22,6 +22,7 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static com.android.launcher3.util.TestUtil.runOnExecutorSync;
+import static com.android.launcher3.util.TestUtil.grantWriteSecurePermission;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -185,6 +186,8 @@
*/
public LauncherModelHelper setupDefaultLayoutProvider(LauncherLayoutBuilder builder)
throws Exception {
+ grantWriteSecurePermission();
+
InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(sandboxContext);
if (idp.numRows == 0 && idp.numColumns == 0) {
idp.numRows = idp.numColumns = idp.numDatabaseHotseatIcons = DEFAULT_GRID_SIZE;
@@ -283,11 +286,11 @@
}
@Override
- public void onDestroy() {
+ protected void cleanUpObjects() {
if (deleteContents(mDbDir)) {
mDbDir.delete();
}
- super.onDestroy();
+ super.cleanUpObjects();
}
@Override
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/PackageManagerHelperTest.java b/tests/multivalentTests/src/com/android/launcher3/util/PackageManagerHelperTest.java
deleted file mode 100644
index b5e797e..0000000
--- a/tests/multivalentTests/src/com/android/launcher3/util/PackageManagerHelperTest.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.util;
-
-import static com.android.launcher3.Flags.FLAG_ENABLE_SUPPORT_FOR_ARCHIVING;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.LauncherApps;
-import android.content.pm.PackageManager;
-import android.os.UserHandle;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-import androidx.test.platform.app.InstrumentationRegistry;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.junit.runner.RunWith;
-
-/** Unit tests for {@link PackageManagerHelper}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public final class PackageManagerHelperTest {
- @Rule
- public ExpectedException exception = ExpectedException.none();
-
- @Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
- private static final String TEST_PACKAGE = "com.android.test.package";
- private static final int TEST_USER = 2;
-
- private Context mContext;
- private LauncherApps mLauncherApps;
- private PackageManagerHelper mPackageManagerHelper;
-
- @Before
- public void setup() {
- mContext = mock(Context.class);
- mLauncherApps = mock(LauncherApps.class);
- when(mContext.getSystemService(eq(LauncherApps.class))).thenReturn(mLauncherApps);
- when(mContext.getResources()).thenReturn(
- InstrumentationRegistry.getInstrumentation().getTargetContext().getResources());
- mPackageManagerHelper = new PackageManagerHelper(mContext);
- }
-
- @Test
- @RequiresFlagsEnabled(FLAG_ENABLE_SUPPORT_FOR_ARCHIVING)
- public void getApplicationInfo_archivedApp_appInfoIsNotNull()
- throws PackageManager.NameNotFoundException {
- ApplicationInfo applicationInfo = new ApplicationInfo();
- applicationInfo.isArchived = true;
- when(mLauncherApps.getApplicationInfo(TEST_PACKAGE, 0 /* flags */,
- UserHandle.of(TEST_USER)))
- .thenReturn(applicationInfo);
-
- assertThat(mPackageManagerHelper.getApplicationInfo(TEST_PACKAGE, UserHandle.of(TEST_USER),
- 0 /* flags */))
- .isNotNull();
- }
-}
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/SystemUiControllerTest.kt b/tests/multivalentTests/src/com/android/launcher3/util/SystemUiControllerTest.kt
index 612fcd4..043bdac 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/SystemUiControllerTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/util/SystemUiControllerTest.kt
@@ -52,7 +52,7 @@
fun setup() {
MockitoAnnotations.initMocks(this)
`when`(window.decorView).thenReturn(decorView)
- underTest = SystemUiController(window)
+ underTest = SystemUiController(window.decorView)
}
@Test
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/TestUtil.java b/tests/multivalentTests/src/com/android/launcher3/util/TestUtil.java
index 3646f0c..64035da 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/TestUtil.java
+++ b/tests/multivalentTests/src/com/android/launcher3/util/TestUtil.java
@@ -26,6 +26,7 @@
import static org.junit.Assert.assertTrue;
+import android.Manifest;
import android.app.Instrumentation;
import android.app.blob.BlobHandle;
import android.app.blob.BlobStoreManager;
@@ -169,6 +170,8 @@
}
String key = Base64.encodeToString(digest, NO_WRAP | NO_PADDING);
+
+ grantWriteSecurePermission();
Settings.Secure.putString(context.getContentResolver(), LAYOUT_DIGEST_KEY, key);
wait.await();
return () ->
@@ -224,6 +227,14 @@
assertTrue(message, failed);
}
+ /**
+ * Grants [WRITE_SECURE_SETTINGS] permission in runtime.
+ */
+ public static void grantWriteSecurePermission() {
+ getInstrumentation().getUiAutomation()
+ .adoptShellPermissionIdentity(Manifest.permission.WRITE_SECURE_SETTINGS);
+ }
+
/** Interface to indicate a runnable which can throw any exception. */
public interface UncheckedRunnable {
/** Method to run the task */
diff --git a/tests/multivalentTests/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProviderTest.java b/tests/multivalentTests/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProviderTest.java
index 3024d26..8b6553f 100644
--- a/tests/multivalentTests/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProviderTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProviderTest.java
@@ -30,14 +30,16 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
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 static org.mockito.Mockito.when;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.content.Context;
-import android.content.ContextWrapper;
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherApps;
import android.os.Process;
@@ -102,13 +104,8 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mContext = new ContextWrapper(getInstrumentation().getTargetContext()) {
- @Override
- public Object getSystemService(String name) {
- return LAUNCHER_APPS_SERVICE.equals(name) ? mLauncherApps : super.getSystemService(
- name);
- }
- };
+ mContext = spy(getInstrumentation().getTargetContext());
+ doReturn(mLauncherApps).when(mContext).getSystemService(LauncherApps.class);
mTestAppInfo.flags = FLAG_INSTALLED;
mTestProfile = new InvariantDeviceProfile();
mTestProfile.numRows = 5;
@@ -132,7 +129,7 @@
mTestAppInfo.category = testCategory.getKey();
when(mLauncherApps.getApplicationInfo(/*packageName=*/ eq(TEST_PACKAGE),
- /*flags=*/ eq(0),
+ /*flags=*/ anyInt(),
/*user=*/ eq(Process.myUserHandle())))
.thenReturn(mTestAppInfo);
diff --git a/tests/res/drawable/test_icon.xml b/tests/res/drawable/test_icon.xml
new file mode 100644
index 0000000..72ebfeb
--- /dev/null
+++ b/tests/res/drawable/test_icon.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@android:color/white"/>
+ <foreground>
+ <color android:color="#FFFF0000" />
+ </foreground>
+ <monochrome>
+ <vector android:width="48dp" android:height="48dp" android:viewportWidth="48.0" android:viewportHeight="48.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M0,24L48,24 48,48, 0,48 Z"/>
+ </vector>
+ </monochrome>
+</adaptive-icon>
diff --git a/tests/src/com/android/launcher3/backuprestore/BackupAndRestoreDBSelectionTest.kt b/tests/src/com/android/launcher3/backuprestore/BackupAndRestoreDBSelectionTest.kt
index 479b201..35ac0a1 100644
--- a/tests/src/com/android/launcher3/backuprestore/BackupAndRestoreDBSelectionTest.kt
+++ b/tests/src/com/android/launcher3/backuprestore/BackupAndRestoreDBSelectionTest.kt
@@ -23,6 +23,7 @@
import com.android.launcher3.Flags
import com.android.launcher3.LauncherPrefs
import com.android.launcher3.model.ModelDbController
+import com.android.launcher3.provider.RestoreDbTask
import com.android.launcher3.util.Executors.MODEL_EXECUTOR
import com.android.launcher3.util.TestUtil
import com.android.launcher3.util.rule.BackAndRestoreRule
@@ -67,4 +68,13 @@
}
}
}
+
+ @Test
+ fun testExistingDbsAndRemovingDbs() {
+ var existingDbs = RestoreDbTask.existingDbs(getInstrumentation().targetContext)
+ assert(existingDbs.size == 4)
+ RestoreDbTask.removeOldDBs(getInstrumentation().targetContext, "launcher_4_by_4.db")
+ existingDbs = RestoreDbTask.existingDbs(getInstrumentation().targetContext)
+ assert(existingDbs.size == 1)
+ }
}
diff --git a/tests/src/com/android/launcher3/dragging/TaplDragTest.java b/tests/src/com/android/launcher3/dragging/TaplDragTest.java
index 76c1948..8fe77ac 100644
--- a/tests/src/com/android/launcher3/dragging/TaplDragTest.java
+++ b/tests/src/com/android/launcher3/dragging/TaplDragTest.java
@@ -197,7 +197,6 @@
@PlatinumTest(focusArea = "launcher")
@Test
@PortraitLandscape
- @ScreenRecordRule.ScreenRecord // b/343953783
public void testDragAppIcon() {
final HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
diff --git a/tests/src/com/android/launcher3/model/LoaderTaskTest.kt b/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
index d16674c..0dd13a9 100644
--- a/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
+++ b/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
@@ -22,6 +22,7 @@
import com.android.launcher3.util.Executors.MODEL_EXECUTOR
import com.android.launcher3.util.LauncherModelHelper.SandboxModelContext
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 java.util.concurrent.CountDownLatch
@@ -116,6 +117,8 @@
`when`(idleLock.awaitLocked(1000)).thenReturn(false)
`when`(iconCache.updateHandler).thenReturn(iconCacheUpdateHandler)
context.putObject(UserCache.INSTANCE, userCache)
+
+ TestUtil.grantWriteSecurePermission()
}
@After
diff --git a/tests/src/com/android/launcher3/model/WorkspaceItemProcessorExtraTest.kt b/tests/src/com/android/launcher3/model/WorkspaceItemProcessorExtraTest.kt
index b93c305..f1b6271 100644
--- a/tests/src/com/android/launcher3/model/WorkspaceItemProcessorExtraTest.kt
+++ b/tests/src/com/android/launcher3/model/WorkspaceItemProcessorExtraTest.kt
@@ -20,6 +20,7 @@
import android.content.ComponentName
import android.content.Context
import android.content.Intent
+import android.content.pm.ApplicationInfo
import android.content.pm.LauncherApps
import android.content.pm.PackageInstaller
import android.content.pm.ShortcutInfo
@@ -28,14 +29,12 @@
import android.util.LongSparseArray
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.launcher3.Flags.FLAG_ENABLE_SUPPORT_FOR_ARCHIVING
import com.android.launcher3.LauncherAppState
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.Utilities
import com.android.launcher3.model.data.IconRequestInfo
import com.android.launcher3.model.data.LauncherAppWidgetInfo
import com.android.launcher3.model.data.LauncherAppWidgetInfo.FLAG_RESTORE_STARTED
@@ -48,7 +47,6 @@
import com.android.launcher3.util.PackageUserKey
import com.android.launcher3.util.UserIconInfo
import com.android.launcher3.widget.WidgetInflater
-import com.android.launcher3.widget.WidgetSections
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -63,7 +61,6 @@
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
-import org.mockito.quality.Strictness
@RunWith(AndroidJUnit4::class)
class WorkspaceItemProcessorExtraTest {
@@ -108,11 +105,17 @@
`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 {
@@ -125,11 +128,6 @@
whenever(getAppLaunchIntent(mComponentName.packageName, mUserHandle))
.thenReturn(intent)
}
- mockLauncherApps =
- mock<LauncherApps>().apply {
- whenever(isPackageEnabled("package", mUserHandle)).thenReturn(true)
- whenever(isActivityEnabled(mComponentName, mUserHandle)).thenReturn(true)
- }
mockCursor =
Mockito.mock(LoaderCursor::class.java, RETURNS_DEEP_STUBS).apply {
user = mUserHandle
@@ -163,138 +161,116 @@
@Test
fun `When Pending App Widget has not started restore then update db and add item`() {
-
- val mockitoSession =
- ExtendedMockito.mockitoSession()
- .strictness(Strictness.LENIENT)
- .mockStatic(WidgetSections::class.java)
- .startMocking()
- try {
- // Given
- val expectedProvider = "com.google.android.testApp/com.android.testApp.testAppProvider"
- val expectedComponentName =
- ComponentName.unflattenFromString(expectedProvider)!!.flattenToString()
- val expectedRestoreStatus = FLAG_UI_NOT_READY or FLAG_RESTORE_STARTED
- val expectedAppWidgetId = 0
- mockCursor.apply {
- itemType = ITEM_TYPE_APPWIDGET
- user = mUserHandle
- restoreFlag = FLAG_UI_NOT_READY
- container = CONTAINER_DESKTOP
- whenever(isOnWorkspaceOrHotseat).thenCallRealMethod()
- whenever(appWidgetProvider).thenReturn(expectedProvider)
- whenever(appWidgetId).thenReturn(expectedAppWidgetId)
- whenever(spanX).thenReturn(2)
- whenever(spanY).thenReturn(1)
- whenever(options).thenReturn(0)
- whenever(appWidgetSource).thenReturn(20)
- whenever(applyCommonProperties(any())).thenCallRealMethod()
- whenever(
- updater()
- .put(Favorites.APPWIDGET_PROVIDER, expectedComponentName)
- .put(Favorites.APPWIDGET_ID, expectedAppWidgetId)
- .put(Favorites.RESTORED, expectedRestoreStatus)
- .commit()
- )
- .thenReturn(1)
- }
- val inflationResult =
- WidgetInflater.InflationResult(
- type = WidgetInflater.TYPE_PENDING,
- widgetInfo = null
- )
- mockWidgetInflater =
- mock<WidgetInflater>().apply {
- whenever(inflateAppWidget(any())).thenReturn(inflationResult)
- }
- val packageUserKey = PackageUserKey("com.google.android.testApp", mUserHandle)
- mInstallingPkgs[packageUserKey] = PackageInstaller.SessionInfo()
-
- // When
- itemProcessorUnderTest =
- createWorkspaceItemProcessorUnderTest(widgetProvidersMap = mWidgetProvidersMap)
- itemProcessorUnderTest.processItem()
-
- // Then
- val expectedWidgetInfo =
- LauncherAppWidgetInfo().apply {
- appWidgetId = expectedAppWidgetId
- providerName = ComponentName.unflattenFromString(expectedProvider)
- restoreStatus = expectedRestoreStatus
- }
- verify(
- mockCursor
- .updater()
- .put(Favorites.APPWIDGET_PROVIDER, expectedProvider)
+ // Given
+ val expectedProvider = "com.google.android.testApp/com.android.testApp.testAppProvider"
+ val expectedComponentName =
+ ComponentName.unflattenFromString(expectedProvider)!!.flattenToString()
+ val expectedRestoreStatus = FLAG_UI_NOT_READY or FLAG_RESTORE_STARTED
+ val expectedAppWidgetId = 0
+ mockCursor.apply {
+ itemType = ITEM_TYPE_APPWIDGET
+ user = mUserHandle
+ restoreFlag = FLAG_UI_NOT_READY
+ container = CONTAINER_DESKTOP
+ whenever(isOnWorkspaceOrHotseat).thenCallRealMethod()
+ whenever(appWidgetProvider).thenReturn(expectedProvider)
+ whenever(appWidgetId).thenReturn(expectedAppWidgetId)
+ whenever(spanX).thenReturn(2)
+ whenever(spanY).thenReturn(1)
+ whenever(options).thenReturn(0)
+ whenever(appWidgetSource).thenReturn(20)
+ whenever(applyCommonProperties(any())).thenCallRealMethod()
+ whenever(
+ updater()
+ .put(Favorites.APPWIDGET_PROVIDER, expectedComponentName)
.put(Favorites.APPWIDGET_ID, expectedAppWidgetId)
.put(Favorites.RESTORED, expectedRestoreStatus)
+ .commit()
)
- .commit()
- val widgetInfoCaptor = ArgumentCaptor.forClass(LauncherAppWidgetInfo::class.java)
- verify(mockCursor).checkAndAddItem(widgetInfoCaptor.capture(), eq(mockBgDataModel))
- val actualWidgetInfo = widgetInfoCaptor.value
- with(actualWidgetInfo) {
- assertThat(providerName).isEqualTo(expectedWidgetInfo.providerName)
- assertThat(restoreStatus).isEqualTo(expectedWidgetInfo.restoreStatus)
- assertThat(targetComponent).isEqualTo(expectedWidgetInfo.targetComponent)
- assertThat(appWidgetId).isEqualTo(expectedWidgetInfo.appWidgetId)
+ .thenReturn(1)
+ }
+ val inflationResult =
+ WidgetInflater.InflationResult(type = WidgetInflater.TYPE_PENDING, widgetInfo = null)
+ mockWidgetInflater =
+ mock<WidgetInflater>().apply {
+ whenever(inflateAppWidget(any())).thenReturn(inflationResult)
}
- } finally {
- mockitoSession.finishMocking()
+ val packageUserKey = PackageUserKey("com.google.android.testApp", mUserHandle)
+ mInstallingPkgs[packageUserKey] = PackageInstaller.SessionInfo()
+
+ // When
+ itemProcessorUnderTest =
+ createWorkspaceItemProcessorUnderTest(widgetProvidersMap = mWidgetProvidersMap)
+ itemProcessorUnderTest.processItem()
+
+ // Then
+ val expectedWidgetInfo =
+ LauncherAppWidgetInfo().apply {
+ appWidgetId = expectedAppWidgetId
+ providerName = ComponentName.unflattenFromString(expectedProvider)
+ restoreStatus = expectedRestoreStatus
+ }
+ verify(
+ mockCursor
+ .updater()
+ .put(Favorites.APPWIDGET_PROVIDER, expectedProvider)
+ .put(Favorites.APPWIDGET_ID, expectedAppWidgetId)
+ .put(Favorites.RESTORED, expectedRestoreStatus)
+ )
+ .commit()
+ val widgetInfoCaptor = ArgumentCaptor.forClass(LauncherAppWidgetInfo::class.java)
+ verify(mockCursor).checkAndAddItem(widgetInfoCaptor.capture(), eq(mockBgDataModel))
+ val actualWidgetInfo = widgetInfoCaptor.value
+ with(actualWidgetInfo) {
+ assertThat(providerName).isEqualTo(expectedWidgetInfo.providerName)
+ assertThat(restoreStatus).isEqualTo(expectedWidgetInfo.restoreStatus)
+ assertThat(targetComponent).isEqualTo(expectedWidgetInfo.targetComponent)
+ assertThat(appWidgetId).isEqualTo(expectedWidgetInfo.appWidgetId)
}
}
@Test
@EnableFlags(FLAG_ENABLE_SUPPORT_FOR_ARCHIVING)
fun `When Archived Pending App Widget then checkAndAddItem`() {
- val mockitoSession =
- ExtendedMockito.mockitoSession().mockStatic(Utilities::class.java).startMocking()
- try {
- // Given
- val expectedProvider = "com.google.android.testApp/com.android.testApp.testAppProvider"
- val expectedComponentName = ComponentName.unflattenFromString(expectedProvider)
- val expectedPackage = expectedComponentName!!.packageName
- mockPmHelper =
- mock<PackageManagerHelper>().apply {
- whenever(isAppArchived(expectedPackage)).thenReturn(true)
- }
- mockCursor =
- mock<LoaderCursor>().apply {
- itemType = ITEM_TYPE_APPWIDGET
- id = 1
- user = UserHandle(1)
- restoreFlag = FLAG_UI_NOT_READY
- container = CONTAINER_DESKTOP
- whenever(isOnWorkspaceOrHotseat).thenCallRealMethod()
- whenever(appWidgetProvider).thenReturn(expectedProvider)
- whenever(appWidgetId).thenReturn(0)
- whenever(spanX).thenReturn(2)
- whenever(spanY).thenReturn(1)
- whenever(options).thenReturn(0)
- whenever(appWidgetSource).thenReturn(20)
- whenever(applyCommonProperties(any())).thenCallRealMethod()
- }
- mInstallingPkgs = hashMapOf()
- val inflationResult =
- WidgetInflater.InflationResult(
- type = WidgetInflater.TYPE_PENDING,
- widgetInfo = null
- )
- mockWidgetInflater =
- mock<WidgetInflater>().apply {
- whenever(inflateAppWidget(any())).thenReturn(inflationResult)
- }
- itemProcessorUnderTest =
- createWorkspaceItemProcessorUnderTest(widgetProvidersMap = mWidgetProvidersMap)
+ // Given
+ val expectedProvider = "com.google.android.testApp/com.android.testApp.testAppProvider"
+ val expectedComponentName = ComponentName.unflattenFromString(expectedProvider)
+ val expectedPackage = expectedComponentName!!.packageName
+ val expectedUser = UserHandle(1)
- // When
- itemProcessorUnderTest.processItem()
+ whenever(mockLauncherApps.getApplicationInfo(eq(expectedPackage), any(), eq(expectedUser)))
+ .thenReturn(ApplicationInfo().apply { isArchived = true })
+ mockCursor =
+ mock<LoaderCursor>().apply {
+ itemType = ITEM_TYPE_APPWIDGET
+ id = 1
+ user = expectedUser
+ restoreFlag = FLAG_UI_NOT_READY
+ container = CONTAINER_DESKTOP
+ whenever(isOnWorkspaceOrHotseat).thenCallRealMethod()
+ whenever(appWidgetProvider).thenReturn(expectedProvider)
+ whenever(appWidgetId).thenReturn(0)
+ whenever(spanX).thenReturn(2)
+ whenever(spanY).thenReturn(1)
+ whenever(options).thenReturn(0)
+ whenever(appWidgetSource).thenReturn(20)
+ whenever(applyCommonProperties(any())).thenCallRealMethod()
+ }
+ mInstallingPkgs = hashMapOf()
+ val inflationResult =
+ WidgetInflater.InflationResult(type = WidgetInflater.TYPE_PENDING, widgetInfo = null)
+ mockWidgetInflater =
+ mock<WidgetInflater>().apply {
+ whenever(inflateAppWidget(any())).thenReturn(inflationResult)
+ }
+ itemProcessorUnderTest =
+ createWorkspaceItemProcessorUnderTest(widgetProvidersMap = mWidgetProvidersMap)
- // Then
- verify(mockCursor).checkAndAddItem(any(), any())
- } finally {
- mockitoSession.finishMocking()
- }
+ // When
+ itemProcessorUnderTest.processItem()
+
+ // Then
+ verify(mockCursor).checkAndAddItem(any(), any())
}
private fun createWorkspaceItemProcessorUnderTest(
@@ -314,7 +290,7 @@
pendingPackages: MutableSet<PackageUserKey> = mPendingPackages,
unlockedUsers: LongSparseArray<Boolean> = mUnlockedUsersArray,
installingPkgs: HashMap<PackageUserKey, PackageInstaller.SessionInfo> = mInstallingPkgs,
- allDeepShortcuts: MutableList<ShortcutInfo> = mAllDeepShortcuts
+ allDeepShortcuts: MutableList<ShortcutInfo> = mAllDeepShortcuts,
) =
WorkspaceItemProcessor(
c = cursor,
@@ -333,6 +309,6 @@
isSdCardReady = isSdCardReady,
shortcutKeyToPinnedShortcuts = shortcutKeyToPinnedShortcuts,
installingPkgs = installingPkgs,
- allDeepShortcuts = allDeepShortcuts
+ allDeepShortcuts = allDeepShortcuts,
)
}
diff --git a/tests/src/com/android/launcher3/model/gridmigration/ValidGridMigrationUnitTest.kt b/tests/src/com/android/launcher3/model/gridmigration/ValidGridMigrationUnitTest.kt
index 7182cf3..03d0195 100644
--- a/tests/src/com/android/launcher3/model/gridmigration/ValidGridMigrationUnitTest.kt
+++ b/tests/src/com/android/launcher3/model/gridmigration/ValidGridMigrationUnitTest.kt
@@ -114,17 +114,14 @@
}
}
- private fun migrate(
- srcGrid: Grid,
- dstGrid: Grid,
- ): List<WorkspaceItem> {
+ private fun migrate(srcGrid: Grid, dstGrid: Grid): List<WorkspaceItem> {
val userSerial = UserCache.INSTANCE[context].getSerialNumberForUser(Process.myUserHandle())
val dbHelper =
DatabaseHelper(
context,
null,
{ UserCache.INSTANCE.get(context).getSerialNumberForUser(it) },
- {}
+ {},
)
Favorites.addTableToDb(dbHelper.writableDatabase, userSerial, false, srcGrid.tableName)
@@ -135,12 +132,12 @@
LauncherDbUtils.SQLiteTransaction(dbHelper.writableDatabase).use {
GridSizeMigrationUtil.migrate(
dbHelper,
- GridSizeMigrationUtil.DbReader(it.db, srcGrid.tableName, context, MockSet(1)),
- GridSizeMigrationUtil.DbReader(it.db, dstGrid.tableName, context, MockSet(1)),
+ GridSizeMigrationUtil.DbReader(it.db, srcGrid.tableName, context),
+ GridSizeMigrationUtil.DbReader(it.db, dstGrid.tableName, context),
dstGrid.size.x,
dstGrid.size,
srcGrid.toGridState(),
- dstGrid.toGridState()
+ dstGrid.toGridState(),
)
it.commit()
}
@@ -157,7 +154,7 @@
Grid(
tableName = Favorites.TMP_TABLE,
size = testCase.srcSize,
- items = generateItemsForTest(testCase.boards, REPEAT_AFTER)
+ items = generateItemsForTest(testCase.boards, REPEAT_AFTER),
)
val dstGrid =
Grid(tableName = Favorites.TABLE_NAME, size = testCase.targetSize, items = listOf())
@@ -175,13 +172,13 @@
Grid(
tableName = Favorites.TMP_TABLE,
size = testCase.srcSize,
- items = generateItemsForTest(testCase.boards, REPEAT_AFTER)
+ items = generateItemsForTest(testCase.boards, REPEAT_AFTER),
)
val dstGrid =
Grid(
tableName = Favorites.TABLE_NAME,
size = testCase.targetSize,
- items = generateItemsForTest(testCase.destBoards, REPEAT_AFTER_DST)
+ items = generateItemsForTest(testCase.destBoards, REPEAT_AFTER_DST),
)
validate(srcGrid, dstGrid, migrate(srcGrid, dstGrid))
}
@@ -199,7 +196,7 @@
Grid(
tableName = Favorites.TMP_TABLE,
size = testCase.srcSize,
- items = generateItemsForTest(testCase.boards, REPEAT_AFTER)
+ items = generateItemsForTest(testCase.boards, REPEAT_AFTER),
)
val dstGrid =
Grid(tableName = Favorites.TABLE_NAME, size = testCase.targetSize, items = listOf())
diff --git a/tests/src/com/android/launcher3/pm/InstallSessionHelperTest.kt b/tests/src/com/android/launcher3/pm/InstallSessionHelperTest.kt
index 3dd8dbc..ca2ef42 100644
--- a/tests/src/com/android/launcher3/pm/InstallSessionHelperTest.kt
+++ b/tests/src/com/android/launcher3/pm/InstallSessionHelperTest.kt
@@ -18,6 +18,7 @@
import android.content.pm.ApplicationInfo
import android.content.pm.ApplicationInfo.FLAG_INSTALLED
+import android.content.pm.ApplicationInfo.FLAG_SYSTEM
import android.content.pm.LauncherApps
import android.content.pm.PackageInstaller
import android.content.pm.PackageManager
@@ -35,7 +36,9 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.any
import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
import org.mockito.kotlin.whenever
@@ -126,13 +129,10 @@
fun `isTrustedPackage returns true if LauncherApps finds ApplicationInfo`() {
// Given
val expectedApplicationInfo =
- ApplicationInfo().apply {
- flags = flags or FLAG_INSTALLED
- enabled = true
- }
+ ApplicationInfo().apply { flags = FLAG_SYSTEM or FLAG_INSTALLED }
doReturn(expectedApplicationInfo)
.whenever(launcherApps)
- .getApplicationInfo(expectedAppPackage, ApplicationInfo.FLAG_SYSTEM, UserHandle(0))
+ .getApplicationInfo(eq(expectedAppPackage), any(), eq(UserHandle(0)))
// When
val actualResult = installSessionHelper.isTrustedPackage(expectedAppPackage, UserHandle(0))
// Then
diff --git a/tests/src/com/android/launcher3/popup/SystemShortcutTest.java b/tests/src/com/android/launcher3/popup/SystemShortcutTest.java
index dcfcad5..f54668c 100644
--- a/tests/src/com/android/launcher3/popup/SystemShortcutTest.java
+++ b/tests/src/com/android/launcher3/popup/SystemShortcutTest.java
@@ -19,19 +19,26 @@
import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.launcher3.AbstractFloatingView.TYPE_SNACKBAR;
+import static com.android.launcher3.Flags.FLAG_ENABLE_DISMISS_PREDICTION_UNDO;
import static com.android.launcher3.Flags.FLAG_ENABLE_PRIVATE_SPACE;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_ALL_APPS;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_DISMISS_PREDICTION_UNDO;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_DONT_SUGGEST_APP_TAP;
import static com.android.launcher3.model.data.WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -50,8 +57,13 @@
import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.R;
import com.android.launcher3.allapps.PrivateProfileManager;
+import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.logging.StatsLogManager.StatsLogger;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -61,8 +73,9 @@
import com.android.launcher3.util.LauncherModelHelper.SandboxModelContext;
import com.android.launcher3.util.LauncherMultivalentJUnit;
import com.android.launcher3.util.TestSandboxModelContextWrapper;
+import com.android.launcher3.util.TestUtil;
import com.android.launcher3.util.UserIconInfo;
-import com.android.launcher3.views.BaseDragLayer;
+import com.android.launcher3.views.Snackbar;
import com.android.launcher3.widget.picker.model.WidgetPickerDataProvider;
import com.android.launcher3.widget.picker.model.data.WidgetPickerData;
@@ -72,6 +85,7 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -88,25 +102,31 @@
private PrivateProfileManager mPrivateProfileManager;
private WidgetPickerDataProvider mWidgetPickerDataProvider;
private AppInfo mAppInfo;
+
@Mock UserCache mUserCache;
@Mock ApiWrapper mApiWrapper;
- @Mock BaseDragLayer mBaseDragLayer;
@Mock UserIconInfo mUserIconInfo;
@Mock LauncherActivityInfo mLauncherActivityInfo;
@Mock ApplicationInfo mApplicationInfo;
@Mock Intent mIntent;
+ @Mock StatsLogManager mStatsLogManager;
+ @Mock(answer = Answers.RETURNS_SELF) StatsLogger mStatsLogger;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mSandboxContext.putObject(UserCache.INSTANCE, mUserCache);
mSandboxContext.putObject(ApiWrapper.INSTANCE, mApiWrapper);
- mTestContext = new TestSandboxModelContextWrapper(mSandboxContext);
- mView = new View(mSandboxContext);
- spyOn(mTestContext);
+ mTestContext = new TestSandboxModelContextWrapper(mSandboxContext) {
+ @Override
+ public StatsLogManager getStatsLogManager() {
+ return mStatsLogManager;
+ }
+ };
spyOn(mSandboxContext);
- doReturn(mBaseDragLayer).when(mTestContext).getDragLayer();
+ doReturn(mStatsLogger).when(mStatsLogManager).logger();
+ mView = new View(mTestContext);
mItemInfo = new ItemInfo();
LauncherApps mLauncherApps = mSandboxContext.spyService(LauncherApps.class);
@@ -114,7 +134,6 @@
when(mLauncherActivityInfo.getApplicationInfo()).thenReturn(mApplicationInfo);
when(mUserCache.getUserInfo(any())).thenReturn(mUserIconInfo);
- when(mBaseDragLayer.getChildCount()).thenReturn(0);
mPrivateProfileManager = mTestContext.getAppsView().getPrivateProfileManager();
spyOn(mPrivateProfileManager);
when(mPrivateProfileManager.getProfileUser()).thenReturn(PRIVATE_HANDLE);
@@ -168,6 +187,7 @@
}
@Test
+ @DisableFlags(FLAG_ENABLE_DISMISS_PREDICTION_UNDO)
public void testDontSuggestAppForPredictedItem() {
mAppInfo = new AppInfo();
mAppInfo.componentName = new ComponentName(mTestContext, getClass());
@@ -176,7 +196,36 @@
SystemShortcut systemShortcut = SystemShortcut.DONT_SUGGEST_APP
.getShortcut(mTestContext, mAppInfo, mView);
assertNotNull(systemShortcut);
- systemShortcut.onClick(mView);
+
+ TestUtil.runOnExecutorSync(MAIN_EXECUTOR, () -> systemShortcut.onClick(mView));
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ verify(mStatsLogger).log(eq(LAUNCHER_SYSTEM_SHORTCUT_DONT_SUGGEST_APP_TAP));
+ assertFalse(AbstractFloatingView.hasOpenView(mTestContext, TYPE_SNACKBAR));
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_DISMISS_PREDICTION_UNDO)
+ public void testDontSuggestAppForPredictedItemWithUndo() {
+ mAppInfo = new AppInfo();
+ mAppInfo.componentName = new ComponentName(mTestContext, getClass());
+ mAppInfo.container = CONTAINER_HOTSEAT_PREDICTION;
+ assertTrue(mAppInfo.isPredictedItem());
+ SystemShortcut systemShortcut = SystemShortcut.DONT_SUGGEST_APP
+ .getShortcut(mTestContext, mAppInfo, mView);
+ assertNotNull(systemShortcut);
+
+ TestUtil.runOnExecutorSync(MAIN_EXECUTOR, () -> systemShortcut.onClick(mView));
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ verify(mStatsLogger).log(eq(LAUNCHER_SYSTEM_SHORTCUT_DONT_SUGGEST_APP_TAP));
+
+ // Undo bar shown
+ Snackbar snackbar = AbstractFloatingView.getOpenView(mTestContext, TYPE_SNACKBAR);
+ assertNotNull(snackbar);
+ reset(mStatsLogger);
+ TestUtil.runOnExecutorSync(MAIN_EXECUTOR, snackbar.findViewById(
+ R.id.action)::performClick);
+ verify(mStatsLogger).log(eq(LAUNCHER_DISMISS_PREDICTION_UNDO));
}
@Test
diff --git a/tests/src/com/android/launcher3/tablet/TaplIsTabletTest.kt b/tests/src/com/android/launcher3/tablet/TaplIsTabletTest.kt
new file mode 100644
index 0000000..a6de607
--- /dev/null
+++ b/tests/src/com/android/launcher3/tablet/TaplIsTabletTest.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.tablet
+
+import android.platform.test.rule.AllowedDevices
+import android.platform.test.rule.DeviceProduct
+import com.android.launcher3.Launcher
+import com.android.launcher3.ui.AbstractLauncherUiTest
+import junit.framework.TestCase.assertFalse
+import junit.framework.TestCase.assertTrue
+import org.junit.Test
+
+class TaplIsTabletTest : AbstractLauncherUiTest<Launcher>() {
+
+ /** Investigating b/366237798 by isolating and seeing flake rate of mLauncher.isTablet */
+ @Test
+ @AllowedDevices(
+ DeviceProduct.CF_FOLDABLE,
+ DeviceProduct.CF_TABLET,
+ DeviceProduct.TANGORPRO,
+ DeviceProduct.FELIX,
+ DeviceProduct.COMET,
+ )
+ fun isTabletShouldBeTrue() {
+ assertTrue(mLauncher.isTablet)
+ }
+
+ /** Investigating b/366237798 by isolating and seeing flake rate of mLauncher.isTablet */
+ @Test
+ @AllowedDevices(DeviceProduct.CF_PHONE, DeviceProduct.CHEETAH)
+ fun isTabletShouldBeFalse() {
+ assertFalse(mLauncher.isTablet)
+ }
+}
diff --git a/tests/src/com/android/launcher3/ui/TaplWorkProfileTest.java b/tests/src/com/android/launcher3/ui/TaplWorkProfileTest.java
index b2e413d..b38dd4b 100644
--- a/tests/src/com/android/launcher3/ui/TaplWorkProfileTest.java
+++ b/tests/src/com/android/launcher3/ui/TaplWorkProfileTest.java
@@ -45,7 +45,6 @@
import com.android.launcher3.allapps.WorkProfileManager;
import com.android.launcher3.tapl.LauncherInstrumentation;
import com.android.launcher3.util.TestUtil;
-import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
import com.android.launcher3.util.rule.TestStabilityRule.Stability;
import org.junit.After;
@@ -147,7 +146,6 @@
// Staging; will be promoted to presubmit if stable
@Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT)
- @ScreenRecord
@Test
public void toggleWorks() {
assumeTrue(mWorkProfileSetupSuccessful);
@@ -195,7 +193,6 @@
}
- @ScreenRecord // b/322823478
@Test
public void testEdu() {
assumeTrue(mWorkProfileSetupSuccessful);
diff --git a/tests/src/com/android/launcher3/ui/workspace/TaplTwoPanelWorkspaceTest.java b/tests/src/com/android/launcher3/ui/workspace/TaplTwoPanelWorkspaceTest.java
index 20c5a25..2edd129 100644
--- a/tests/src/com/android/launcher3/ui/workspace/TaplTwoPanelWorkspaceTest.java
+++ b/tests/src/com/android/launcher3/ui/workspace/TaplTwoPanelWorkspaceTest.java
@@ -164,7 +164,6 @@
@Test
@PortraitLandscape
- @ScreenRecordRule.ScreenRecord // b/352130094
public void testDragIconToPage2() {
Workspace workspace = mLauncher.getWorkspace();
@@ -242,7 +241,6 @@
});
}
- @ScreenRecordRule.ScreenRecord // b/329935119
@Test
@PortraitLandscape
public void testEmptyPageDoesNotGetRemovedIfPagePairIsNotEmpty() {
diff --git a/tests/src/com/android/launcher3/util/rule/ExtendedLongPressTimeoutRule.java b/tests/src/com/android/launcher3/util/rule/ExtendedLongPressTimeoutRule.java
index 702988c..8a9ff3e 100644
--- a/tests/src/com/android/launcher3/util/rule/ExtendedLongPressTimeoutRule.java
+++ b/tests/src/com/android/launcher3/util/rule/ExtendedLongPressTimeoutRule.java
@@ -16,6 +16,9 @@
package com.android.launcher3.util.rule;
+import static com.android.launcher3.util.TestUtil.grantWriteSecurePermission;
+
+import android.app.Instrumentation;
import android.content.ContentResolver;
import android.provider.Settings;
import android.util.Log;
@@ -51,6 +54,7 @@
try {
Log.d(TAG, "In try-block: Setting long press timeout from "
+ prevLongPressTimeout + "ms to " + newLongPressTimeout + "ms");
+ grantWriteSecurePermission();
Settings.Secure.putInt(
contentResolver,
Settings.Secure.LONG_PRESS_TIMEOUT,
@@ -63,6 +67,7 @@
} finally {
Log.d(TAG, "In finally-block: resetting long press timeout to "
+ prevLongPressTimeout + "ms");
+ grantWriteSecurePermission();
Settings.Secure.putInt(
contentResolver,
Settings.Secure.LONG_PRESS_TIMEOUT,
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index b7ebfcd..e1bd686 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -123,10 +123,10 @@
TASK_SELECTOR);
final int centerX = mLauncher.getDevice().getDisplayWidth() / 2;
mLauncher.assertTrue(
- "All tasks not to the left of the swiped task",
- tasks.stream()
- .allMatch(
- t -> t.getVisibleBounds().right < centerX));
+ "Task(s) found to the right of the swiped task",
+ tasks.stream().allMatch(t ->
+ t.getVisibleBounds().right < centerX
+ || t.getVisibleBounds().centerX() == centerX));
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index d3c423e..78627e5 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -55,6 +55,7 @@
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.Trace;
import android.text.TextUtils;
import android.util.Log;
import android.view.InputDevice;
@@ -524,16 +525,19 @@
Closable addContextLayer(String piece) {
mDiagnosticContext.addLast(piece);
+ Trace.beginSection("Context: " + piece);
log("Entering context: " + piece);
return () -> {
+ Trace.endSection();
log("Leaving context: " + piece);
mDiagnosticContext.removeLast();
};
}
public void dumpViewHierarchy() {
- final ByteArrayOutputStream stream = new ByteArrayOutputStream();
try {
+ Trace.beginSection("dumpViewHierarchy");
+ final ByteArrayOutputStream stream = new ByteArrayOutputStream();
mDevice.dumpWindowHierarchy(stream);
stream.flush();
stream.close();
@@ -542,6 +546,8 @@
}
} catch (IOException e) {
Log.e(TAG, "error dumping XML to logcat", e);
+ } finally {
+ Trace.endSection();
}
}
@@ -621,15 +627,20 @@
*/
public void checkForAnomaly(
boolean ignoreNavmodeChangeStates, boolean ignoreOnlySystemUiViews) {
- if (mTestAnomalyChecker != null) mTestAnomalyChecker.run();
+ try {
+ Trace.beginSection("checkForAnomaly");
+ if (mTestAnomalyChecker != null) mTestAnomalyChecker.run();
- final String systemAnomalyMessage =
- getSystemAnomalyMessage(ignoreNavmodeChangeStates, ignoreOnlySystemUiViews);
- if (systemAnomalyMessage != null) {
- if (mOnFailure != null) mOnFailure.run();
- Assert.fail(formatSystemHealthMessage(formatErrorWithEvents(
- "http://go/tapl : Tests are broken by a non-Launcher system error: "
- + systemAnomalyMessage, false)));
+ final String systemAnomalyMessage =
+ getSystemAnomalyMessage(ignoreNavmodeChangeStates, ignoreOnlySystemUiViews);
+ if (systemAnomalyMessage != null) {
+ if (mOnFailure != null) mOnFailure.run();
+ Assert.fail(formatSystemHealthMessage(formatErrorWithEvents(
+ "http://go/tapl : Tests are broken by a non-Launcher system error: "
+ + systemAnomalyMessage, false)));
+ }
+ } finally {
+ Trace.endSection();
}
}
@@ -1005,16 +1016,20 @@
}
public void waitForLauncherInitialized() {
- for (int i = 0; i < 100; ++i) {
- if (getTestInfo(
- TestProtocol.REQUEST_IS_LAUNCHER_INITIALIZED).
- getBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD)) {
- return;
+ try {
+ Trace.beginSection("waitForLauncherInitialized");
+ for (int i = 0; i < 100; ++i) {
+ if (getTestInfo(TestProtocol.REQUEST_IS_LAUNCHER_INITIALIZED).getBoolean(
+ TestProtocol.TEST_INFO_RESPONSE_FIELD)) {
+ return;
+ }
+ SystemClock.sleep(100);
}
- SystemClock.sleep(100);
+ checkForAnomaly();
+ fail("Launcher didn't initialize");
+ } finally {
+ Trace.endSection();
}
- checkForAnomaly();
- fail("Launcher didn't initialize");
}
public boolean isLauncherActivityStarted() {
@@ -1259,8 +1274,13 @@
}
boolean isLauncherVisible() {
- mDevice.waitForIdle();
- return hasLauncherObject(getAnyObjectSelector());
+ try {
+ Trace.beginSection("isLauncherVisible");
+ mDevice.waitForIdle();
+ return hasLauncherObject(getAnyObjectSelector());
+ } finally {
+ Trace.endSection();
+ }
}
boolean isLauncherContainerVisible() {
@@ -2403,7 +2423,7 @@
eventChecker.setLogExclusionRule(event -> {
Matcher matcher = Pattern.compile("KeyEvent.*flags=0x([0-9a-fA-F]+)").matcher(event);
if (matcher.find()) {
- int keyEventFlags = Integer.parseInt(matcher.group(1), 16);
+ long keyEventFlags = Long.parseLong(matcher.group(1), 16);
// ignore KeyEvents with FLAG_CANCELED
return (keyEventFlags & KeyEvent.FLAG_CANCELED) != 0;
}
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
index 5433fa7..9a8d952 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -282,7 +282,7 @@
* Returns whether the given String is contained in this Task's contentDescription. Also returns
* true if both Strings are null.
*
- * TODO(b/326565120): remove Nullable support once the bug causing it to be null is fixed.
+ * TODO(b/342627272): remove Nullable support once the bug causing it to be null is fixed.
*/
public boolean containsContentDescription(@Nullable String expected,
OverviewSplitTask overviewSplitTask) {