Merge "Fix Square Recent button unresponsiveness" into main
diff --git a/Android.bp b/Android.bp
index def024e..4274613 100644
--- a/Android.bp
+++ b/Android.bp
@@ -27,7 +27,7 @@
"android.os.flags-aconfig-java",
"android.appwidget.flags-aconfig-java",
"com.android.window.flags.window-aconfig-java",
- ]
+ ],
}
// Common source files used to build launcher (java and kotlin)
@@ -153,7 +153,7 @@
soong_config_variables: {
release_enable_compose_in_launcher: {
srcs: [
- ":launcher-compose-enabled-src"
+ ":launcher-compose-enabled-src",
],
// Compose dependencies
@@ -166,7 +166,7 @@
// in compose/launcher3/facade/disabled/.
conditions_default: {
srcs: [
- ":launcher-compose-disabled-src"
+ ":launcher-compose-disabled-src",
],
static_libs: [],
},
@@ -179,7 +179,7 @@
soong_config_variables: {
release_enable_compose_in_launcher: {
srcs: [
- ":launcher-quickstep-compose-enabled-src"
+ ":launcher-quickstep-compose-enabled-src",
],
// Compose dependencies
@@ -192,7 +192,7 @@
// in compose/quickstep/facade/disabled/.
conditions_default: {
srcs: [
- ":launcher-quickstep-compose-disabled-src"
+ ":launcher-quickstep-compose-disabled-src",
],
static_libs: [],
},
@@ -221,7 +221,7 @@
android_library {
name: "launcher-aosp-tapl",
libs: [
- "framework-statsd",
+ "framework-statsd.stubs.module_lib",
],
static_libs: [
"androidx.annotation_annotation",
@@ -322,6 +322,8 @@
"kotlinx_coroutines",
"com_android_launcher3_flags_lib",
"com_android_wm_shell_flags_lib",
+ "dagger2",
+ "jsr330",
],
manifest: "AndroidManifest-common.xml",
@@ -357,6 +359,7 @@
sdk_version: "current",
min_sdk_version: min_launcher3_sdk_version,
target_sdk_version: "current",
+ plugins: ["dagger2-compiler"],
privileged: true,
system_ext_specific: true,
@@ -385,13 +388,14 @@
"quickstep/res",
],
libs: [
- "framework-statsd",
+ "framework-statsd.stubs.module_lib",
],
static_libs: [
"Launcher3ResLib",
"lottie",
"SystemUISharedLib",
"SettingsLibSettingsTheme",
+ "dagger2",
],
manifest: "quickstep/AndroidManifest.xml",
min_sdk_version: "current",
@@ -421,7 +425,10 @@
"QuickstepResLib",
"androidx.room_room-runtime",
],
- plugins: ["androidx.room_room-compiler-plugin"],
+ plugins: [
+ "androidx.room_room-compiler-plugin",
+ "dagger2-compiler",
+ ],
manifest: "quickstep/AndroidManifest.xml",
additional_manifests: [
"go/AndroidManifest.xml",
@@ -437,7 +444,7 @@
name: "Launcher3QuickStepLib",
defaults: [
"launcher_compose_defaults",
- "quickstep_compose_defaults"
+ "quickstep_compose_defaults",
],
srcs: [
":launcher-src",
@@ -446,7 +453,7 @@
],
resource_dirs: [],
libs: [
- "framework-statsd",
+ "framework-statsd.stubs.module_lib",
],
// Note the ordering here is important when it comes to resource
// overriding. We want the most specific resource overrides defined
@@ -458,6 +465,7 @@
],
manifest: "quickstep/AndroidManifest.xml",
platform_apis: true,
+ plugins: ["dagger2-compiler"],
min_sdk_version: "current",
// TODO(b/319712088): re-enable use_resource_processor
use_resource_processor: false,
@@ -500,7 +508,6 @@
}
-
// Build rule for Launcher3 Go app with quickstep for Android Go devices.
// Note that the following two rules are exactly same, and should
// eventually be merged into a single target
@@ -540,6 +547,7 @@
include_filter: ["com.android.launcher3.*"],
},
}
+
android_app {
name: "Launcher3QuickStepGo",
static_libs: ["Launcher3GoLib"],
diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig
index dbcf49c..40c3797 100644
--- a/aconfig/launcher.aconfig
+++ b/aconfig/launcher.aconfig
@@ -316,6 +316,7 @@
description: "Menu in Taskbar with options to launch and manage multiple instances of the same app"
bug: "355237285"
}
+
flag {
name: "navigate_to_child_preference"
namespace: "launcher"
@@ -349,3 +350,13 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "ignore_three_finger_trackpad_for_nav_handle_long_press"
+ namespace: "launcher"
+ description: "Ignore three finger trackpad event for nav handle long press"
+ bug: "342143522"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/quickstep/Android.bp b/quickstep/Android.bp
index 1b9c661..4c724dc 100644
--- a/quickstep/Android.bp
+++ b/quickstep/Android.bp
@@ -52,6 +52,7 @@
"tests/src/com/android/quickstep/TaplOverviewIconTest.java",
"tests/src/com/android/quickstep/TaplTestsQuickstep.java",
"tests/src/com/android/quickstep/TaplTestsSplitscreen.java",
+ "tests/src/com/android/quickstep/util/SplitScreenTestUtils.kt",
"tests/src/com/android/launcher3/testcomponent/ExcludeFromRecentsTestActivity.java",
],
}
diff --git a/quickstep/res/drawable/keyboard_quick_switch_overview_button_background.xml b/quickstep/res/drawable/keyboard_quick_switch_text_button_background.xml
similarity index 78%
rename from quickstep/res/drawable/keyboard_quick_switch_overview_button_background.xml
rename to quickstep/res/drawable/keyboard_quick_switch_text_button_background.xml
index 8180293..f204920 100644
--- a/quickstep/res/drawable/keyboard_quick_switch_overview_button_background.xml
+++ b/quickstep/res/drawable/keyboard_quick_switch_text_button_background.xml
@@ -15,8 +15,7 @@
-->
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
- <solid android:color="?attr/materialColorSurfaceBright" />
- <corners android:radius="@dimen/keyboard_quick_switch_task_view_radius" />
+ <solid android:color="@android:color/white" />
+ <corners android:radius="@dimen/keyboard_quick_switch_text_button_radius" />
</shape>
diff --git a/quickstep/res/layout-land/keyboard_quick_switch_taskview.xml b/quickstep/res/layout-land/keyboard_quick_switch_taskview.xml
index 613edac..d1e5667 100644
--- a/quickstep/res/layout-land/keyboard_quick_switch_taskview.xml
+++ b/quickstep/res/layout-land/keyboard_quick_switch_taskview.xml
@@ -18,8 +18,8 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
- android:layout_width="@dimen/keyboard_quick_switch_taskview_width"
- android:layout_height="@dimen/keyboard_quick_switch_taskview_height"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:importantForAccessibility="yes"
android:background="@drawable/keyboard_quick_switch_task_view_background"
android:clipToOutline="true"
@@ -27,8 +27,8 @@
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/content"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_width="@dimen/keyboard_quick_switch_taskview_width"
+ android:layout_height="@dimen/keyboard_quick_switch_taskview_height"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
@@ -52,7 +52,7 @@
android:layout_width="0dp"
android:layout_height="match_parent"
android:visibility="gone"
- android:layout_marginStart="@dimen/keyboard_quick_switch_split_view_spacing"
+ android:layout_marginStart="@dimen/keyboard_quick_switch_view_small_spacing"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
diff --git a/quickstep/res/layout-land/keyboard_quick_switch_taskview_square.xml b/quickstep/res/layout-land/keyboard_quick_switch_taskview_square.xml
new file mode 100644
index 0000000..0eccd8e
--- /dev/null
+++ b/quickstep/res/layout-land/keyboard_quick_switch_taskview_square.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.launcher3.taskbar.KeyboardQuickSwitchTaskView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:launcher="http://schemas.android.com/apk/res-auto"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:importantForAccessibility="yes"
+ android:background="@drawable/keyboard_quick_switch_task_view_background"
+ android:clipToOutline="true"
+ launcher:focusBorderColor="?androidprv:attr/materialColorOutline">
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/content"
+ android:layout_width="0dp"
+ android:layout_height="@dimen/keyboard_quick_switch_taskview_height"
+
+ app:layout_constraintDimensionRatio="1:1"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent">
+
+ <include
+ layout="@layout/keyboard_quick_switch_taskview_thumbnail"
+ android:id="@+id/thumbnail_1"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toTopOf="@id/thumbnail_2"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"/>
+
+ <include
+ layout="@layout/keyboard_quick_switch_taskview_thumbnail"
+ android:id="@+id/thumbnail_2"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:visibility="gone"
+ android:layout_marginTop="@dimen/keyboard_quick_switch_view_small_spacing"
+
+ app:layout_constraintTop_toBottomOf="@id/thumbnail_1"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"/>
+
+ <ImageView
+ android:id="@+id/icon_1"
+ android:layout_width="@dimen/keyboard_quick_switch_taskview_icon_size"
+ android:layout_height="@dimen/keyboard_quick_switch_taskview_icon_size"
+ android:importantForAccessibility="no"
+ android:scaleType="centerCrop"
+
+ app:layout_constraintTop_toTopOf="@id/thumbnail_1"
+ app:layout_constraintBottom_toBottomOf="@id/thumbnail_1"
+ app:layout_constraintStart_toStartOf="@id/thumbnail_1"
+ app:layout_constraintEnd_toEndOf="@id/thumbnail_1"/>
+
+ <ImageView
+ android:id="@+id/icon_2"
+ android:layout_width="@dimen/keyboard_quick_switch_taskview_icon_size"
+ android:layout_height="@dimen/keyboard_quick_switch_taskview_icon_size"
+ android:importantForAccessibility="no"
+ android:visibility="gone"
+ android:scaleType="centerCrop"
+
+ app:layout_constraintTop_toTopOf="@id/thumbnail_2"
+ app:layout_constraintBottom_toBottomOf="@id/thumbnail_2"
+ app:layout_constraintStart_toStartOf="@id/thumbnail_2"
+ app:layout_constraintEnd_toEndOf="@id/thumbnail_2"/>
+
+ </androidx.constraintlayout.widget.ConstraintLayout>
+
+</com.android.launcher3.taskbar.KeyboardQuickSwitchTaskView>
diff --git a/quickstep/res/layout/digital_wellbeing_toast.xml b/quickstep/res/layout/digital_wellbeing_toast.xml
index 6a99a3b..3973e56 100644
--- a/quickstep/res/layout/digital_wellbeing_toast.xml
+++ b/quickstep/res/layout/digital_wellbeing_toast.xml
@@ -14,7 +14,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<TextView
+<com.android.quickstep.views.DigitalWellBeingToast
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
style="@style/TextTitle"
diff --git a/quickstep/res/layout/keyboard_quick_switch_textonly_taskview.xml b/quickstep/res/layout/keyboard_quick_switch_desktop_taskview.xml
similarity index 68%
rename from quickstep/res/layout/keyboard_quick_switch_textonly_taskview.xml
rename to quickstep/res/layout/keyboard_quick_switch_desktop_taskview.xml
index c76a2e3..c3f9e54 100644
--- a/quickstep/res/layout/keyboard_quick_switch_textonly_taskview.xml
+++ b/quickstep/res/layout/keyboard_quick_switch_desktop_taskview.xml
@@ -18,17 +18,20 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
- android:layout_width="@dimen/keyboard_quick_switch_taskview_width"
- android:layout_height="@dimen/keyboard_quick_switch_taskview_height"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:clipToOutline="true"
android:importantForAccessibility="yes"
- launcher:focusBorderColor="?attr/materialColorOutline">
+ launcher:focusBorderColor="?androidprv:attr/materialColorOutline"
+ launcher:focusBorderRadius="@dimen/keyboard_quick_switch_text_button_radius">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/content"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@drawable/keyboard_quick_switch_overview_button_background"
+ android:layout_width="@dimen/keyboard_quick_switch_text_button_width"
+ android:layout_height="@dimen/keyboard_quick_switch_taskview_height"
+ android:background="@drawable/keyboard_quick_switch_text_button_background"
+ android:backgroundTint="?androidprv:attr/materialColorSurfaceContainer"
+ android:paddingHorizontal="@dimen/keyboard_quick_switch_text_button_horizontal_padding"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
@@ -37,10 +40,11 @@
<ImageView
android:id="@+id/icon"
- android:layout_width="@dimen/keyboard_quick_switch_recents_icon_size"
- android:layout_height="@dimen/keyboard_quick_switch_recents_icon_size"
- android:layout_marginBottom="8dp"
- android:tint="?attr/materialColorOnSurface"
+ android:layout_width="@dimen/keyboard_quick_switch_desktop_icon_size"
+ android:layout_height="@dimen/keyboard_quick_switch_desktop_icon_size"
+ android:layout_marginBottom="4dp"
+ android:tint="?androidprv:attr/materialColorOnSurface"
+ android:src="@drawable/ic_desktop"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintTop_toTopOf="parent"
@@ -49,9 +53,9 @@
app:layout_constraintEnd_toEndOf="parent"/>
<TextView
- style="@style/KeyboardQuickSwitchText"
+ style="@style/KeyboardQuickSwitchText.OnTaskView"
android:id="@+id/text"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAlignment="center"
diff --git a/quickstep/res/layout/keyboard_quick_switch_textonly_taskview.xml b/quickstep/res/layout/keyboard_quick_switch_overview_taskview.xml
similarity index 62%
copy from quickstep/res/layout/keyboard_quick_switch_textonly_taskview.xml
copy to quickstep/res/layout/keyboard_quick_switch_overview_taskview.xml
index c76a2e3..0b44a2a 100644
--- a/quickstep/res/layout/keyboard_quick_switch_textonly_taskview.xml
+++ b/quickstep/res/layout/keyboard_quick_switch_overview_taskview.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2023 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,44 +18,47 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
- android:layout_width="@dimen/keyboard_quick_switch_taskview_width"
- android:layout_height="@dimen/keyboard_quick_switch_taskview_height"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:clipToOutline="true"
android:importantForAccessibility="yes"
- launcher:focusBorderColor="?attr/materialColorOutline">
+ launcher:focusBorderColor="?androidprv:attr/materialColorOutline"
+ launcher:focusBorderRadius="@dimen/keyboard_quick_switch_text_button_radius">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/content"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@drawable/keyboard_quick_switch_overview_button_background"
+ android:layout_width="@dimen/keyboard_quick_switch_text_button_width"
+ android:layout_height="@dimen/keyboard_quick_switch_taskview_height"
+ android:background="@drawable/keyboard_quick_switch_text_button_background"
+ android:backgroundTint="?androidprv:attr/materialColorSurfaceBright"
+ android:paddingHorizontal="@dimen/keyboard_quick_switch_text_button_horizontal_padding"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
- <ImageView
- android:id="@+id/icon"
- android:layout_width="@dimen/keyboard_quick_switch_recents_icon_size"
- android:layout_height="@dimen/keyboard_quick_switch_recents_icon_size"
- android:layout_marginBottom="8dp"
- android:tint="?attr/materialColorOnSurface"
-
- app:layout_constraintVertical_chainStyle="packed"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintBottom_toTopOf="@id/text"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"/>
-
<TextView
- style="@style/KeyboardQuickSwitchText"
- android:id="@+id/text"
+ style="@style/KeyboardQuickSwitchText.LargeText"
+ android:id="@+id/large_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAlignment="center"
- app:layout_constraintTop_toBottomOf="@id/icon"
+ app:layout_constraintVertical_chainStyle="packed"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toTopOf="@id/small_text"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"/>
+
+ <TextView
+ style="@style/KeyboardQuickSwitchText.OnTaskView"
+ android:id="@+id/small_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAlignment="center"
+
+ app:layout_constraintTop_toBottomOf="@id/large_text"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
diff --git a/quickstep/res/layout/keyboard_quick_switch_taskview.xml b/quickstep/res/layout/keyboard_quick_switch_taskview.xml
index 8f09176..41eb623 100644
--- a/quickstep/res/layout/keyboard_quick_switch_taskview.xml
+++ b/quickstep/res/layout/keyboard_quick_switch_taskview.xml
@@ -18,8 +18,8 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
- android:layout_width="@dimen/keyboard_quick_switch_taskview_width"
- android:layout_height="@dimen/keyboard_quick_switch_taskview_height"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:importantForAccessibility="yes"
android:background="@drawable/keyboard_quick_switch_task_view_background"
android:clipToOutline="true"
@@ -27,8 +27,8 @@
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/content"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_width="@dimen/keyboard_quick_switch_taskview_width"
+ android:layout_height="@dimen/keyboard_quick_switch_taskview_height"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
@@ -52,7 +52,7 @@
android:layout_width="match_parent"
android:layout_height="0dp"
android:visibility="gone"
- android:layout_marginTop="@dimen/keyboard_quick_switch_split_view_spacing"
+ android:layout_marginTop="@dimen/keyboard_quick_switch_view_small_spacing"
app:layout_constraintTop_toBottomOf="@id/thumbnail_1"
app:layout_constraintBottom_toBottomOf="parent"
diff --git a/quickstep/res/layout/keyboard_quick_switch_taskview_square.xml b/quickstep/res/layout/keyboard_quick_switch_taskview_square.xml
new file mode 100644
index 0000000..1474949
--- /dev/null
+++ b/quickstep/res/layout/keyboard_quick_switch_taskview_square.xml
@@ -0,0 +1,90 @@
+<?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.
+-->
+<com.android.launcher3.taskbar.KeyboardQuickSwitchTaskView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:launcher="http://schemas.android.com/apk/res-auto"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:importantForAccessibility="yes"
+ android:background="@drawable/keyboard_quick_switch_task_view_background"
+ android:clipToOutline="true"
+ launcher:focusBorderColor="?androidprv:attr/materialColorOutline">
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/content"
+ android:layout_width="0dp"
+ android:layout_height="@dimen/keyboard_quick_switch_taskview_height"
+
+ app:layout_constraintDimensionRatio="1:1"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent">
+
+ <include
+ layout="@layout/keyboard_quick_switch_taskview_thumbnail"
+ android:id="@+id/thumbnail_1"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/thumbnail_2"/>
+
+ <include
+ layout="@layout/keyboard_quick_switch_taskview_thumbnail"
+ android:id="@+id/thumbnail_2"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:visibility="gone"
+ android:layout_marginStart="@dimen/keyboard_quick_switch_view_small_spacing"
+
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toEndOf="@id/thumbnail_1"
+ app:layout_constraintEnd_toEndOf="parent"/>
+
+ <ImageView
+ android:id="@+id/icon_1"
+ android:layout_width="@dimen/keyboard_quick_switch_taskview_icon_size"
+ android:layout_height="@dimen/keyboard_quick_switch_taskview_icon_size"
+ android:importantForAccessibility="no"
+ android:scaleType="centerCrop"
+
+ app:layout_constraintTop_toTopOf="@id/thumbnail_1"
+ app:layout_constraintBottom_toBottomOf="@id/thumbnail_1"
+ app:layout_constraintStart_toStartOf="@id/thumbnail_1"
+ app:layout_constraintEnd_toEndOf="@id/thumbnail_1"/>
+
+ <ImageView
+ android:id="@+id/icon_2"
+ android:layout_width="@dimen/keyboard_quick_switch_taskview_icon_size"
+ android:layout_height="@dimen/keyboard_quick_switch_taskview_icon_size"
+ android:importantForAccessibility="no"
+ android:visibility="gone"
+ android:scaleType="centerCrop"
+
+ app:layout_constraintTop_toTopOf="@id/thumbnail_2"
+ app:layout_constraintBottom_toBottomOf="@id/thumbnail_2"
+ app:layout_constraintStart_toStartOf="@id/thumbnail_2"
+ app:layout_constraintEnd_toEndOf="@id/thumbnail_2"/>
+
+ </androidx.constraintlayout.widget.ConstraintLayout>
+
+</com.android.launcher3.taskbar.KeyboardQuickSwitchTaskView>
diff --git a/quickstep/res/layout/task.xml b/quickstep/res/layout/task.xml
index 34193d3..bdfd241 100644
--- a/quickstep/res/layout/task.xml
+++ b/quickstep/res/layout/task.xml
@@ -47,4 +47,8 @@
android:inflatedId="@id/icon"
android:layout_height="wrap_content"
android:layout_width="wrap_content" />
+
+ <include layout="@layout/digital_wellbeing_toast"
+ android:id="@+id/digital_wellbeing_toast"
+ android:visibility="invisible"/>
</com.android.quickstep.views.TaskView>
\ No newline at end of file
diff --git a/quickstep/res/layout/task_grouped.xml b/quickstep/res/layout/task_grouped.xml
index cb4b98f..00a990b 100644
--- a/quickstep/res/layout/task_grouped.xml
+++ b/quickstep/res/layout/task_grouped.xml
@@ -73,4 +73,12 @@
android:inflatedId="@id/bottomRight_icon"
android:layout_height="wrap_content"
android:layout_width="wrap_content" />
+
+ <include layout="@layout/digital_wellbeing_toast"
+ android:id="@+id/digital_wellbeing_toast"
+ android:visibility="invisible"/>
+
+ <include layout="@layout/digital_wellbeing_toast"
+ android:id="@+id/bottomRight_digital_wellbeing_toast"
+ android:visibility="invisible"/>
</com.android.quickstep.views.GroupedTaskView>
\ No newline at end of file
diff --git a/quickstep/res/values-land/dimens.xml b/quickstep/res/values-land/dimens.xml
index e862b9e..2239f8b 100644
--- a/quickstep/res/values-land/dimens.xml
+++ b/quickstep/res/values-land/dimens.xml
@@ -83,7 +83,7 @@
<dimen name="taskbar_suw_frame">96dp</dimen>
<dimen name="taskbar_suw_insets">24dp</dimen>
- <dimen name="keyboard_quick_switch_taskview_width">205dp</dimen>
- <dimen name="keyboard_quick_switch_taskview_height">119dp</dimen>
+ <!-- Keyboard Quick Switch -->
+ <dimen name="keyboard_quick_switch_taskview_width">217.6dp</dimen>
</resources>
diff --git a/quickstep/res/values-night/styles.xml b/quickstep/res/values-night/styles.xml
index 2cb633a..eb88310 100644
--- a/quickstep/res/values-night/styles.xml
+++ b/quickstep/res/values-night/styles.xml
@@ -14,16 +14,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
-
- <style name="AllSetTheme" parent="@android:style/Theme.DeviceDefault.NoActionBar">
- <item name="android:navigationBarColor">@android:color/transparent</item>
- <item name="android:statusBarColor">@android:color/transparent</item>
- <item name="android:enforceNavigationBarContrast">false</item>
- <item name="android:windowLightStatusBar">false</item>
- <item name="android:windowBackground">@android:color/transparent</item>
- </style>
+<resources>
<style name="TextAppearance.GestureTutorial.MainTitle.Home"
parent="TextAppearance.GestureTutorial.MainTitle">
diff --git a/quickstep/res/values/attrs.xml b/quickstep/res/values/attrs.xml
index ccc7f18..7fd6b5c 100644
--- a/quickstep/res/values/attrs.xml
+++ b/quickstep/res/values/attrs.xml
@@ -28,6 +28,7 @@
<!-- Border color for a keyboard quick switch task views -->
<attr name="focusBorderColor" format="color" />
<attr name="hoverBorderColor" format="color" />
+ <attr name="focusBorderRadius" format="dimension" />
</declare-styleable>
<declare-styleable name="ClearAllButton">
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index d981882..ce3f3ac 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -437,6 +437,7 @@
<dimen name="bubblebar_stashed_handle_width">55dp</dimen>
<dimen name="bubblebar_stashed_size">@dimen/transient_taskbar_stashed_height</dimen>
<dimen name="bubblebar_stashed_handle_height">@dimen/taskbar_stashed_handle_height</dimen>
+ <dimen name="bubblebar_stashed_handle_spring_velocity_dp_per_s">@dimen/transient_taskbar_stash_spring_velocity_dp_per_s</dimen>
<!-- this is a pointer height minus 1dp to ensure the pointer overlaps with the bubblebar
background appropriately when close to the rounded corner -->
<dimen name="bubblebar_pointer_visible_size">9dp</dimen>
@@ -487,17 +488,22 @@
<!-- Keyboard Quick Switch -->
<dimen name="keyboard_quick_switch_border_width">4dp</dimen>
<dimen name="keyboard_quick_switch_taskview_width">104dp</dimen>
- <dimen name="keyboard_quick_switch_taskview_height">134dp</dimen>
+ <dimen name="keyboard_quick_switch_taskview_height">136dp</dimen>
<dimen name="keyboard_quick_switch_taskview_icon_size">52dp</dimen>
<dimen name="keyboard_quick_switch_recents_icon_size">20dp</dimen>
+ <dimen name="keyboard_quick_switch_desktop_icon_size">32dp</dimen>
<dimen name="keyboard_quick_switch_margin_top">56dp</dimen>
<dimen name="keyboard_quick_switch_margin_ends">16dp</dimen>
<dimen name="keyboard_quick_switch_view_spacing">16dp</dimen>
- <dimen name="keyboard_quick_switch_split_view_spacing">2dp</dimen>
+ <dimen name="keyboard_quick_switch_view_small_spacing">4dp</dimen>
<dimen name="keyboard_quick_switch_view_radius">28dp</dimen>
<dimen name="keyboard_quick_switch_task_view_radius">16dp</dimen>
<dimen name="keyboard_quick_switch_no_recent_items_icon_size">24dp</dimen>
<dimen name="keyboard_quick_switch_no_recent_items_icon_margin">8dp</dimen>
+ <dimen name="keyboard_quick_switch_text_button_width">104dp</dimen>
+ <dimen name="keyboard_quick_switch_text_button_radius">360dp</dimen>
+ <dimen name="keyboard_quick_switch_text_button_horizontal_padding">16dp</dimen>
+ <dimen name="keyboard_quick_switch_text_button_fade_edge_length">20dp</dimen>
<!-- Digital Wellbeing -->
<dimen name="digital_wellbeing_toast_height">48dp</dimen>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index d0f474f..f72f3c5 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -327,17 +327,14 @@
<!-- Label for moving drop target to the bottom or right side of the screen, depending on orientation (from the Taskbar only). -->
<string name="move_drop_target_bottom_or_right">Move to bottom/right</string>
- <!-- Label for quick switch tile showing how many more apps are available [CHAR LIMIT=NONE] -->
+ <!-- Label for quick switch tile showing how many more apps are available. The number will be displayed above this text. [CHAR LIMIT=NONE] -->
<string name="quick_switch_overflow">{count, plural,
- =1{Show # more app.}
- other{Show # more apps.}
+ =1{more app}
+ other{more apps}
}</string>
- <!-- Label for quick switch tile showing how many apps are available in desktop mode [CHAR LIMIT=NONE] -->
- <string name="quick_switch_desktop">{count, plural,
- =1{Show # desktop app.}
- other{Show # desktop apps.}
- }</string>
+ <!-- Label for quick switch tile to launch desktop mode [CHAR LIMIT=NONE] -->
+ <string name="quick_switch_desktop">Desktop</string>
<!-- Accessibility label for quick switch tiles showing split tasks [CHAR LIMIT=NONE] -->
<string name="quick_switch_split_task"><xliff:g id="app_name_1" example="Chrome">%1$s</xliff:g> and <xliff:g id="app_name_2" example="Gmail">%2$s</xliff:g></string>
diff --git a/quickstep/res/values/styles.xml b/quickstep/res/values/styles.xml
index 1f4720c..c423d09 100644
--- a/quickstep/res/values/styles.xml
+++ b/quickstep/res/values/styles.xml
@@ -205,7 +205,7 @@
<item name="android:textColor">?attr/tutorialSubtitle</item>
</style>
- <style name="AllSetTheme" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar">
+ <style name="AllSetTheme" parent="@style/DynamicColorsBaseLauncherTheme.NoActionBar">
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:enforceNavigationBarContrast">false</item>
@@ -272,10 +272,22 @@
<item name="lineHeight">20sp</item>
</style>
+ <style name="KeyboardQuickSwitchText.LargeText" parent="KeyboardQuickSwitchText">
+ <item name="android:textSize">32sp</item>
+ </style>
+
<style name="KeyboardQuickSwitchText.OnBackground" parent="KeyboardQuickSwitchText">
<item name="android:textColor">?attr/materialColorOnSurface</item>
</style>
+ <style name="KeyboardQuickSwitchText.OnTaskView" parent="KeyboardQuickSwitchText">
+ <item name="android:requiresFadingEdge">horizontal</item>
+ <item name="android:fadeScrollbars">false</item>
+ <item name="android:fadingEdgeLength">20dp</item>
+ <item name="android:ellipsize">none</item>
+ <item name="android:singleLine">true</item>
+ </style>
+
<style name="GestureTutorialActivity" parent="@style/AppTheme">
<item name="background">@android:color/transparent</item>
<item name="tutorialSubtitle">@android:color/black</item>
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 4a1035f..e51c956 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -65,7 +65,6 @@
import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import static com.android.launcher3.views.FloatingIconView.getFloatingIconView;
-import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
import static com.android.quickstep.util.AnimUtils.clampToDuration;
import static com.android.quickstep.util.AnimUtils.completeRunnableListCallback;
@@ -1228,9 +1227,7 @@
* Registers remote animations used when closing apps to home screen.
*/
public void registerRemoteTransitions() {
- if (ENABLE_SHELL_TRANSITIONS) {
- SystemUiProxy.INSTANCE.get(mLauncher).shareTransactionQueue();
- }
+ SystemUiProxy.INSTANCE.get(mLauncher).shareTransactionQueue();
if (SEPARATE_RECENTS_ACTIVITY.get()) {
return;
}
@@ -1294,9 +1291,7 @@
}
protected void unregisterRemoteTransitions() {
- if (ENABLE_SHELL_TRANSITIONS) {
- SystemUiProxy.INSTANCE.get(mLauncher).unshareTransactionQueue();
- }
+ SystemUiProxy.INSTANCE.get(mLauncher).unshareTransactionQueue();
if (SEPARATE_RECENTS_ACTIVITY.get()) {
return;
}
diff --git a/quickstep/src/com/android/launcher3/WidgetPickerActivity.java b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
index e925af6..50e8e5e 100644
--- a/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
+++ b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
@@ -20,7 +20,6 @@
import static android.view.WindowInsets.Type.navigationBars;
import static android.view.WindowInsets.Type.statusBars;
-import static com.android.launcher3.Flags.enablePredictiveBackGesture;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
@@ -170,11 +169,6 @@
@Override
protected void registerBackDispatcher() {
- if (!enablePredictiveBackGesture()) {
- super.registerBackDispatcher();
- return;
- }
-
getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
OnBackInvokedDispatcher.PRIORITY_DEFAULT,
new BackAnimationCallback());
diff --git a/quickstep/src/com/android/launcher3/dagger/LauncherAppComponent.java b/quickstep/src/com/android/launcher3/dagger/LauncherAppComponent.java
new file mode 100644
index 0000000..dab2582
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/dagger/LauncherAppComponent.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.dagger;
+
+import dagger.Component;
+
+import javax.inject.Singleton;
+
+/**
+ * Root component for Dagger injection for Launcher Quickstep.
+ */
+@Singleton
+@Component
+public interface LauncherAppComponent extends LauncherBaseAppComponent {
+ /** Builder for quickstep LauncherAppComponent. */
+ @Component.Builder
+ interface Builder extends LauncherBaseAppComponent.Builder {
+ LauncherAppComponent build();
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
index 644705b..e31b1d4 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
@@ -36,10 +36,10 @@
import com.android.quickstep.GestureState;
import com.android.quickstep.SystemUiProxy;
import com.android.wm.shell.desktopmode.IDesktopTaskListener;
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import java.util.HashSet;
import java.util.Set;
-import java.util.concurrent.Executor;
/**
* Controls the visibility of the workspace and the resumed / paused state when desktop mode
@@ -51,6 +51,7 @@
private static final boolean DEBUG = false;
private final Launcher mLauncher;
private final Set<DesktopVisibilityListener> mDesktopVisibilityListeners = new HashSet<>();
+ private final Set<TaskbarDesktopModeListener> mTaskbarDesktopModeListeners = new HashSet<>();
private int mVisibleDesktopTasksCount;
private boolean mInOverviewState;
@@ -110,6 +111,16 @@
mDesktopVisibilityListeners.remove(listener);
}
+ /** Registers a listener for Taskbar changes in Desktop Mode. */
+ public void registerTaskbarDesktopModeListener(TaskbarDesktopModeListener listener) {
+ mTaskbarDesktopModeListeners.add(listener);
+ }
+
+ /** Removes a previously registered listener for Taskbar changes in Desktop Mode. */
+ public void unregisterTaskbarDesktopModeListener(TaskbarDesktopModeListener listener) {
+ mTaskbarDesktopModeListeners.remove(listener);
+ }
+
/**
* Sets the number of desktop windows that are visible and updates launcher visibility based on
* it.
@@ -201,6 +212,16 @@
DisplayController.handleInfoChangeForDesktopMode(mLauncher);
}
+ private void notifyTaskbarDesktopModeListeners(boolean doesAnyTaskRequireTaskbarRounding) {
+ if (DEBUG) {
+ Log.d(TAG, "notifyTaskbarDesktopModeListeners: doesAnyTaskRequireTaskbarRounding="
+ + doesAnyTaskRequireTaskbarRounding);
+ }
+ for (TaskbarDesktopModeListener listener : mTaskbarDesktopModeListeners) {
+ listener.onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding);
+ }
+ }
+
/**
* TODO: b/333533253 - Remove after flag rollout
*/
@@ -377,7 +398,29 @@
@Override
public void onStashedChanged(int displayId, boolean stashed) {
- Log.w(TAG, "IDesktopTaskListener: onStashedChanged is deprecated");
+ Log.w(TAG, "DesktopTaskListenerImpl: onStashedChanged is deprecated");
}
+
+ @Override
+ public void onTaskbarCornerRoundingUpdate(boolean doesAnyTaskRequireTaskbarRounding) {
+ MAIN_EXECUTOR.execute(() -> {
+ if (mController != null && DesktopModeStatus.useRoundedCorners()) {
+ Log.d(TAG, "DesktopTaskListenerImpl: doesAnyTaskRequireTaskbarRounding= "
+ + doesAnyTaskRequireTaskbarRounding);
+ mController.notifyTaskbarDesktopModeListeners(
+ doesAnyTaskRequireTaskbarRounding);
+ }
+ });
+ }
+ }
+
+ /** A listener for Taskbar in Desktop Mode. */
+ public interface TaskbarDesktopModeListener {
+ /**
+ * Callback for when task is resized in desktop mode.
+ *
+ * @param doesAnyTaskRequireTaskbarRounding whether task requires taskbar corner roundness.
+ */
+ void onTaskbarCornerRoundingUpdate(boolean doesAnyTaskRequireTaskbarRounding);
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
index 46501c4..e4cc6bb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
@@ -29,6 +29,7 @@
import com.android.quickstep.RecentsModel;
import com.android.quickstep.util.DesktopTask;
import com.android.quickstep.util.GroupTask;
+import com.android.quickstep.util.LayoutUtils;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -66,6 +67,9 @@
@Nullable private KeyboardQuickSwitchViewController mQuickSwitchViewController;
+ private boolean mHasDesktopTask = false;
+ private boolean mWasDesktopTaskFilteredOut = false;
+
/** Initialize the controller. */
public void init(@NonNull TaskbarControllers controllers) {
mControllers = controllers;
@@ -126,11 +130,15 @@
/* updateTasks= */ false,
currentFocusedIndex == -1 && !mControllerCallbacks.isFirstTaskRunning()
? 0 : currentFocusedIndex,
- onDesktop);
+ onDesktop,
+ mHasDesktopTask,
+ mWasDesktopTaskFilteredOut);
return;
}
mTaskListChangeId = mModel.getTasks((tasks) -> {
+ mHasDesktopTask = false;
+ mWasDesktopTaskFilteredOut = false;
if (onDesktop) {
processLoadedTasksOnDesktop(tasks);
} else {
@@ -144,7 +152,9 @@
/* updateTasks= */ true,
currentFocusedIndex == -1 && !mControllerCallbacks.isFirstTaskRunning()
? 0 : currentFocusedIndex,
- onDesktop);
+ onDesktop,
+ mHasDesktopTask,
+ mWasDesktopTaskFilteredOut);
});
}
@@ -152,9 +162,22 @@
// Only store MAX_TASK tasks, from most to least recent
Collections.reverse(tasks);
mTasks = tasks.stream()
+ .filter(task -> !(task instanceof DesktopTask))
.limit(MAX_TASKS)
.collect(Collectors.toList());
- mNumHiddenTasks = Math.max(0, tasks.size() - MAX_TASKS);
+
+ for (int i = 0; i < tasks.size(); i++) {
+ if (tasks.get(i) instanceof DesktopTask) {
+ mHasDesktopTask = true;
+ if (i < mTasks.size()) {
+ mWasDesktopTaskFilteredOut = true;
+ }
+ break;
+ }
+ }
+
+ mNumHiddenTasks = Math.max(0,
+ tasks.size() - (mWasDesktopTaskFilteredOut ? 1 : 0) - MAX_TASKS);
}
private void processLoadedTasksOnDesktop(List<GroupTask> tasks) {
@@ -214,6 +237,8 @@
pw.println(prefix + "\tisOpen=" + (mQuickSwitchViewController != null));
pw.println(prefix + "\tmNumHiddenTasks=" + mNumHiddenTasks);
pw.println(prefix + "\tmTaskListChangeId=" + mTaskListChangeId);
+ pw.println(prefix + "\tmHasDesktopTask=" + mHasDesktopTask);
+ pw.println(prefix + "\tmWasDesktopTaskFilteredOut=" + mWasDesktopTaskFilteredOut);
pw.println(prefix + "\tmTasks=[");
for (GroupTask task : mTasks) {
Task task1 = task.task1;
@@ -235,10 +260,6 @@
class ControllerCallbacks {
- int getTaskCount() {
- return mTasks.size() + (mNumHiddenTasks == 0 ? 0 : 1);
- }
-
@Nullable
GroupTask getTaskAt(int index) {
return index < 0 || index >= mTasks.size() ? null : mTasks.get(index);
@@ -279,5 +300,10 @@
boolean isFirstTaskRunning() {
return isTaskRunning(getTaskAt(0));
}
+
+ boolean isAspectRatioSquare() {
+ return mControllers != null && LayoutUtils.isAspectRatioSquare(
+ mControllers.taskbarActivityContext.getDeviceProfile().aspectRatio);
+ }
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
index 39b4f77..8ceb77d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
@@ -50,8 +50,10 @@
public class KeyboardQuickSwitchTaskView extends ConstraintLayout {
private static final float THUMBNAIL_BLUR_RADIUS = 1f;
+ private static final int INVALID_BORDER_RADIUS = -1;
@ColorInt private final int mBorderColor;
+ @ColorInt private final int mBorderRadius;
@Nullable private BorderAnimator mBorderAnimator;
@@ -87,6 +89,8 @@
mBorderColor = ta.getColor(
R.styleable.TaskView_focusBorderColor, DEFAULT_BORDER_COLOR);
+ mBorderRadius = ta.getDimensionPixelSize(
+ R.styleable.TaskView_focusBorderRadius, INVALID_BORDER_RADIUS);
ta.recycle();
}
@@ -103,8 +107,10 @@
Preconditions.assertNotNull(mContent);
mBorderAnimator = BorderAnimator.createScalingBorderAnimator(
- /* borderRadiusPx= */ resources.getDimensionPixelSize(
- R.dimen.keyboard_quick_switch_task_view_radius),
+ /* borderRadiusPx= */ mBorderRadius != INVALID_BORDER_RADIUS
+ ? mBorderRadius
+ : resources.getDimensionPixelSize(
+ R.dimen.keyboard_quick_switch_task_view_radius),
/* borderWidthPx= */ resources.getDimensionPixelSize(
R.dimen.keyboard_quick_switch_border_width),
/* boundsBuilder= */ bounds -> {
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
index dbd9c73..a527c82 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
@@ -36,14 +36,12 @@
import android.view.ViewTreeObserver;
import android.view.animation.Interpolator;
import android.widget.HorizontalScrollView;
-import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
-import androidx.core.content.res.ResourcesCompat;
import com.android.app.animation.Interpolators;
import com.android.internal.jank.Cuj;
@@ -52,9 +50,7 @@
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
-import com.android.quickstep.util.DesktopTask;
import com.android.quickstep.util.GroupTask;
-import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import java.util.HashMap;
@@ -102,9 +98,13 @@
private int mTaskViewWidth;
private int mTaskViewHeight;
private int mSpacing;
+ private int mSmallSpacing;
private int mOutlineRadius;
private boolean mIsRtl;
+ private int mOverviewTaskIndex = -1;
+ private int mDesktopTaskIndex = -1;
+
@Nullable private AnimatorSet mOpenAnimation;
@Nullable private KeyboardQuickSwitchViewController.ViewCallbacks mViewCallbacks;
@@ -141,6 +141,8 @@
mTaskViewHeight = resources.getDimensionPixelSize(
R.dimen.keyboard_quick_switch_taskview_height);
mSpacing = resources.getDimensionPixelSize(R.dimen.keyboard_quick_switch_view_spacing);
+ mSmallSpacing = resources.getDimensionPixelSize(
+ R.dimen.keyboard_quick_switch_view_small_spacing);
mOutlineRadius = resources.getDimensionPixelSize(R.dimen.keyboard_quick_switch_view_radius);
mIsRtl = Utilities.isRtl(resources);
}
@@ -148,6 +150,7 @@
private KeyboardQuickSwitchTaskView createAndAddTaskView(
int index,
boolean isFinalView,
+ boolean useSmallStartSpacing,
@LayoutRes int resId,
@NonNull LayoutInflater layoutInflater,
@Nullable View previousView) {
@@ -156,7 +159,7 @@
taskView.setId(View.generateViewId());
taskView.setOnClickListener(v -> mViewCallbacks.launchTaskAt(index));
- LayoutParams lp = new LayoutParams(mTaskViewWidth, mTaskViewHeight);
+ LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
// Create a left-to-right ordering of views (or right-to-left in RTL locales)
if (previousView != null) {
lp.startToEnd = previousView.getId();
@@ -166,7 +169,7 @@
lp.topToTop = PARENT_ID;
lp.bottomToBottom = PARENT_ID;
// Add spacing between views
- lp.setMarginStart(mSpacing);
+ lp.setMarginStart(useSmallStartSpacing ? mSmallSpacing : mSpacing);
if (isFinalView) {
// Add spacing to the end of the final view so that scrolling ends with some padding.
lp.endToEnd = PARENT_ID;
@@ -185,7 +188,8 @@
int numHiddenTasks,
boolean updateTasks,
int currentFocusIndexOverride,
- @NonNull KeyboardQuickSwitchViewController.ViewCallbacks viewCallbacks) {
+ @NonNull KeyboardQuickSwitchViewController.ViewCallbacks viewCallbacks,
+ boolean useDesktopTaskView) {
mViewCallbacks = viewCallbacks;
Resources resources = context.getResources();
Resources.Theme theme = context.getTheme();
@@ -197,57 +201,62 @@
GroupTask groupTask = groupTasks.get(i);
KeyboardQuickSwitchTaskView currentTaskView = createAndAddTaskView(
i,
- /* isFinalView= */ i == tasksToDisplay - 1 && numHiddenTasks == 0,
- groupTask instanceof DesktopTask
- ? R.layout.keyboard_quick_switch_textonly_taskview
+ /* isFinalView= */ i == tasksToDisplay - 1
+ && numHiddenTasks == 0 && !useDesktopTaskView,
+ /* useSmallStartSpacing= */ false,
+ mViewCallbacks.isAspectRatioSquare()
+ ? R.layout.keyboard_quick_switch_taskview_square
: R.layout.keyboard_quick_switch_taskview,
layoutInflater,
previousTaskView);
- if (groupTask instanceof DesktopTask desktopTask) {
- HashMap<String, Integer> args = new HashMap<>();
- args.put("count", desktopTask.tasks.size());
+ final boolean firstTaskIsLeftTopTask =
+ groupTask.mSplitBounds == null
+ || groupTask.mSplitBounds.leftTopTaskId == groupTask.task1.key.id
+ || groupTask.task2 == null;
+ currentTaskView.setThumbnails(
+ firstTaskIsLeftTopTask ? groupTask.task1 : groupTask.task2,
+ firstTaskIsLeftTopTask ? groupTask.task2 : groupTask.task1,
+ updateTasks ? mViewCallbacks::updateThumbnailInBackground : null,
+ updateTasks ? mViewCallbacks::updateIconInBackground : null);
- currentTaskView.<ImageView>findViewById(R.id.icon).setImageDrawable(
- ResourcesCompat.getDrawable(resources, R.drawable.ic_desktop, theme));
- currentTaskView.<TextView>findViewById(R.id.text).setText(new MessageFormat(
- resources.getString(R.string.quick_switch_desktop),
- Locale.getDefault()).format(args));
- } else {
- final boolean firstTaskIsLeftTopTask =
- groupTask.mSplitBounds == null
- || groupTask.mSplitBounds.leftTopTaskId == groupTask.task1.key.id;
- final Task leftTopTask = firstTaskIsLeftTopTask
- ? groupTask.task1 : groupTask.task2;
- final Task rightBottomTask = firstTaskIsLeftTopTask
- ? groupTask.task2 : groupTask.task1;
- currentTaskView.setThumbnails(
- leftTopTask,
- rightBottomTask,
- updateTasks ? mViewCallbacks::updateThumbnailInBackground : null,
- updateTasks ? mViewCallbacks::updateIconInBackground : null);
- }
previousTaskView = currentTaskView;
}
-
if (numHiddenTasks > 0) {
HashMap<String, Integer> args = new HashMap<>();
args.put("count", numHiddenTasks);
+ mOverviewTaskIndex = getTaskCount();
View overviewButton = createAndAddTaskView(
- MAX_TASKS,
- /* isFinalView= */ true,
- R.layout.keyboard_quick_switch_textonly_taskview,
+ mOverviewTaskIndex,
+ /* isFinalView= */ !useDesktopTaskView,
+ /* useSmallStartSpacing= */ false,
+ R.layout.keyboard_quick_switch_overview_taskview,
layoutInflater,
previousTaskView);
- overviewButton.<ImageView>findViewById(R.id.icon).setImageDrawable(
- ResourcesCompat.getDrawable(resources, R.drawable.view_carousel, theme));
- overviewButton.<TextView>findViewById(R.id.text).setText(new MessageFormat(
+ overviewButton.<TextView>findViewById(R.id.large_text).setText(
+ String.format(Locale.getDefault(), "%d", numHiddenTasks));
+ overviewButton.<TextView>findViewById(R.id.small_text).setText(new MessageFormat(
resources.getString(R.string.quick_switch_overflow),
Locale.getDefault()).format(args));
+
+ previousTaskView = overviewButton;
}
- mDisplayingRecentTasks = !groupTasks.isEmpty();
+ if (useDesktopTaskView) {
+ mDesktopTaskIndex = getTaskCount();
+ View desktopButton = createAndAddTaskView(
+ mDesktopTaskIndex,
+ /* isFinalView= */ true,
+ /* useSmallStartSpacing= */ numHiddenTasks > 0,
+ R.layout.keyboard_quick_switch_desktop_taskview,
+ layoutInflater,
+ previousTaskView);
+
+ desktopButton.<TextView>findViewById(R.id.text).setText(
+ resources.getString(R.string.quick_switch_desktop));
+ }
+ mDisplayingRecentTasks = !groupTasks.isEmpty() || useDesktopTaskView;
getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
@@ -260,6 +269,14 @@
});
}
+ int getOverviewTaskIndex() {
+ return mOverviewTaskIndex;
+ }
+
+ int getDesktopTaskIndex() {
+ return mDesktopTaskIndex;
+ }
+
protected Animator getCloseAnimation() {
AnimatorSet closeAnimation = new AnimatorSet();
@@ -370,7 +387,7 @@
}
});
animateFocusMove(-1, Math.min(
- mContent.getChildCount() - 1,
+ getTaskCount() - 1,
currentFocusIndexOverride == -1 ? 1 : currentFocusIndexOverride));
displayedContent.setVisibility(VISIBLE);
setVisibility(VISIBLE);
@@ -577,7 +594,11 @@
@Nullable
protected KeyboardQuickSwitchTaskView getTaskAt(int index) {
- return !mDisplayingRecentTasks || index < 0 || index >= mContent.getChildCount()
+ return !mDisplayingRecentTasks || index < 0 || index >= getTaskCount()
? null : (KeyboardQuickSwitchTaskView) mContent.getChildAt(index);
}
+
+ public int getTaskCount() {
+ return mContent.getChildCount();
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
index d411ba6..40e77e2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.taskbar;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.view.KeyEvent;
@@ -28,6 +30,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
+import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.util.SlideInRemoteTransition;
import com.android.systemui.shared.recents.model.Task;
@@ -56,6 +59,7 @@
private int mCurrentFocusIndex = -1;
private boolean mOnDesktop;
+ private boolean mWasDesktopTaskFilteredOut;
protected KeyboardQuickSwitchViewController(
@NonNull TaskbarControllers controllers,
@@ -77,9 +81,12 @@
int numHiddenTasks,
boolean updateTasks,
int currentFocusIndexOverride,
- boolean onDesktop) {
+ boolean onDesktop,
+ boolean hasDesktopTask,
+ boolean wasDesktopTaskFilteredOut) {
mOverlayContext.getDragLayer().addView(mKeyboardQuickSwitchView);
mOnDesktop = onDesktop;
+ mWasDesktopTaskFilteredOut = wasDesktopTaskFilteredOut;
mKeyboardQuickSwitchView.applyLoadPlan(
mOverlayContext,
@@ -87,7 +94,8 @@
numHiddenTasks,
updateTasks,
currentFocusIndexOverride,
- mViewCallbacks);
+ mViewCallbacks,
+ /* useDesktopTaskView= */ !onDesktop && hasDesktopTask);
}
boolean isCloseAnimationRunning() {
@@ -136,7 +144,7 @@
}
// If the user quick switches too quickly, updateCurrentFocusIndex might not have run.
return launchTaskAt(mControllerCallbacks.isFirstTaskRunning()
- && mControllerCallbacks.getTaskCount() > 1 ? 1 : 0);
+ && mKeyboardQuickSwitchView.getTaskCount() > 1 ? 1 : 0);
}
private int launchTaskAt(int index) {
@@ -144,17 +152,11 @@
// Ignore taps on task views and alt key unpresses while the close animation is running.
return -1;
}
- // Even with a valid index, this can be null if the user tries to quick switch before the
- // views have been added in the KeyboardQuickSwitchView.
- GroupTask task = mControllerCallbacks.getTaskAt(index);
- if (task == null) {
- return mOnDesktop ? 1 : Math.max(0, index);
+ if (index == mKeyboardQuickSwitchView.getOverviewTaskIndex()) {
+ // If there is a desktop task view, then we should account for it when focusing the
+ // first hidden non-desktop task view in recents view
+ return mOnDesktop ? 1 : (mWasDesktopTaskFilteredOut ? index + 1 : index);
}
- if (mControllerCallbacks.isTaskRunning(task)) {
- // Ignore attempts to run the selected task if it is already running.
- return -1;
- }
-
Runnable onStartCallback = () -> InteractionJankMonitorWrapper.begin(
mKeyboardQuickSwitchView, Cuj.CUJ_LAUNCHER_KEYBOARD_QUICK_SWITCH_APP_LAUNCH);
Runnable onFinishCallback = () -> InteractionJankMonitorWrapper.end(
@@ -169,6 +171,24 @@
onStartCallback,
onFinishCallback),
"SlideInTransition");
+ if (index == mKeyboardQuickSwitchView.getDesktopTaskIndex()) {
+ UI_HELPER_EXECUTOR.execute(() ->
+ SystemUiProxy.INSTANCE.get(mKeyboardQuickSwitchView.getContext())
+ .showDesktopApps(
+ mKeyboardQuickSwitchView.getDisplay().getDisplayId(),
+ remoteTransition));
+ return -1;
+ }
+ // Even with a valid index, this can be null if the user tries to quick switch before the
+ // views have been added in the KeyboardQuickSwitchView.
+ GroupTask task = mControllerCallbacks.getTaskAt(index);
+ if (task == null) {
+ return mOnDesktop ? 1 : Math.max(0, index);
+ }
+ if (mControllerCallbacks.isTaskRunning(task)) {
+ // Ignore attempts to run the selected task if it is already running.
+ return -1;
+ }
mControllers.taskbarActivityContext.handleGroupTaskLaunch(
task,
remoteTransition,
@@ -195,6 +215,8 @@
pw.println(prefix + "\thasFocus=" + mKeyboardQuickSwitchView.hasFocus());
pw.println(prefix + "\tisCloseAnimationRunning=" + isCloseAnimationRunning());
pw.println(prefix + "\tmCurrentFocusIndex=" + mCurrentFocusIndex);
+ pw.println(prefix + "\tmOnDesktop=" + mOnDesktop);
+ pw.println(prefix + "\tmWasDesktopTaskFilteredOut=" + mWasDesktopTaskFilteredOut);
}
class ViewCallbacks {
@@ -222,7 +244,7 @@
boolean traverseBackwards = (keyCode == KeyEvent.KEYCODE_TAB && event.isShiftPressed())
|| (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT && isRTL)
|| (keyCode == KeyEvent.KEYCODE_DPAD_LEFT && !isRTL);
- int taskCount = mControllerCallbacks.getTaskCount();
+ int taskCount = mKeyboardQuickSwitchView.getTaskCount();
int toIndex = mCurrentFocusIndex == -1
// Focus the second-most recent app if possible
? (taskCount > 1 ? 1 : 0)
@@ -257,5 +279,9 @@
void updateIconInBackground(Task task, Consumer<Task> callback) {
mControllerCallbacks.updateIconInBackground(task, callback);
}
+
+ boolean isAspectRatioSquare() {
+ return mControllerCallbacks.isAspectRatioSquare();
+ }
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 96a6d28..69da7b6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -19,7 +19,6 @@
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.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
import static com.android.wm.shell.shared.desktopmode.DesktopModeFlags.WALLPAPER_ACTIVITY;
import android.animation.Animator;
@@ -232,9 +231,7 @@
LauncherState state = mLauncher.getStateManager().getState();
boolean nonInteractiveState = state.hasFlag(FLAG_NON_INTERACTIVE)
&& !state.isTaskbarAlignedWithHotseat(mLauncher);
- if ((ENABLE_SHELL_TRANSITIONS
- && isVisible
- && (nonInteractiveState || mSkipLauncherVisibilityChange))) {
+ if (isVisible && (nonInteractiveState || mSkipLauncherVisibilityChange)) {
return null;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index f61840a..a979d58 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -63,7 +63,6 @@
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.Region.Op;
-import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.PaintDrawable;
import android.graphics.drawable.RotateDrawable;
@@ -74,8 +73,6 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnAttachStateChangeListener;
-import android.view.View.OnClickListener;
-import android.view.View.OnHoverListener;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
@@ -106,7 +103,6 @@
import com.android.systemui.shared.navigationbar.KeyButtonRipple;
import com.android.systemui.shared.rotation.FloatingRotationButton;
import com.android.systemui.shared.rotation.RotationButton;
-import com.android.systemui.shared.rotation.RotationButtonController;
import com.android.systemui.shared.statusbar.phone.BarTransitions;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
@@ -304,8 +300,13 @@
.get(ALPHA_INDEX_SMALL_SCREEN),
flags -> (flags & FLAG_SMALL_SCREEN) == 0));
- mPropertyHolders.add(new StatePropertyHolder(mControllers.taskbarDragLayerController
- .getKeyguardBgTaskbar(), flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0));
+ if (!mContext.isPhoneMode()) {
+ mPropertyHolders.add(new StatePropertyHolder(mControllers.taskbarDragLayerController
+ .getKeyguardBgTaskbar(), flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0));
+ }
+
+ // Start at 1 because relevant flags are unset at init.
+ mOnBackgroundNavButtonColorOverrideMultiplier.value = 1;
// Force nav buttons (specifically back button) to be visible during setup wizard.
boolean isInSetup = !mContext.isUserSetupComplete();
@@ -317,39 +318,41 @@
// - IME is showing (add separate translation for IME)
// - VoiceInteractionWindow (assistant) is showing
// - Keyboard shortcuts helper is showing
- int flagsToRemoveTranslation = FLAG_NOTIFICATION_SHADE_EXPANDED | FLAG_IME_VISIBLE
- | FLAG_VOICE_INTERACTION_WINDOW_SHOWING | FLAG_KEYBOARD_SHORTCUT_HELPER_SHOWING;
- mPropertyHolders.add(new StatePropertyHolder(mNavButtonInAppDisplayProgressForSysui,
- flags -> (flags & flagsToRemoveTranslation) != 0, AnimatedFloat.VALUE,
- 1, 0));
- // Center nav buttons in new height for IME.
- float transForIme = (mContext.getDeviceProfile().taskbarHeight
- - mControllers.taskbarInsetsController.getTaskbarHeightForIme()) / 2f;
- // For gesture nav, nav buttons only show for IME anyway so keep them translated down.
- float defaultButtonTransY = alwaysShowButtons ? 0 : transForIme;
- mPropertyHolders.add(new StatePropertyHolder(mTaskbarNavButtonTranslationYForIme,
- flags -> (flags & FLAG_IME_VISIBLE) != 0 && !isInKidsMode, AnimatedFloat.VALUE,
- transForIme, defaultButtonTransY));
+ if (!mContext.isPhoneMode()) {
+ int flagsToRemoveTranslation = FLAG_NOTIFICATION_SHADE_EXPANDED | FLAG_IME_VISIBLE
+ | FLAG_VOICE_INTERACTION_WINDOW_SHOWING | FLAG_KEYBOARD_SHORTCUT_HELPER_SHOWING;
+ mPropertyHolders.add(new StatePropertyHolder(mNavButtonInAppDisplayProgressForSysui,
+ flags -> (flags & flagsToRemoveTranslation) != 0, AnimatedFloat.VALUE,
+ 1, 0));
+ // Center nav buttons in new height for IME.
+ float transForIme = (mContext.getDeviceProfile().taskbarHeight
+ - mControllers.taskbarInsetsController.getTaskbarHeightForIme()) / 2f;
+ // For gesture nav, nav buttons only show for IME anyway so keep them translated down.
+ float defaultButtonTransY = alwaysShowButtons ? 0 : transForIme;
+ mPropertyHolders.add(new StatePropertyHolder(mTaskbarNavButtonTranslationYForIme,
+ flags -> (flags & FLAG_IME_VISIBLE) != 0 && !isInKidsMode, AnimatedFloat.VALUE,
+ transForIme, defaultButtonTransY));
- // Start at 1 because relevant flags are unset at init.
- mOnBackgroundNavButtonColorOverrideMultiplier.value = 1;
- mPropertyHolders.add(new StatePropertyHolder(
- mOnBackgroundNavButtonColorOverrideMultiplier,
- flags -> (flags & FLAGS_ON_BACKGROUND_COLOR_OVERRIDE_DISABLED) == 0));
+ mPropertyHolders.add(new StatePropertyHolder(
+ mOnBackgroundNavButtonColorOverrideMultiplier,
+ flags -> (flags & FLAGS_ON_BACKGROUND_COLOR_OVERRIDE_DISABLED) == 0));
- mPropertyHolders.add(new StatePropertyHolder(
- mSlideInViewVisibleNavButtonColorOverride,
- flags -> (flags & FLAG_SLIDE_IN_VIEW_VISIBLE) != 0));
+ mPropertyHolders.add(new StatePropertyHolder(
+ mSlideInViewVisibleNavButtonColorOverride,
+ flags -> (flags & FLAG_SLIDE_IN_VIEW_VISIBLE) != 0));
+ }
if (alwaysShowButtons) {
initButtons(mNavButtonContainer, mEndContextualContainer,
mControllers.navButtonController);
updateButtonLayoutSpacing();
- updateStateForFlag(FLAG_SMALL_SCREEN, mContext.isPhoneButtonNavMode());
+ updateStateForFlag(FLAG_SMALL_SCREEN, mContext.isPhoneMode());
- mPropertyHolders.add(new StatePropertyHolder(
- mControllers.taskbarDragLayerController.getNavbarBackgroundAlpha(),
- flags -> (flags & FLAG_ONLY_BACK_FOR_BOUNCER_VISIBLE) != 0));
+ if (!mContext.isPhoneMode()) {
+ mPropertyHolders.add(new StatePropertyHolder(
+ mControllers.taskbarDragLayerController.getNavbarBackgroundAlpha(),
+ flags -> (flags & FLAG_ONLY_BACK_FOR_BOUNCER_VISIBLE) != 0));
+ }
} else if (!mIsImeRenderingNavButtons) {
View imeDownButton = addButton(R.drawable.ic_sysbar_back, BUTTON_BACK,
mStartContextualContainer, mControllers.navButtonController, R.id.back);
@@ -711,7 +714,7 @@
private void applyState() {
int count = mPropertyHolders.size();
for (int i = 0; i < count; i++) {
- mPropertyHolders.get(i).setState(mState);
+ mPropertyHolders.get(i).setState(mState, mContext.isGestureNav());
}
}
@@ -1177,83 +1180,6 @@
}
}
- private class RotationButtonImpl implements RotationButton {
-
- private final ImageView mButton;
- private AnimatedVectorDrawable mImageDrawable;
-
- RotationButtonImpl(ImageView button) {
- mButton = button;
- }
-
- @Override
- public void setRotationButtonController(RotationButtonController rotationButtonController) {
- // TODO(b/187754252) UI polish, different icons based on light/dark context, etc
- mImageDrawable = (AnimatedVectorDrawable) mButton.getContext()
- .getDrawable(rotationButtonController.getIconResId());
- mButton.setImageDrawable(mImageDrawable);
- mButton.setContentDescription(mButton.getResources()
- .getString(R.string.accessibility_rotate_button));
- mImageDrawable.setCallback(mButton);
- }
-
- @Override
- public View getCurrentView() {
- return mButton;
- }
-
- @Override
- public boolean show() {
- mButton.setVisibility(View.VISIBLE);
- mState |= FLAG_ROTATION_BUTTON_VISIBLE;
- applyState();
- return true;
- }
-
- @Override
- public boolean hide() {
- mButton.setVisibility(View.GONE);
- mState &= ~FLAG_ROTATION_BUTTON_VISIBLE;
- applyState();
- return true;
- }
-
- @Override
- public boolean isVisible() {
- return mButton.getVisibility() == View.VISIBLE;
- }
-
- @Override
- public void updateIcon(int lightIconColor, int darkIconColor) {
- // TODO(b/187754252): UI Polish
- }
-
- @Override
- public void setOnClickListener(OnClickListener onClickListener) {
- mButton.setOnClickListener(onClickListener);
- }
-
- @Override
- public void setOnHoverListener(OnHoverListener onHoverListener) {
- mButton.setOnHoverListener(onHoverListener);
- }
-
- @Override
- public AnimatedVectorDrawable getImageDrawable() {
- return mImageDrawable;
- }
-
- @Override
- public void setDarkIntensity(float darkIntensity) {
- // TODO(b/187754252) UI polish
- }
-
- @Override
- public boolean acceptRotationProposal() {
- return mButton.isAttachedToWindow();
- }
- }
-
private static class StatePropertyHolder {
private final float mEnabledValue, mDisabledValue;
@@ -1284,13 +1210,16 @@
mAnimator = ObjectAnimator.ofFloat(target, property, enabledValue, disabledValue);
}
- public void setState(int flags) {
+ public void setState(int flags, boolean skipAnimation) {
boolean isEnabled = mEnableCondition.test(flags);
if (mIsEnabled != isEnabled) {
mIsEnabled = isEnabled;
mAnimator.cancel();
mAnimator.setFloatValues(mIsEnabled ? mEnabledValue : mDisabledValue);
mAnimator.start();
+ if (skipAnimation) {
+ mAnimator.end();
+ }
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
index 475b516..ec710c5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
@@ -41,8 +41,8 @@
import com.android.launcher3.util.MultiPropertyFactory;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.quickstep.NavHandle;
-import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
+import com.android.wm.shell.shared.handles.RegionSamplingHelper;
import java.io.PrintWriter;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 487bc54..47ae741 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -29,7 +29,6 @@
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.enableTaskbarCustomization;
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;
@@ -230,15 +229,12 @@
mNavigationBarPanelContext = navigationBarPanelContext;
applyDeviceProfile(launcherDp);
final Resources resources = getResources();
-
- if (enableTaskbarCustomization()) {
- mTaskbarFeatureEvaluator = TaskbarFeatureEvaluator.getInstance(this);
- mTaskbarSpecsEvaluator = new TaskbarSpecsEvaluator(
- this,
- mTaskbarFeatureEvaluator,
- mDeviceProfile.inv.numRows,
- mDeviceProfile.inv.numColumns);
- }
+ mTaskbarFeatureEvaluator = TaskbarFeatureEvaluator.getInstance(this);
+ mTaskbarSpecsEvaluator = new TaskbarSpecsEvaluator(
+ this,
+ mTaskbarFeatureEvaluator,
+ mDeviceProfile.inv.numRows,
+ mDeviceProfile.inv.numColumns);
mImeDrawsImeNavBar = getBoolByName(IME_DRAWS_IME_NAV_BAR_RES_NAME, resources, false);
mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode",
@@ -287,7 +283,7 @@
TaskbarHotseatDimensionsProvider dimensionsProvider =
new DeviceProfileDimensionsProviderAdapter(this);
BubbleStashController bubbleStashController = isTransientTaskbar
- ? new TransientBubbleStashController(dimensionsProvider, getResources())
+ ? new TransientBubbleStashController(dimensionsProvider, this)
: new PersistentBubbleStashController(dimensionsProvider);
bubbleControllersOptional = Optional.of(new BubbleControllers(
new BubbleBarController(this, bubbleBarView),
@@ -348,8 +344,9 @@
new KeyboardQuickSwitchController(),
new TaskbarPinningController(this, () ->
DisplayController.isInDesktopMode(this)),
- bubbleControllersOptional);
-
+ bubbleControllersOptional,
+ new TaskbarDesktopModeController(
+ LauncherActivityInterface.INSTANCE::getDesktopVisibilityController));
mLauncherPrefs = LauncherPrefs.get(this);
}
@@ -1760,12 +1757,10 @@
return mControllers.taskbarStashController.isInStashedLauncherState();
}
- @Nullable
public TaskbarFeatureEvaluator getTaskbarFeatureEvaluator() {
return mTaskbarFeatureEvaluator;
}
- @Nullable
public TaskbarSpecsEvaluator getTaskbarSpecsEvaluator() {
return mTaskbarSpecsEvaluator;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
index ee64060..4ac7514 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
@@ -24,6 +24,7 @@
import android.graphics.Rect
import android.graphics.RectF
import com.android.app.animation.Interpolators
+import com.android.internal.policy.ScreenDecorationsUtils
import com.android.launcher3.R
import com.android.launcher3.Utilities
import com.android.launcher3.Utilities.mapRange
@@ -70,8 +71,8 @@
private var keyShadowDistance = 0f
private var bottomMargin = 0
- private val fullCornerRadius = context.cornerRadius.toFloat()
- private var cornerRadius = fullCornerRadius
+ private val fullCornerRadius: Float
+ private var cornerRadius = 0f
private var widthInsetPercentage = 0f
private val square = Path()
private val circle = Path()
@@ -101,7 +102,14 @@
shadowAlpha = LIGHT_THEME_SHADOW_ALPHA
}
- setCornerRoundness(DEFAULT_ROUNDNESS)
+ if (DisplayController.isInDesktopMode(context)) {
+ fullCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context)
+ cornerRadius = fullCornerRadius
+ } else {
+ fullCornerRadius = context.cornerRadius.toFloat()
+ cornerRadius = fullCornerRadius
+ setCornerRoundness(MAX_ROUNDNESS)
+ }
}
fun updateStashedHandleWidth(context: TaskbarActivityContext, res: Resources) {
@@ -273,7 +281,7 @@
}
companion object {
- const val DEFAULT_ROUNDNESS = 1f
+ const val MAX_ROUNDNESS = 1f
private const val DARK_THEME_STROKE_ALPHA = 51
private const val LIGHT_THEME_STROKE_ALPHA = 41
private const val DARK_THEME_SHADOW_ALPHA = 51f
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
index d94d9175..34ab9f0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
@@ -25,6 +25,7 @@
import com.android.launcher3.taskbar.allapps.TaskbarAllAppsController;
import com.android.launcher3.taskbar.bubbles.BubbleControllers;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayController;
+import com.android.launcher3.util.DisplayController;
import com.android.systemui.shared.rotation.RotationButtonController;
import java.io.PrintWriter;
@@ -64,6 +65,7 @@
public final KeyboardQuickSwitchController keyboardQuickSwitchController;
public final TaskbarPinningController taskbarPinningController;
public final Optional<BubbleControllers> bubbleControllers;
+ public final TaskbarDesktopModeController taskbarDesktopModeController;
@Nullable private LoggableTaskbarController[] mControllersToLog = null;
@Nullable private BackgroundRendererController[] mBackgroundRendererControllers = null;
@@ -111,7 +113,8 @@
TaskbarEduTooltipController taskbarEduTooltipController,
KeyboardQuickSwitchController keyboardQuickSwitchController,
TaskbarPinningController taskbarPinningController,
- Optional<BubbleControllers> bubbleControllers) {
+ Optional<BubbleControllers> bubbleControllers,
+ TaskbarDesktopModeController taskbarDesktopModeController) {
this.taskbarActivityContext = taskbarActivityContext;
this.taskbarDragController = taskbarDragController;
this.navButtonController = navButtonController;
@@ -138,6 +141,7 @@
this.keyboardQuickSwitchController = keyboardQuickSwitchController;
this.taskbarPinningController = taskbarPinningController;
this.bubbleControllers = bubbleControllers;
+ this.taskbarDesktopModeController = taskbarDesktopModeController;
}
/**
@@ -173,6 +177,7 @@
taskbarEduTooltipController.init(this);
keyboardQuickSwitchController.init(this);
taskbarPinningController.init(this, mSharedState);
+ taskbarDesktopModeController.init(this, mSharedState);
mControllersToLog = new LoggableTaskbarController[] {
taskbarDragController, navButtonController, navbarButtonsViewController,
@@ -188,7 +193,13 @@
taskbarDragLayerController, taskbarScrimViewController,
voiceInteractionWindowController
};
- mCornerRoundness.updateValue(TaskbarBackgroundRenderer.DEFAULT_ROUNDNESS);
+
+ if (DisplayController.isInDesktopMode(taskbarActivityContext)) {
+ mCornerRoundness.updateValue(taskbarDesktopModeController.getTaskbarCornerRoundness(
+ mSharedState.showCornerRadiusInDesktopMode));
+ } else {
+ mCornerRoundness.updateValue(TaskbarBackgroundRenderer.MAX_ROUNDNESS);
+ }
mAreAllControllersInitialized = true;
for (Runnable postInitCallback : mPostInitCallbacks) {
@@ -248,6 +259,7 @@
keyboardQuickSwitchController.onDestroy();
taskbarStashController.onDestroy();
bubbleControllers.ifPresent(controllers -> controllers.onDestroy());
+ taskbarDesktopModeController.onDestroy();
mControllersToLog = null;
mBackgroundRendererControllers = null;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDesktopModeController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarDesktopModeController.kt
new file mode 100644
index 0000000..a376531
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDesktopModeController.kt
@@ -0,0 +1,54 @@
+/*
+ * 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 com.android.launcher3.statehandlers.DesktopVisibilityController
+import com.android.launcher3.statehandlers.DesktopVisibilityController.TaskbarDesktopModeListener
+import com.android.launcher3.taskbar.TaskbarBackgroundRenderer.Companion.MAX_ROUNDNESS
+
+/** Handles Taskbar in Desktop Windowing mode. */
+class TaskbarDesktopModeController(
+ private val desktopVisibilityControllerProvider: () -> DesktopVisibilityController?
+) : TaskbarDesktopModeListener {
+ private lateinit var taskbarControllers: TaskbarControllers
+ private lateinit var taskbarSharedState: TaskbarSharedState
+
+ private val desktopVisibilityController: DesktopVisibilityController?
+ get() = desktopVisibilityControllerProvider()
+
+ fun init(controllers: TaskbarControllers, sharedState: TaskbarSharedState) {
+ taskbarControllers = controllers
+ taskbarSharedState = sharedState
+ desktopVisibilityController?.registerTaskbarDesktopModeListener(this)
+ }
+
+ override fun onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding: Boolean) {
+ taskbarSharedState.showCornerRadiusInDesktopMode = doesAnyTaskRequireTaskbarRounding
+ val cornerRadius = getTaskbarCornerRoundness(doesAnyTaskRequireTaskbarRounding)
+ taskbarControllers.taskbarCornerRoundness.animateToValue(cornerRadius).start()
+ }
+
+ fun getTaskbarCornerRoundness(doesAnyTaskRequireTaskbarRounding: Boolean): Float {
+ return if (doesAnyTaskRequireTaskbarRounding) {
+ MAX_ROUNDNESS
+ } else {
+ 0f
+ }
+ }
+
+ fun onDestroy() = desktopVisibilityController?.unregisterTaskbarDesktopModeListener(this)
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
index a635537..b5a3314 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
@@ -169,8 +169,11 @@
override fun addArrow() {
super.addArrow()
+ val location = IntArray(2)
+ popupContainer.getLocationInDragLayer(dividerView, location)
+ val dividerViewX = location[0].toFloat()
// Change arrow location to the middle of popup.
- mArrow.x = (dividerView.x + dividerView.width / 2) - (mArrowWidth / 2)
+ mArrow.x = (dividerViewX + dividerView.width / 2) - (mArrowWidth / 2)
}
override fun updateArrowColor() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
index acf976f..2845cee 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
@@ -171,6 +171,10 @@
}
private void updateBackgroundAlpha() {
+ if (mActivity.isPhoneMode()) {
+ return;
+ }
+
final float bgNavbar = mBgNavbar.value;
final float bgTaskbar = mBgTaskbar.value * mKeyguardBgTaskbar.value
* mNotificationShadeBgTaskbar.value * mImeBgTaskbar.value
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 0eb8890..63fae8c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -48,6 +48,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;
@@ -591,6 +592,12 @@
float cornerRoundness = isInLauncher ? 0 : 1;
+ if (DisplayController.isInDesktopMode(mLauncher) && mControllers.getSharedState() != null) {
+ cornerRoundness =
+ mControllers.taskbarDesktopModeController.getTaskbarCornerRoundness(
+ mControllers.getSharedState().showCornerRadiusInDesktopMode);
+ }
+
// Don't animate if corner roundness has reached desired value.
if (mTaskbarCornerRoundness.isAnimating()
|| mTaskbarCornerRoundness.value != cornerRoundness) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
index 4df0223..2370dfd 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
@@ -21,7 +21,7 @@
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE;
-import static com.android.wm.shell.common.bubbles.BubbleConstants.BUBBLE_EXPANDED_SCRIM_ALPHA;
+import static com.android.wm.shell.shared.bubbles.BubbleConstants.BUBBLE_EXPANDED_SCRIM_ALPHA;
import android.animation.ObjectAnimator;
import android.view.animation.Interpolator;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java
index 77bd35f..729cbe9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java
@@ -102,5 +102,8 @@
// To track if taskbar was stashed / unstashed between configuration changes (which recreates
// the task bar).
- public Boolean taskbarWasStashedAuto = true;
+ public boolean taskbarWasStashedAuto = true;
+
+ // should show corner radius on persistent taskbar when in desktop mode.
+ public boolean showCornerRadiusInDesktopMode = false;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 56f88d1..60e65b3 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -152,12 +152,12 @@
/**
* How long to delay the icon/stash handle alpha.
*/
- private static final long TASKBAR_STASH_ALPHA_START_DELAY = 33;
+ public static final long TASKBAR_STASH_ALPHA_START_DELAY = 33;
/**
* How long the icon/stash handle alpha animation plays.
*/
- private static final long TASKBAR_STASH_ALPHA_DURATION = 50;
+ public static final long TASKBAR_STASH_ALPHA_DURATION = 50;
/**
* How long to delay the icon/stash handle alpha for the home to app taskbar animation.
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index 54a7fdc..32d6561 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -22,10 +22,8 @@
import static com.android.launcher3.Flags.enableRecentsInTaskbar;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_FOLDER;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_ALL_APPS_SEARCH_IN_TASKBAR;
import static com.android.launcher3.config.FeatureFlags.enableTaskbarPinning;
import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import android.content.Context;
import android.content.res.Resources;
@@ -39,12 +37,9 @@
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewConfiguration;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
-import androidx.annotation.DimenRes;
-import androidx.annotation.DrawableRes;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -62,17 +57,16 @@
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.taskbar.customization.TaskbarAllAppsButtonContainer;
+import com.android.launcher3.taskbar.customization.TaskbarDividerContainer;
import com.android.launcher3.util.DisplayController;
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.DeviceConfigWrapper;
-import com.android.quickstep.util.AssistStateManager;
import com.android.quickstep.util.DesktopTask;
import com.android.quickstep.util.GroupTask;
import com.android.systemui.shared.recents.model.Task;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
import java.util.List;
import java.util.function.Predicate;
@@ -100,16 +94,13 @@
private View.OnLongClickListener mIconLongClickListener;
// Only non-null when the corresponding Folder is open.
- private @Nullable FolderIcon mLeaveBehindFolderIcon;
+ @Nullable private FolderIcon mLeaveBehindFolderIcon;
// Only non-null when device supports having an All Apps button.
- private @Nullable IconButtonView mAllAppsButton;
- private Runnable mAllAppsTouchRunnable;
- private long mAllAppsButtonTouchDelayMs;
- private boolean mAllAppsTouchTriggered;
+ @Nullable private final TaskbarAllAppsButtonContainer mAllAppsButtonContainer;
- // Only non-null when device supports having an All Apps button, or Recent Apps.
- private @Nullable IconButtonView mTaskbarDivider;
+ // Only non-null when device supports having an All Apps button.
+ @Nullable private TaskbarDividerContainer mTaskbarDividerContainer;
/**
* Whether the divider is between Hotseat icons and Recents,
@@ -173,55 +164,14 @@
// Needed to draw folder leave-behind when opening one.
setWillNotDraw(false);
- mAllAppsButton = (IconButtonView) LayoutInflater.from(context)
- .inflate(R.layout.taskbar_all_apps_button, this, false);
- mAllAppsButton.setIconDrawable(resources.getDrawable(
- getAllAppsButton(isTransientTaskbar)));
- mAllAppsButton.setPadding(mItemPadding, mItemPadding, mItemPadding, mItemPadding);
- mAllAppsButton.setForegroundTint(
- mActivityContext.getColor(R.color.all_apps_button_color));
+ mAllAppsButtonContainer = new TaskbarAllAppsButtonContainer(context);
if (enableTaskbarPinning() || enableRecentsInTaskbar()) {
- mTaskbarDivider = (IconButtonView) LayoutInflater.from(context).inflate(
- R.layout.taskbar_divider,
- this, false);
- mTaskbarDivider.setIconDrawable(
- resources.getDrawable(R.drawable.taskbar_divider_button));
- mTaskbarDivider.setPadding(mItemPadding, mItemPadding, mItemPadding, mItemPadding);
+ mTaskbarDividerContainer = new TaskbarDividerContainer(context);
}
// TODO: Disable touch events on QSB otherwise it can crash.
mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false);
-
- // Default long press (touch) delay = 400ms
- mAllAppsButtonTouchDelayMs = ViewConfiguration.getLongPressTimeout();
- }
-
- @DrawableRes
- private int getAllAppsButton(boolean isTransientTaskbar) {
- boolean shouldSelectTransientIcon =
- (isTransientTaskbar || enableTaskbarPinning())
- && !mActivityContext.isThreeButtonNav();
- if (ENABLE_ALL_APPS_SEARCH_IN_TASKBAR.get()) {
- return shouldSelectTransientIcon
- ? R.drawable.ic_transient_taskbar_all_apps_search_button
- : R.drawable.ic_taskbar_all_apps_search_button;
- } else {
- return shouldSelectTransientIcon
- ? R.drawable.ic_transient_taskbar_all_apps_button
- : R.drawable.ic_taskbar_all_apps_button;
- }
- }
-
- @DimenRes
- public int getAllAppsButtonTranslationXOffset(boolean isTransientTaskbar) {
- if (isTransientTaskbar) {
- return R.dimen.transient_taskbar_all_apps_button_translation_x_offset;
- } else {
- return ENABLE_ALL_APPS_SEARCH_IN_TASKBAR.get()
- ? R.dimen.taskbar_all_apps_search_button_translation_x_offset
- : R.dimen.taskbar_all_apps_button_translation_x_offset;
- }
}
@Override
@@ -306,27 +256,11 @@
mIconClickListener = mControllerCallbacks.getIconOnClickListener();
mIconLongClickListener = mControllerCallbacks.getIconOnLongClickListener();
- if (mAllAppsButton != null) {
- mAllAppsButton.setOnClickListener(this::onAllAppsButtonClick);
- mAllAppsButton.setOnLongClickListener(this::onAllAppsButtonLongClick);
- mAllAppsButton.setOnTouchListener(this::onAllAppsButtonTouch);
- mAllAppsButton.setHapticFeedbackEnabled(
- mControllerCallbacks.isAllAppsButtonHapticFeedbackEnabled());
- mAllAppsTouchRunnable = () -> {
- mControllerCallbacks.triggerAllAppsButtonLongClick();
- mAllAppsTouchTriggered = true;
- };
- AssistStateManager assistStateManager = AssistStateManager.INSTANCE.get(mContext);
- if (DeviceConfigWrapper.get().getCustomLpaaThresholds()
- && assistStateManager.getLPNHDurationMillis().isPresent()) {
- mAllAppsButtonTouchDelayMs = assistStateManager.getLPNHDurationMillis().get();
- }
+ if (mAllAppsButtonContainer != null) {
+ mAllAppsButtonContainer.setUpCallbacks(callbacks);
}
- if (mTaskbarDivider != null && !mActivityContext.isThreeButtonNav()) {
- mTaskbarDivider.setOnLongClickListener(
- mControllerCallbacks.getTaskbarDividerLongClickListener());
- mTaskbarDivider.setOnTouchListener(
- mControllerCallbacks.getTaskbarDividerRightClickListener());
+ if (mTaskbarDividerContainer != null && callbacks.supportsDividerLongPress()) {
+ mTaskbarDividerContainer.setUpCallbacks(callbacks);
}
}
@@ -348,11 +282,11 @@
int numViewsAnimated = 0;
mAddedDividerForRecents = false;
- if (mAllAppsButton != null) {
- removeView(mAllAppsButton);
+ if (mAllAppsButtonContainer != null) {
+ removeView(mAllAppsButtonContainer);
- if (mTaskbarDivider != null) {
- removeView(mTaskbarDivider);
+ if (mTaskbarDividerContainer != null) {
+ removeView(mTaskbarDividerContainer);
}
}
removeView(mQsb);
@@ -439,8 +373,8 @@
nextViewIndex++;
}
- if (mTaskbarDivider != null && !recentTasks.isEmpty()) {
- addView(mTaskbarDivider, nextViewIndex++);
+ if (mTaskbarDividerContainer != null && !recentTasks.isEmpty()) {
+ addView(mTaskbarDividerContainer, nextViewIndex++);
mAddedDividerForRecents = true;
}
@@ -504,12 +438,14 @@
removeAndRecycle(getChildAt(nextViewIndex));
}
- if (mAllAppsButton != null) {
- addView(mAllAppsButton, mIsRtl ? hotseatItemInfos.length : 0);
+ if (mAllAppsButtonContainer != null) {
+ addView(mAllAppsButtonContainer, mIsRtl ? hotseatItemInfos.length : 0);
// If there are no recent tasks, add divider after All Apps (unless it's the only view).
- if (!mAddedDividerForRecents && mTaskbarDivider != null && getChildCount() > 1) {
- addView(mTaskbarDivider, mIsRtl ? (getChildCount() - 1) : 1);
+ if (!mAddedDividerForRecents
+ && mTaskbarDividerContainer != null
+ && getChildCount() > 1) {
+ addView(mTaskbarDividerContainer, mIsRtl ? (getChildCount() - 1) : 1);
}
}
if (mActivityContext.getDeviceProfile().isQsbInline) {
@@ -637,7 +573,7 @@
int qsbTop = (bottom - top - deviceProfile.hotseatQsbHeight) / 2;
int qsbBottom = qsbTop + deviceProfile.hotseatQsbHeight;
child.layout(qsbStart, qsbTop, qsbEnd, qsbBottom);
- } else if (child == mTaskbarDivider) {
+ } else if (child == mTaskbarDividerContainer) {
iconEnd += mItemMarginLeftRight;
int iconStart = iconEnd - mIconTouchSize;
child.layout(iconStart, mIconLayoutBounds.top, iconEnd, mIconLayoutBounds.bottom);
@@ -727,16 +663,16 @@
* Returns the all apps button in the taskbar.
*/
@Nullable
- public View getAllAppsButtonView() {
- return mAllAppsButton;
+ public TaskbarAllAppsButtonContainer getAllAppsButtonContainer() {
+ return mAllAppsButtonContainer;
}
/**
* Returns the taskbar divider in the taskbar.
*/
@Nullable
- public View getTaskbarDividerView() {
- return mTaskbarDivider;
+ public TaskbarDividerContainer getTaskbarDividerViewContainer() {
+ return mTaskbarDividerContainer;
}
/**
@@ -832,48 +768,6 @@
}
}
}
- return mAllAppsButton;
- }
-
- private boolean onAllAppsButtonTouch(View view, MotionEvent ev) {
- switch (ev.getAction()) {
- case MotionEvent.ACTION_DOWN:
- mAllAppsTouchTriggered = false;
- MAIN_EXECUTOR.getHandler().postDelayed(
- mAllAppsTouchRunnable, mAllAppsButtonTouchDelayMs);
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- cancelAllAppsButtonTouch();
- }
- return false;
- }
-
- private void cancelAllAppsButtonTouch() {
- MAIN_EXECUTOR.getHandler().removeCallbacks(mAllAppsTouchRunnable);
- // ACTION_UP is first triggered, then click listener / long-click listener is triggered on
- // the next frame, so we need to post twice and delay the reset.
- if (mAllAppsButton != null) {
- mAllAppsButton.post(() -> {
- mAllAppsButton.post(() -> {
- mAllAppsTouchTriggered = false;
- });
- });
- }
- }
-
- private void onAllAppsButtonClick(View view) {
- if (!mAllAppsTouchTriggered) {
- mControllerCallbacks.triggerAllAppsButtonClick(view);
- }
- }
-
- // Handle long click from Switch Access and Voice Access
- private boolean onAllAppsButtonLongClick(View view) {
- if (!MAIN_EXECUTOR.getHandler().hasCallbacks(mAllAppsTouchRunnable)
- && !mAllAppsTouchTriggered) {
- mControllerCallbacks.triggerAllAppsButtonLongClick();
- }
- return true;
+ return mAllAppsButtonContainer;
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java
index e6cac2f..5ec00ac 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java
@@ -28,7 +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.common.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
/**
* Callbacks for {@link TaskbarView} to interact with its controller.
@@ -51,7 +51,7 @@
}
/** Trigger All Apps button click action. */
- protected void triggerAllAppsButtonClick(View v) {
+ public void triggerAllAppsButtonClick(View v) {
InteractionJankMonitorWrapper.begin(v, Cuj.CUJ_LAUNCHER_OPEN_ALL_APPS,
/* tag= */ "TASKBAR_BUTTON");
mActivity.getStatsLogManager().logger().log(LAUNCHER_TASKBAR_ALLAPPS_BUTTON_TAP);
@@ -59,7 +59,7 @@
}
/** Trigger All Apps button long click action. */
- protected void triggerAllAppsButtonLongClick() {
+ public void triggerAllAppsButtonLongClick() {
mActivity.getStatsLogManager().logger().log(LAUNCHER_TASKBAR_ALLAPPS_BUTTON_LONG_PRESS);
}
@@ -74,6 +74,11 @@
};
}
+ /** Check to see if we support long press on taskbar divider */
+ public boolean supportsDividerLongPress() {
+ return !mActivity.isThreeButtonNav();
+ }
+
public View.OnTouchListener getTaskbarDividerRightClickListener() {
return (v, event) -> {
if (event.isFromSource(InputDevice.SOURCE_MOUSE)
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index aef21aa..aa3e6bf 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -72,7 +72,6 @@
import com.android.launcher3.util.MultiPropertyFactory;
import com.android.launcher3.util.MultiTranslateDelegate;
import com.android.launcher3.util.MultiValueAlpha;
-import com.android.launcher3.views.IconButtonView;
import com.android.quickstep.util.GroupTask;
import com.android.systemui.shared.recents.model.Task;
@@ -219,8 +218,8 @@
if (ENABLE_TASKBAR_NAVBAR_UNIFICATION) {
// This gets modified in NavbarButtonsViewController, but the initial value it reads
// may be incorrect since it's state gets destroyed on taskbar recreate, so reset here
- mTaskbarIconAlpha.get(ALPHA_INDEX_SMALL_SCREEN)
- .animateToValue(mActivity.isPhoneButtonNavMode() ? 0 : 1).start();
+ mTaskbarIconAlpha.get(ALPHA_INDEX_SMALL_SCREEN).setValue(
+ mActivity.isPhoneMode() ? 0 : 1);
}
if (enableTaskbarPinning()) {
mTaskbarView.addOnLayoutChangeListener(mTaskbarViewLayoutChangeListener);
@@ -299,7 +298,7 @@
@Nullable
public View getAllAppsButtonView() {
- return mTaskbarView.getAllAppsButtonView();
+ return mTaskbarView.getAllAppsButtonContainer();
}
public AnimatedFloat getTaskbarIconScaleForStash() {
@@ -366,9 +365,9 @@
View[] iconViews = mTaskbarView.getIconViews();
float scale = mTaskbarIconTranslationXForPinning.value;
float transientTaskbarAllAppsOffset = mActivity.getResources().getDimension(
- mTaskbarView.getAllAppsButtonTranslationXOffset(true));
+ mTaskbarView.getAllAppsButtonContainer().getAllAppsButtonTranslationXOffset(true));
float persistentTaskbarAllAppsOffset = mActivity.getResources().getDimension(
- mTaskbarView.getAllAppsButtonTranslationXOffset(false));
+ mTaskbarView.getAllAppsButtonContainer().getAllAppsButtonTranslationXOffset(false));
float allAppIconTranslateRange = mapRange(scale, transientTaskbarAllAppsOffset,
persistentTaskbarAllAppsOffset);
@@ -383,7 +382,7 @@
}
if (mActivity.isThreeButtonNav()) {
- ((IconButtonView) mTaskbarView.getAllAppsButtonView())
+ mTaskbarView.getAllAppsButtonContainer()
.setTranslationXForTaskbarAllAppsIcon(allAppIconTranslateRange);
return;
}
@@ -408,8 +407,8 @@
-finalMarginScale * (iconIndex - halfIconCount));
}
- if (iconView.equals(mTaskbarView.getAllAppsButtonView())) {
- ((IconButtonView) iconView).setTranslationXForTaskbarAllAppsIcon(
+ if (iconView.equals(mTaskbarView.getAllAppsButtonContainer())) {
+ mTaskbarView.getAllAppsButtonContainer().setTranslationXForTaskbarAllAppsIcon(
allAppIconTranslateRange);
}
}
@@ -537,7 +536,7 @@
}
public View getTaskbarDividerView() {
- return mTaskbarView.getTaskbarDividerView();
+ return mTaskbarView.getTaskbarDividerViewContainer();
}
/**
@@ -753,8 +752,8 @@
int firstRecentTaskIndex = -1;
for (int i = 0; i < mTaskbarView.getChildCount(); i++) {
View child = mTaskbarView.getChildAt(i);
- boolean isAllAppsButton = child == mTaskbarView.getAllAppsButtonView();
- boolean isTaskbarDividerView = child == mTaskbarView.getTaskbarDividerView();
+ boolean isAllAppsButton = child == mTaskbarView.getAllAppsButtonContainer();
+ boolean isTaskbarDividerView = child == mTaskbarView.getTaskbarDividerViewContainer();
boolean isRecentTask = child.getTag() instanceof GroupTask;
// TODO(b/343522351): show recents on the home screen.
final boolean isRecentsInHotseat = false;
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
index 90ac872..f5ac66f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
@@ -16,7 +16,6 @@
package com.android.launcher3.taskbar.allapps;
import static com.android.app.animation.Interpolators.EMPHASIZED;
-import static com.android.launcher3.Flags.enablePredictiveBackGesture;
import static com.android.launcher3.touch.AllAppsSwipeController.ALL_APPS_FADE_MANUAL;
import static com.android.launcher3.touch.AllAppsSwipeController.SCRIM_FADE_MANUAL;
@@ -193,14 +192,12 @@
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mActivityContext.addOnDeviceProfileChangeListener(this);
- if (enablePredictiveBackGesture()) {
- mAppsView.getAppsRecyclerViewContainer().setOutlineProvider(mViewOutlineProvider);
- mAppsView.getAppsRecyclerViewContainer().setClipToOutline(true);
- OnBackInvokedDispatcher dispatcher = findOnBackInvokedDispatcher();
- if (dispatcher != null) {
- dispatcher.registerOnBackInvokedCallback(
- OnBackInvokedDispatcher.PRIORITY_DEFAULT, this);
- }
+ mAppsView.getAppsRecyclerViewContainer().setOutlineProvider(mViewOutlineProvider);
+ mAppsView.getAppsRecyclerViewContainer().setClipToOutline(true);
+ OnBackInvokedDispatcher dispatcher = findOnBackInvokedDispatcher();
+ if (dispatcher != null) {
+ dispatcher.registerOnBackInvokedCallback(
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT, this);
}
}
@@ -208,13 +205,11 @@
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mActivityContext.removeOnDeviceProfileChangeListener(this);
- if (enablePredictiveBackGesture()) {
- mAppsView.getAppsRecyclerViewContainer().setOutlineProvider(null);
- mAppsView.getAppsRecyclerViewContainer().setClipToOutline(false);
- OnBackInvokedDispatcher dispatcher = findOnBackInvokedDispatcher();
- if (dispatcher != null) {
- dispatcher.unregisterOnBackInvokedCallback(this);
- }
+ mAppsView.getAppsRecyclerViewContainer().setOutlineProvider(null);
+ mAppsView.getAppsRecyclerViewContainer().setClipToOutline(false);
+ OnBackInvokedDispatcher dispatcher = findOnBackInvokedDispatcher();
+ if (dispatcher != null) {
+ dispatcher.unregisterOnBackInvokedCallback(this);
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
index cdd3e13..d70a317 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
@@ -41,10 +41,10 @@
import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
import com.android.wm.shell.Flags;
import com.android.wm.shell.bubbles.IBubblesListener;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
-import com.android.wm.shell.common.bubbles.BubbleBarUpdate;
-import com.android.wm.shell.common.bubbles.BubbleInfo;
-import com.android.wm.shell.common.bubbles.RemovedBubble;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.BubbleBarUpdate;
+import com.android.wm.shell.shared.bubbles.BubbleInfo;
+import com.android.wm.shell.shared.bubbles.RemovedBubble;
import java.util.ArrayList;
import java.util.List;
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarItem.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarItem.kt
index 39d1ed7..7a32ef1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarItem.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarItem.kt
@@ -17,7 +17,7 @@
import android.graphics.Bitmap
import android.graphics.Path
-import com.android.wm.shell.common.bubbles.BubbleInfo
+import com.android.wm.shell.shared.bubbles.BubbleInfo
/** An entity in the bubble bar. */
sealed class BubbleBarItem(open var key: String, open var view: BubbleView)
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarPinController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarPinController.kt
index a6b0860..9c34307 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarPinController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarPinController.kt
@@ -28,8 +28,8 @@
import androidx.core.view.updateLayoutParams
import com.android.launcher3.R
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController
-import com.android.wm.shell.common.bubbles.BaseBubblePinController
-import com.android.wm.shell.common.bubbles.BubbleBarLocation
+import com.android.wm.shell.shared.bubbles.BaseBubblePinController
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation
/**
* Controller to manage pinning bubble bar to left or right when dragging starts from the bubble bar
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
index c458936..06301c7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
@@ -48,7 +48,7 @@
import com.android.launcher3.anim.SpringAnimationBuilder;
import com.android.launcher3.taskbar.bubbles.animation.BubbleAnimator;
import com.android.launcher3.util.DisplayController;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -1303,7 +1303,10 @@
return totalIconSize + totalSpace + horizontalPadding;
}
- private float collapsedWidth() {
+ /**
+ * Get width of the bubble bar if it is collapsed
+ */
+ float collapsedWidth() {
final int bubbleChildCount = getBubbleChildCount();
final float horizontalPadding = 2 * mBubbleBarPadding;
// If there are more than 2 bubbles, the first 2 should be visible when collapsed,
@@ -1473,8 +1476,9 @@
pw.println("BubbleBarView state:");
pw.println(" visibility: " + getVisibility());
pw.println(" alpha: " + getAlpha());
- pw.println(" translation Y: " + getTranslationY());
- pw.println(" bubbles in bar (childCount = " + getChildCount() + ")");
+ pw.println(" translationY: " + getTranslationY());
+ pw.println(" childCount: " + getChildCount());
+ pw.println(" hasOverflow: " + hasOverflow());
for (BubbleView bubbleView: getBubbles()) {
BubbleBarItem bubble = bubbleView.getBubble();
String key = bubble == null ? "null" : bubble.getKey();
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index 5c1a546..d9e3406 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -43,7 +43,7 @@
import com.android.launcher3.util.MultiPropertyFactory;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.quickstep.SystemUiProxy;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
import java.io.PrintWriter;
import java.util.List;
@@ -80,12 +80,15 @@
// These are exposed to {@link BubbleStashController} to animate for stashing/un-stashing
private final MultiValueAlpha mBubbleBarAlpha;
- private final AnimatedFloat mBubbleBarScale = new AnimatedFloat(this::updateScale);
+ private final AnimatedFloat mBubbleBarScaleX = new AnimatedFloat(this::updateScaleX);
+ private final AnimatedFloat mBubbleBarScaleY = new AnimatedFloat(this::updateScaleY);
private final AnimatedFloat mBubbleBarTranslationY = new AnimatedFloat(
this::updateTranslationY);
// Modified when swipe up is happening on the bubble bar or task bar.
private float mBubbleBarSwipeUpTranslationY;
+ // Modified when bubble bar is springing back into the stash handle.
+ private float mBubbleBarStashTranslationY;
// Whether the bar is hidden for a sysui state.
private boolean mHiddenForSysui;
@@ -125,7 +128,7 @@
onBubbleBarConfigurationChanged(/* animate= */ false);
mActivity.addOnDeviceProfileChangeListener(
dp -> onBubbleBarConfigurationChanged(/* animate= */ true));
- mBubbleBarScale.updateValue(1f);
+ mBubbleBarScaleY.updateValue(1f);
mBubbleClickListener = v -> onBubbleClicked((BubbleView) v);
mBubbleBarClickListener = v -> expandBubbleBar();
mBubbleDragController.setupBubbleBarView(mBarView);
@@ -255,19 +258,48 @@
return mBubbleBarAlpha;
}
- public AnimatedFloat getBubbleBarScale() {
- return mBubbleBarScale;
+ public AnimatedFloat getBubbleBarScaleX() {
+ return mBubbleBarScaleX;
+ }
+
+ public AnimatedFloat getBubbleBarScaleY() {
+ return mBubbleBarScaleY;
}
public AnimatedFloat getBubbleBarTranslationY() {
return mBubbleBarTranslationY;
}
+ public float getBubbleBarCollapsedWidth() {
+ return mBarView.collapsedWidth();
+ }
+
public float getBubbleBarCollapsedHeight() {
return mBarView.getBubbleBarCollapsedHeight();
}
/**
+ * @see BubbleBarView#getRelativePivotX()
+ */
+ public float getBubbleBarRelativePivotX() {
+ return mBarView.getRelativePivotX();
+ }
+
+ /**
+ * @see BubbleBarView#getRelativePivotY()
+ */
+ public float getBubbleBarRelativePivotY() {
+ return mBarView.getRelativePivotY();
+ }
+
+ /**
+ * @see BubbleBarView#setRelativePivot(float, float)
+ */
+ public void setBubbleBarRelativePivot(float x, float y) {
+ mBarView.setRelativePivot(x, y);
+ }
+
+ /**
* Whether the bubble bar is visible or not.
*/
public boolean isBubbleBarVisible() {
@@ -287,6 +319,14 @@
}
/**
+ * @return {@code true} if bubble bar is on the left edge of the screen, {@code false} if on
+ * the right
+ */
+ public boolean isBubbleBarOnLeft() {
+ return mBarView.getBubbleBarLocation().isOnLeft(mBarView.isLayoutRtl());
+ }
+
+ /**
* Update bar {@link BubbleBarLocation}
*/
public void setBubbleBarLocation(BubbleBarLocation bubbleBarLocation) {
@@ -474,17 +514,24 @@
updateTranslationY();
}
- private void updateTranslationY() {
- mBarView.setTranslationY(mBubbleBarTranslationY.value
- + mBubbleBarSwipeUpTranslationY);
+ /**
+ * Sets the translation of the bubble bar during the stash animation.
+ */
+ public void setTranslationYForStash(float transY) {
+ mBubbleBarStashTranslationY = transY;
+ updateTranslationY();
}
- /**
- * Applies scale properties for the entire bubble bar.
- */
- private void updateScale() {
- float scale = mBubbleBarScale.value;
+ private void updateTranslationY() {
+ mBarView.setTranslationY(mBubbleBarTranslationY.value + mBubbleBarSwipeUpTranslationY
+ + mBubbleBarStashTranslationY);
+ }
+
+ private void updateScaleX(float scale) {
mBarView.setScaleX(scale);
+ }
+
+ private void updateScaleY(float scale) {
mBarView.setScaleY(scale);
}
@@ -793,6 +840,7 @@
pw.println(" mShouldShowEducation: " + mShouldShowEducation);
pw.println(" mBubbleBarTranslationY.value: " + mBubbleBarTranslationY.value);
pw.println(" mBubbleBarSwipeUpTranslationY: " + mBubbleBarSwipeUpTranslationY);
+ pw.println(" mOverflowAdded: " + mOverflowAdded);
if (mBarView != null) {
mBarView.dump(pw);
} else {
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleCreator.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleCreator.java
index 8e9a2f6..12b1487 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleCreator.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleCreator.java
@@ -51,7 +51,7 @@
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.BubbleIconFactory;
import com.android.launcher3.shortcuts.ShortcutRequest;
-import com.android.wm.shell.common.bubbles.BubbleInfo;
+import com.android.wm.shell.shared.bubbles.BubbleInfo;
/**
* Loads the necessary info to populate / present a bubble (name, icon, shortcut).
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissController.java
index f554fcd..a459dd9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissController.java
@@ -29,7 +29,7 @@
import com.android.launcher3.R;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarDragLayer;
-import com.android.wm.shell.common.bubbles.DismissView;
+import com.android.wm.shell.shared.bubbles.DismissView;
import com.android.wm.shell.shared.magnetictarget.MagnetizedObject;
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissViewExt.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissViewExt.kt
index 6c3f0d8..a8002a5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissViewExt.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissViewExt.kt
@@ -18,7 +18,7 @@
package com.android.launcher3.taskbar.bubbles
import com.android.launcher3.R
-import com.android.wm.shell.common.bubbles.DismissView
+import com.android.wm.shell.shared.bubbles.DismissView
/**
* Dismiss view is shared from WMShell. It requires setup with local resources.
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java
index 87f466f..adaba7a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java
@@ -29,8 +29,8 @@
import androidx.dynamicanimation.animation.FloatPropertyCompat;
import com.android.launcher3.R;
-import com.android.wm.shell.common.bubbles.DismissCircleView;
-import com.android.wm.shell.common.bubbles.DismissView;
+import com.android.wm.shell.shared.bubbles.DismissCircleView;
+import com.android.wm.shell.shared.bubbles.DismissView;
import com.android.wm.shell.shared.animation.PhysicsAnimator;
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
index 54b883c..42bd197 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
@@ -27,8 +27,8 @@
import androidx.dynamicanimation.animation.FloatPropertyCompat;
import com.android.launcher3.taskbar.TaskbarActivityContext;
-import com.android.wm.shell.common.bubbles.BaseBubblePinController.LocationChangeListener;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.BaseBubblePinController.LocationChangeListener;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
/**
* Controls bubble bar drag interactions.
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubblePinController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubblePinController.kt
index 1341b53..af1666f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubblePinController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubblePinController.kt
@@ -28,8 +28,8 @@
import androidx.core.view.updateLayoutParams
import com.android.launcher3.R
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController
-import com.android.wm.shell.common.bubbles.BaseBubblePinController
-import com.android.wm.shell.common.bubbles.BubbleBarLocation
+import com.android.wm.shell.shared.bubbles.BaseBubblePinController
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation
/** Controller to manage pinning bubble bar to left or right when dragging starts from a bubble */
class BubblePinController(
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java
index 6bfe8f4..fdd385a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java
@@ -21,6 +21,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
+import android.annotation.Nullable;
import android.content.res.Resources;
import android.graphics.Outline;
import android.graphics.Rect;
@@ -28,6 +29,7 @@
import android.view.View;
import android.view.ViewOutlineProvider;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.anim.RevealOutlineAnimation;
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
@@ -37,9 +39,9 @@
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.MultiPropertyFactory;
import com.android.launcher3.util.MultiValueAlpha;
-import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
import com.android.wm.shell.shared.animation.PhysicsAnimator;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.handles.RegionSamplingHelper;
/**
* Handles properties/data collection, then passes the results to our stashed handle View to render.
@@ -49,23 +51,29 @@
private final TaskbarActivityContext mActivity;
private final StashedHandleView mStashedHandleView;
private final MultiValueAlpha mStashedHandleAlpha;
+ private float mTranslationForSwipeY;
+ private float mTranslationForStashY;
// Initialized in init.
private BubbleBarViewController mBarViewController;
private BubbleStashController mBubbleStashController;
private RegionSamplingHelper mRegionSamplingHelper;
- private int mBarSize;
- private int mStashedTaskbarHeight;
+ // Height of the area for the stash handle. Handle will be drawn in the center of this.
+ // This is also the area where touch is handled on the handle.
+ private int mStashedBubbleBarHeight;
private int mStashedHandleWidth;
private int mStashedHandleHeight;
- // The bounds we want to clip to in the settled state when showing the stashed handle.
+ // The bounds of the stashed handle in settled state.
private final Rect mStashedHandleBounds = new Rect();
+ private float mStashedHandleRadius;
// When the reveal animation is cancelled, we can assume it's about to create a new animation,
// which should start off at the same point the cancelled one left off.
private float mStartProgressForNextRevealAnim;
- private boolean mWasLastRevealAnimReversed;
+ // Use a nullable boolean to handle initial case where the last animation direction is not known
+ @Nullable
+ private Boolean mWasLastRevealAnimReversed = null;
// XXX: if there are more of these maybe do state flags instead
private boolean mHiddenForSysui;
@@ -77,6 +85,7 @@
mActivity = activity;
mStashedHandleView = stashedHandleView;
mStashedHandleAlpha = new MultiValueAlpha(mStashedHandleView, 1);
+ mStashedHandleAlpha.setUpdateVisibility(true);
}
/** Initialize controller. */
@@ -84,26 +93,31 @@
mBarViewController = bubbleControllers.bubbleBarViewController;
mBubbleStashController = bubbleControllers.bubbleStashController;
+ DeviceProfile deviceProfile = mActivity.getDeviceProfile();
Resources resources = mActivity.getResources();
mStashedHandleHeight = resources.getDimensionPixelSize(
R.dimen.bubblebar_stashed_handle_height);
mStashedHandleWidth = resources.getDimensionPixelSize(
R.dimen.bubblebar_stashed_handle_width);
- mBarSize = resources.getDimensionPixelSize(R.dimen.bubblebar_size);
- final int bottomMargin = resources.getDimensionPixelSize(
- R.dimen.transient_taskbar_bottom_margin);
- mStashedHandleView.getLayoutParams().height = mBarSize + bottomMargin;
+ int barSize = resources.getDimensionPixelSize(R.dimen.bubblebar_size);
+ // Use the max translation for bubble bar whether it is on the home screen or in app.
+ // Use values directly from device profile to avoid referencing other bubble controllers
+ // during init flow.
+ int maxTy = Math.max(deviceProfile.hotseatBarBottomSpacePx,
+ deviceProfile.taskbarBottomMargin);
+ // Adjust handle view size to accommodate the handle morphing into the bubble bar
+ mStashedHandleView.getLayoutParams().height = barSize + maxTy;
mStashedHandleAlpha.get(0).setValue(0);
- mStashedTaskbarHeight = resources.getDimensionPixelSize(
+ mStashedBubbleBarHeight = resources.getDimensionPixelSize(
R.dimen.bubblebar_stashed_size);
mStashedHandleView.setOutlineProvider(new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
- float stashedHandleRadius = view.getHeight() / 2f;
- outline.setRoundRect(mStashedHandleBounds, stashedHandleRadius);
+ mStashedHandleRadius = view.getHeight() / 2f;
+ outline.setRoundRect(mStashedHandleBounds, mStashedHandleRadius);
}
});
@@ -132,28 +146,25 @@
private void updateBounds(BubbleBarLocation bubbleBarLocation) {
// As more bubbles get added, the icon bounds become larger. To ensure a consistent
// handle bar position, we pin it to the edge of the screen.
- final int stashedCenterY = mStashedHandleView.getHeight() - mStashedTaskbarHeight / 2;
+ final int stashedCenterY = mStashedHandleView.getHeight() - mStashedBubbleBarHeight / 2;
+ final int stashedCenterX;
if (bubbleBarLocation.isOnLeft(mStashedHandleView.isLayoutRtl())) {
final int left = mBarViewController.getHorizontalMargin();
- mStashedHandleBounds.set(
- left,
- stashedCenterY - mStashedHandleHeight / 2,
- left + mStashedHandleWidth,
- stashedCenterY + mStashedHandleHeight / 2);
- mStashedHandleView.setPivotX(0);
+ stashedCenterX = left + mStashedHandleWidth / 2;
} else {
final int right =
- mActivity.getDeviceProfile().widthPx - mBarViewController.getHorizontalMargin();
- mStashedHandleBounds.set(
- right - mStashedHandleWidth,
- stashedCenterY - mStashedHandleHeight / 2,
- right,
- stashedCenterY + mStashedHandleHeight / 2);
- mStashedHandleView.setPivotX(mStashedHandleView.getWidth());
+ mStashedHandleView.getRight() - mBarViewController.getHorizontalMargin();
+ stashedCenterX = right - mStashedHandleWidth / 2;
}
-
+ mStashedHandleBounds.set(
+ stashedCenterX - mStashedHandleWidth / 2,
+ stashedCenterY - mStashedHandleHeight / 2,
+ stashedCenterX + mStashedHandleWidth / 2,
+ stashedCenterY + mStashedHandleHeight / 2
+ );
mStashedHandleView.updateSampledRegion(mStashedHandleBounds);
- mStashedHandleView.setPivotY(mStashedHandleView.getHeight() - mStashedTaskbarHeight / 2f);
+ mStashedHandleView.setPivotX(stashedCenterX);
+ mStashedHandleView.setPivotY(stashedCenterY);
}
public void onDestroy() {
@@ -162,6 +173,13 @@
}
/**
+ * Returns the width of the stashed handle.
+ */
+ public int getStashedWidth() {
+ return mStashedHandleWidth;
+ }
+
+ /**
* Returns the height of the stashed handle.
*/
public int getStashedHeight() {
@@ -169,13 +187,6 @@
}
/**
- * Returns the height when the bubble bar is unstashed (so the height of the bubble bar).
- */
- public int getUnstashedHeight() {
- return mBarSize;
- }
-
- /**
* Called when system ui state changes. Bubbles don't show when the device is locked.
*/
public void setHiddenForSysui(boolean hidden) {
@@ -242,7 +253,20 @@
* Sets the translation of the stashed handle during the swipe up gesture.
*/
public void setTranslationYForSwipe(float transY) {
- mStashedHandleView.setTranslationY(transY);
+ mTranslationForSwipeY = transY;
+ updateTranslationY();
+ }
+
+ /**
+ * Sets the translation of the stashed handle during the spring on stash animation.
+ */
+ public void setTranslationYForStash(float transY) {
+ mTranslationForStashY = transY;
+ updateTranslationY();
+ }
+
+ private void updateTranslationY() {
+ mStashedHandleView.setTranslationY(mTranslationForSwipeY + mTranslationForStashY);
}
/** Returns the translation of the stashed handle. */
@@ -263,18 +287,17 @@
* the size of where the bubble bar icons will be.
*/
public Animator createRevealAnimToIsStashed(boolean isStashed) {
- Rect bubbleBarBounds = new Rect(mBarViewController.getBubbleBarBounds());
+ Rect bubbleBarBounds = getLocalBubbleBarBounds();
- // Account for the full visual height of the bubble bar
- int heightDiff = (mBarSize - bubbleBarBounds.height()) / 2;
- bubbleBarBounds.top -= heightDiff;
- bubbleBarBounds.bottom += heightDiff;
- float stashedHandleRadius = mStashedHandleView.getHeight() / 2f;
+ float bubbleBarRadius = bubbleBarBounds.height() / 2f;
final RevealOutlineAnimation handleRevealProvider = new RoundedRectRevealOutlineProvider(
- stashedHandleRadius, stashedHandleRadius, bubbleBarBounds, mStashedHandleBounds);
+ bubbleBarRadius, mStashedHandleRadius, bubbleBarBounds, mStashedHandleBounds);
boolean isReversed = !isStashed;
- boolean changingDirection = mWasLastRevealAnimReversed != isReversed;
+ // We are only changing direction when mWasLastRevealAnimReversed is set at least once
+ boolean changingDirection =
+ mWasLastRevealAnimReversed != null && mWasLastRevealAnimReversed != isReversed;
+
mWasLastRevealAnimReversed = isReversed;
if (changingDirection) {
mStartProgressForNextRevealAnim = 1f - mStartProgressForNextRevealAnim;
@@ -291,6 +314,21 @@
return revealAnim;
}
+ /**
+ * Get bounds for the bubble bar in the space of the handle view
+ */
+ private Rect getLocalBubbleBarBounds() {
+ // Position the bubble bar bounds to the space of handle view
+ Rect bubbleBarBounds = new Rect(mBarViewController.getBubbleBarBounds());
+ // Start by moving bubble bar bounds to the bottom of handle view
+ int height = bubbleBarBounds.height();
+ bubbleBarBounds.bottom = mStashedHandleView.getHeight();
+ bubbleBarBounds.top = bubbleBarBounds.bottom - height;
+ // Then apply translation that is applied to the bubble bar
+ bubbleBarBounds.offset(0, (int) mBubbleStashController.getBubbleBarTranslationY());
+ return bubbleBarBounds;
+ }
+
/** Checks that the stash handle is visible and that the motion event is within bounds. */
public boolean isEventOverHandle(MotionEvent ev) {
if (mStashedHandleView.getVisibility() != VISIBLE) {
@@ -299,7 +337,7 @@
// the bounds of the handle only include the visible part, so we check that the Y coordinate
// is anywhere within the stashed height of bubble bar (same as taskbar stashed height).
- final int top = mActivity.getDeviceProfile().heightPx - mStashedTaskbarHeight;
+ final int top = mActivity.getDeviceProfile().heightPx - mStashedBubbleBarHeight;
final float x = ev.getRawX();
return ev.getRawY() >= top && x >= mStashedHandleBounds.left
&& x <= mStashedHandleBounds.right;
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
index eb3b24b..591a9da 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
@@ -34,9 +34,9 @@
import com.android.launcher3.R;
import com.android.launcher3.icons.DotRenderer;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
-import com.android.wm.shell.common.bubbles.BubbleInfo;
import com.android.wm.shell.shared.animation.Interpolators;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.BubbleInfo;
// TODO: (b/276978250) This is will be similar to WMShell's BadgedImageView, it'd be nice to share.
@@ -237,7 +237,11 @@
mBubble = bubble;
mIcon = bubble.getIcon();
updateBubbleIcon();
- mAppIcon.setImageBitmap(bubble.getBadge());
+ if (bubble.getInfo().showAppBadge()) {
+ mAppIcon.setImageBitmap(bubble.getBadge());
+ } else {
+ mAppIcon.setVisibility(GONE);
+ }
mDotColor = bubble.getDotColor();
mDotRenderer = new DotRenderer(mBubbleSize, bubble.getDotPath(), DEFAULT_PATH_SIZE);
String contentDesc = bubble.getInfo().getTitle();
@@ -302,8 +306,10 @@
}
void setBadgeScale(float fraction) {
- mAppIcon.setScaleX(fraction);
- mAppIcon.setScaleY(fraction);
+ if (mAppIcon.getVisibility() == VISIBLE) {
+ mAppIcon.setScaleX(fraction);
+ mAppIcon.setScaleY(fraction);
+ }
}
boolean hasUnseenContent() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt
index 48eb7de..8d63217 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt
@@ -23,8 +23,8 @@
import com.android.launcher3.taskbar.bubbles.BubbleBarView
import com.android.launcher3.taskbar.bubbles.BubbleBarViewController
import com.android.launcher3.taskbar.bubbles.BubbleStashedHandleViewController
-import com.android.wm.shell.common.bubbles.BubbleBarLocation
import com.android.wm.shell.shared.animation.PhysicsAnimator
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation
import java.io.PrintWriter
/** StashController that defines stashing behaviour for the taskbar modes. */
@@ -181,8 +181,5 @@
/** How long to translate Y coordinate of the BubbleBar. */
const val BAR_TRANSLATION_DURATION = 300L
-
- /** The scale bubble bar animates to when being stashed. */
- const val STASHED_BAR_SCALE = 0.5f
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashController.kt
index 1b65019..eaf4bf9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashController.kt
@@ -30,8 +30,8 @@
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.ControllersAfterInitAction
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.TaskbarHotseatDimensionsProvider
import com.android.launcher3.util.MultiPropertyFactory
-import com.android.wm.shell.common.bubbles.BubbleBarLocation
import com.android.wm.shell.shared.animation.PhysicsAnimator
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation
class PersistentBubbleStashController(
private val taskbarHotseatDimensionsProvider: TaskbarHotseatDimensionsProvider,
@@ -116,7 +116,7 @@
bubbleBarTranslationYAnimator = bubbleBarViewController.bubbleBarTranslationY
// bubble bar has only alpha property, getting it at index 0
bubbleBarAlphaAnimator = bubbleBarViewController.bubbleBarAlpha.get(/* index= */ 0)
- bubbleBarScaleAnimator = bubbleBarViewController.bubbleBarScale
+ bubbleBarScaleAnimator = bubbleBarViewController.bubbleBarScaleY
}
private fun animateAfterUnlock() {
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 1a4b982..1157305 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt
@@ -17,30 +17,36 @@
package com.android.launcher3.taskbar.bubbles.stashing
import android.animation.Animator
-import android.animation.AnimatorListenerAdapter
import android.animation.AnimatorSet
-import android.content.res.Resources
+import android.content.Context
import android.view.MotionEvent
import android.view.View
import androidx.annotation.VisibleForTesting
+import androidx.core.animation.doOnEnd
+import androidx.core.animation.doOnStart
+import androidx.dynamicanimation.animation.SpringForce
+import com.android.app.animation.Interpolators.EMPHASIZED
+import com.android.app.animation.Interpolators.LINEAR
import com.android.launcher3.R
import com.android.launcher3.anim.AnimatedFloat
-import com.android.launcher3.taskbar.StashedHandleViewController
+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.bubbles.BubbleBarViewController
import com.android.launcher3.taskbar.bubbles.BubbleStashedHandleViewController
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.Companion.BAR_STASH_DURATION
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.Companion.BAR_TRANSLATION_DURATION
-import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.Companion.STASHED_BAR_SCALE
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.ControllersAfterInitAction
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.TaskbarHotseatDimensionsProvider
import com.android.launcher3.util.MultiPropertyFactory
-import com.android.wm.shell.common.bubbles.BubbleBarLocation
import com.android.wm.shell.shared.animation.PhysicsAnimator
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation
+import kotlin.math.max
class TransientBubbleStashController(
private val taskbarHotseatDimensionsProvider: TaskbarHotseatDimensionsProvider,
- resources: Resources
+ private val context: Context
) : BubbleStashController {
private lateinit var bubbleBarViewController: BubbleBarViewController
@@ -50,14 +56,21 @@
// stash view properties
private var bubbleStashedHandleViewController: BubbleStashedHandleViewController? = null
private var stashHandleViewAlpha: MultiPropertyFactory<View>.MultiProperty? = null
+ private var translationYDuringStash = AnimatedFloat { transY ->
+ bubbleStashedHandleViewController?.setTranslationYForStash(transY)
+ bubbleBarViewController.setTranslationYForStash(transY)
+ }
+ private val stashHandleStashVelocity =
+ context.resources.getDimension(R.dimen.bubblebar_stashed_handle_spring_velocity_dp_per_s)
private var stashedHeight: Int = 0
// bubble bar properties
private lateinit var bubbleBarAlpha: MultiPropertyFactory<View>.MultiProperty
private lateinit var bubbleBarTranslationYAnimator: AnimatedFloat
- private lateinit var bubbleBarScale: AnimatedFloat
- private val mHandleCenterFromScreenBottom =
- resources.getDimensionPixelSize(R.dimen.bubblebar_stashed_size) / 2f
+ private lateinit var bubbleBarScaleX: AnimatedFloat
+ private lateinit var bubbleBarScaleY: AnimatedFloat
+ private val handleCenterFromScreenBottom =
+ context.resources.getDimensionPixelSize(R.dimen.bubblebar_stashed_size) / 2f
private var animator: AnimatorSet? = null
@@ -136,12 +149,10 @@
bubbleBarTranslationYAnimator = bubbleBarViewController.bubbleBarTranslationY
// bubble bar has only alpha property, getting it at index 0
bubbleBarAlpha = bubbleBarViewController.bubbleBarAlpha.get(/* index= */ 0)
- bubbleBarScale = bubbleBarViewController.bubbleBarScale
+ bubbleBarScaleX = bubbleBarViewController.bubbleBarScaleX
+ bubbleBarScaleY = bubbleBarViewController.bubbleBarScaleY
stashedHeight = bubbleStashedHandleViewController?.stashedHeight ?: 0
- stashHandleViewAlpha =
- bubbleStashedHandleViewController
- ?.stashedHandleAlpha
- ?.get(StashedHandleViewController.ALPHA_INDEX_STASHED)
+ stashHandleViewAlpha = bubbleStashedHandleViewController?.stashedHandleAlpha?.get(0)
}
private fun animateAfterUnlock() {
@@ -149,7 +160,8 @@
if (isBubblesShowingOnHome || isBubblesShowingOnOverview) {
isStashed = false
animatorSet.playTogether(
- bubbleBarScale.animateToValue(1f),
+ bubbleBarScaleX.animateToValue(1f),
+ bubbleBarScaleY.animateToValue(1f),
bubbleBarTranslationYAnimator.animateToValue(bubbleBarTranslationY),
bubbleBarAlpha.animateToValue(1f)
)
@@ -169,7 +181,8 @@
stashHandleViewAlpha?.value = 0f
this.bubbleBarTranslationYAnimator.updateValue(bubbleBarTranslationY)
bubbleBarAlpha.setValue(1f)
- bubbleBarScale.updateValue(1f)
+ bubbleBarScaleX.updateValue(1f)
+ bubbleBarScaleY.updateValue(1f)
isStashed = false
onIsStashedChanged()
}
@@ -179,7 +192,8 @@
stashHandleViewAlpha?.value = 1f
this.bubbleBarTranslationYAnimator.updateValue(getStashTranslation())
bubbleBarAlpha.setValue(0f)
- bubbleBarScale.updateValue(STASHED_BAR_SCALE)
+ bubbleBarScaleX.updateValue(getStashScaleX())
+ bubbleBarScaleY.updateValue(getStashScaleY())
isStashed = true
onIsStashedChanged()
}
@@ -223,11 +237,11 @@
// the difference between the centers of the handle and the bubble bar is the difference
// between their distance from the bottom of the screen.
val barCenter: Float = bubbleBarViewController.bubbleBarCollapsedHeight / 2f
- return mHandleCenterFromScreenBottom - barCenter
+ return handleCenterFromScreenBottom - barCenter
}
override fun getStashedHandleTranslationForNewBubbleAnimation(): Float {
- return -mHandleCenterFromScreenBottom
+ return -handleCenterFromScreenBottom
}
override fun getStashedHandlePhysicsAnimator(): PhysicsAnimator<View>? {
@@ -245,7 +259,19 @@
override fun getHandleTranslationY(): Float? = bubbleStashedHandleViewController?.translationY
private fun getStashTranslation(): Float {
- return (bubbleBarViewController.bubbleBarCollapsedHeight - stashedHeight) / 2f
+ return bubbleBarTranslationY / 2f
+ }
+
+ @VisibleForTesting
+ fun getStashScaleX(): Float {
+ val handleWidth = bubbleStashedHandleViewController?.stashedWidth ?: 0
+ return handleWidth / bubbleBarViewController.bubbleBarCollapsedWidth
+ }
+
+ @VisibleForTesting
+ fun getStashScaleY(): Float {
+ val handleHeight = bubbleStashedHandleViewController?.stashedHeight ?: 0
+ return handleHeight / bubbleBarViewController.bubbleBarCollapsedHeight
}
/**
@@ -258,61 +284,93 @@
@Suppress("SameParameterValue")
private fun createStashAnimator(isStashed: Boolean, duration: Long): AnimatorSet {
val animatorSet = AnimatorSet()
- val fullLengthAnimatorSet = AnimatorSet()
- // Not exactly half and may overlap. See [first|second]HalfDurationScale below.
- val firstHalfAnimatorSet = AnimatorSet()
- val secondHalfAnimatorSet = AnimatorSet()
- val firstHalfDurationScale: Float
- val secondHalfDurationScale: Float
- val stashHandleAlphaValue: Float
- if (isStashed) {
- firstHalfDurationScale = 0.75f
- secondHalfDurationScale = 0.5f
- stashHandleAlphaValue = 1f
- fullLengthAnimatorSet.play(
- bubbleBarTranslationYAnimator.animateToValue(getStashTranslation())
- )
- firstHalfAnimatorSet.playTogether(
- bubbleBarAlpha.animateToValue(0f),
- bubbleBarScale.animateToValue(STASHED_BAR_SCALE)
- )
- } else {
- firstHalfDurationScale = 0.5f
- secondHalfDurationScale = 0.75f
- stashHandleAlphaValue = 0f
- fullLengthAnimatorSet.playTogether(
- bubbleBarScale.animateToValue(1f),
- bubbleBarTranslationYAnimator.animateToValue(bubbleBarTranslationY)
- )
- secondHalfAnimatorSet.playTogether(bubbleBarAlpha.animateToValue(1f))
- }
- stashHandleViewAlpha?.let {
- secondHalfAnimatorSet.playTogether(it.animateToValue(stashHandleAlphaValue))
- }
- bubbleStashedHandleViewController?.createRevealAnimToIsStashed(isStashed)?.let {
- fullLengthAnimatorSet.play(it)
- }
- fullLengthAnimatorSet.setDuration(duration)
- firstHalfAnimatorSet.setDuration((duration * firstHalfDurationScale).toLong())
- secondHalfAnimatorSet.setDuration((duration * secondHalfDurationScale).toLong())
- secondHalfAnimatorSet.startDelay = (duration * (1 - secondHalfDurationScale)).toLong()
- animatorSet.playTogether(fullLengthAnimatorSet, firstHalfAnimatorSet, secondHalfAnimatorSet)
- animatorSet.addListener(
- object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator) {
- animator = null
- controllersAfterInitAction.runAfterInit {
- if (isStashed) {
- bubbleBarViewController.isExpanded = false
- }
- taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
- }
- }
+
+ val alphaDuration = if (isStashed) duration else TASKBAR_STASH_ALPHA_DURATION
+ val alphaDelay = if (isStashed) TASKBAR_STASH_ALPHA_START_DELAY else 0L
+ animatorSet.play(
+ createStashAlphaAnimator(isStashed).apply {
+ this.duration = max(0L, alphaDuration - alphaDelay)
+ this.startDelay = alphaDelay
+ this.interpolator = LINEAR
}
)
+
+ animatorSet.play(
+ createSpringOnStashAnimator(isStashed).apply {
+ this.duration = duration
+ this.interpolator = LINEAR
+ }
+ )
+
+ animatorSet.play(
+ bubbleStashedHandleViewController?.createRevealAnimToIsStashed(isStashed)?.apply {
+ this.duration = duration
+ this.interpolator = EMPHASIZED
+ }
+ )
+
+ val pivotX = if (bubbleBarViewController.isBubbleBarOnLeft) 0f else 1f
+ animatorSet.play(
+ createScaleAnimator(isStashed).apply {
+ this.duration = duration
+ this.interpolator = EMPHASIZED
+ this.setBubbleBarPivotDuringAnim(pivotX, 1f)
+ }
+ )
+
+ val translationYTarget = if (isStashed) getStashTranslation() else bubbleBarTranslationY
+ animatorSet.play(
+ bubbleBarTranslationYAnimator.animateToValue(translationYTarget).apply {
+ this.duration = duration
+ this.interpolator = EMPHASIZED
+ }
+ )
+
+ animatorSet.doOnEnd {
+ animator = null
+ controllersAfterInitAction.runAfterInit {
+ if (isStashed) {
+ bubbleBarViewController.isExpanded = false
+ }
+ taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
+ }
+ }
return animatorSet
}
+ private fun createStashAlphaAnimator(isStashed: Boolean): AnimatorSet {
+ val stashHandleAlphaTarget = if (isStashed) 1f else 0f
+ val barAlphaTarget = if (isStashed) 0f else 1f
+ return AnimatorSet().apply {
+ play(bubbleBarAlpha.animateToValue(barAlphaTarget))
+ play(stashHandleViewAlpha?.animateToValue(stashHandleAlphaTarget))
+ }
+ }
+
+ private fun createSpringOnStashAnimator(isStashed: Boolean): Animator {
+ if (!isStashed) {
+ // Animate the stash translation back to 0
+ return translationYDuringStash.animateToValue(0f)
+ }
+ // Apply a spring to the handle
+ return SpringAnimationBuilder(context)
+ .setStartValue(translationYDuringStash.value)
+ .setEndValue(0f)
+ .setDampingRatio(SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY)
+ .setStiffness(SpringForce.STIFFNESS_LOW)
+ .setStartVelocity(stashHandleStashVelocity)
+ .build(translationYDuringStash, AnimatedFloat.VALUE)
+ }
+
+ private fun createScaleAnimator(isStashed: Boolean): AnimatorSet {
+ val scaleXTarget = if (isStashed) getStashScaleX() else 1f
+ val scaleYTarget = if (isStashed) getStashScaleY() else 1f
+ return AnimatorSet().apply {
+ play(bubbleBarScaleX.animateToValue(scaleXTarget))
+ play(bubbleBarScaleY.animateToValue(scaleYTarget))
+ }
+ }
+
private fun onIsStashedChanged() {
controllersAfterInitAction.runAfterInit {
taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
@@ -363,13 +421,23 @@
}
private fun Animator.updateTouchRegionOnAnimationEnd(): Animator {
- this.addListener(
- object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator) {
- onIsStashedChanged()
- }
+ doOnEnd { onIsStashedChanged() }
+ return this
+ }
+
+ private fun Animator.setBubbleBarPivotDuringAnim(pivotX: Float, pivotY: Float): Animator {
+ var initialPivotX = Float.NaN
+ var initialPivotY = Float.NaN
+ doOnStart {
+ initialPivotX = bubbleBarViewController.bubbleBarRelativePivotX
+ initialPivotY = bubbleBarViewController.bubbleBarRelativePivotY
+ bubbleBarViewController.setBubbleBarRelativePivot(pivotX, pivotY)
+ }
+ doOnEnd {
+ if (!initialPivotX.isNaN() && !initialPivotY.isNaN()) {
+ bubbleBarViewController.setBubbleBarRelativePivot(initialPivotX, initialPivotY)
}
- )
+ }
return this
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt
index 7d2d36d..726800c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt
@@ -18,18 +18,27 @@
import android.annotation.SuppressLint
import android.content.Context
+import android.content.res.ColorStateList
+import android.graphics.Color.TRANSPARENT
import android.util.AttributeSet
import android.view.LayoutInflater
-import android.widget.LinearLayout
+import android.view.MotionEvent
+import android.view.View
+import android.view.ViewConfiguration
import androidx.annotation.DimenRes
import androidx.annotation.DrawableRes
import androidx.core.view.setPadding
import com.android.launcher3.R
import com.android.launcher3.Utilities.dpToPx
-import com.android.launcher3.config.FeatureFlags
+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
+import com.android.launcher3.util.Executors.MAIN_EXECUTOR
import com.android.launcher3.views.ActivityContext
import com.android.launcher3.views.IconButtonView
+import com.android.quickstep.DeviceConfigWrapper
+import com.android.quickstep.util.AssistStateManager
/** Taskbar all apps button container for customizable taskbar. */
class TaskbarAllAppsButtonContainer
@@ -38,19 +47,21 @@
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
-) : LinearLayout(context, attrs), TaskbarContainer {
+) : IconButtonView(context, attrs), TaskbarContainer {
- private val allAppsButton: IconButtonView =
- LayoutInflater.from(context).inflate(R.layout.taskbar_all_apps_button, this, false)
- as IconButtonView
private val activityContext: TaskbarActivityContext = ActivityContext.lookupContext(context)
+ private var allAppsTouchTriggered = false
+ private var allAppsTouchRunnable: Runnable? = null
+ private var allAppsButtonTouchDelayMs: Long = ViewConfiguration.getLongPressTimeout().toLong()
+ private lateinit var taskbarViewCallbacks: TaskbarViewCallbacks
override val spaceNeeded: Int
get() {
- return dpToPx(activityContext.taskbarSpecsEvaluator!!.taskbarIconSize.size.toFloat())
+ return dpToPx(activityContext.taskbarSpecsEvaluator.taskbarIconSize.size.toFloat())
}
init {
+ LayoutInflater.from(context).inflate(R.layout.taskbar_all_apps_button, null, false)
setUpIcon()
}
@@ -58,24 +69,39 @@
private fun setUpIcon() {
val drawable =
resources.getDrawable(
- getAllAppsButton(activityContext.taskbarFeatureEvaluator!!.isTransient)
+ getAllAppsButton(activityContext.taskbarFeatureEvaluator.isTransient)
)
- val padding = activityContext.taskbarSpecsEvaluator!!.taskbarIconPadding
+ backgroundTintList = ColorStateList.valueOf(TRANSPARENT)
+ setIconDrawable(drawable)
+ setPadding(dpToPx(activityContext.taskbarSpecsEvaluator.taskbarIconPadding.toFloat()))
+ setForegroundTint(activityContext.getColor(R.color.all_apps_button_color))
+ }
- allAppsButton.setIconDrawable(drawable)
- allAppsButton.setPadding(padding)
- allAppsButton.setForegroundTint(activityContext.getColor(R.color.all_apps_button_color))
-
- // TODO(b/356465292) : add click listeners in future cl
- addView(allAppsButton)
+ @SuppressLint("ClickableViewAccessibility")
+ fun setUpCallbacks(callbacks: TaskbarViewCallbacks) {
+ taskbarViewCallbacks = callbacks
+ setOnClickListener(this::onAllAppsButtonClick)
+ setOnLongClickListener(this::onAllAppsButtonLongClick)
+ setOnTouchListener(this::onAllAppsButtonTouch)
+ isHapticFeedbackEnabled = taskbarViewCallbacks.isAllAppsButtonHapticFeedbackEnabled()
+ allAppsTouchRunnable = Runnable {
+ taskbarViewCallbacks.triggerAllAppsButtonLongClick()
+ allAppsTouchTriggered = true
+ }
+ val assistStateManager = AssistStateManager.INSTANCE[mContext]
+ if (
+ DeviceConfigWrapper.get().customLpaaThresholds &&
+ assistStateManager.lpnhDurationMillis.isPresent
+ ) {
+ allAppsButtonTouchDelayMs = assistStateManager.lpnhDurationMillis.get()
+ }
}
@DrawableRes
private fun getAllAppsButton(isTransientTaskbar: Boolean): Int {
val shouldSelectTransientIcon =
- isTransientTaskbar ||
- (FeatureFlags.enableTaskbarPinning() && !activityContext.isThreeButtonNav)
- return if (FeatureFlags.ENABLE_ALL_APPS_SEARCH_IN_TASKBAR.get()) {
+ 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 {
@@ -88,10 +114,43 @@
fun getAllAppsButtonTranslationXOffset(isTransientTaskbar: Boolean): Int {
return if (isTransientTaskbar) {
R.dimen.transient_taskbar_all_apps_button_translation_x_offset
- } else if (FeatureFlags.ENABLE_ALL_APPS_SEARCH_IN_TASKBAR.get()) {
+ } 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
}
}
+
+ private fun onAllAppsButtonTouch(view: View, ev: MotionEvent): Boolean {
+ when (ev.action) {
+ MotionEvent.ACTION_DOWN -> {
+ allAppsTouchTriggered = false
+ MAIN_EXECUTOR.handler.postDelayed(allAppsTouchRunnable!!, allAppsButtonTouchDelayMs)
+ }
+ MotionEvent.ACTION_UP,
+ MotionEvent.ACTION_CANCEL -> cancelAllAppsButtonTouch()
+ }
+ return false
+ }
+
+ private fun cancelAllAppsButtonTouch() {
+ MAIN_EXECUTOR.handler.removeCallbacks(allAppsTouchRunnable!!)
+ // ACTION_UP is first triggered, then click listener / long-click listener is triggered on
+ // the next frame, so we need to post twice and delay the reset.
+ this.post { this.post { allAppsTouchTriggered = false } }
+ }
+
+ private fun onAllAppsButtonClick(view: View) {
+ if (!allAppsTouchTriggered) {
+ taskbarViewCallbacks.triggerAllAppsButtonClick(view)
+ }
+ }
+
+ // Handle long click from Switch Access and Voice Access
+ private fun onAllAppsButtonLongClick(view: View): Boolean {
+ if (!MAIN_EXECUTOR.handler.hasCallbacks(allAppsTouchRunnable!!) && !allAppsTouchTriggered) {
+ taskbarViewCallbacks.triggerAllAppsButtonLongClick()
+ }
+ return true
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt
index 26e71f7..1fb835a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt
@@ -18,13 +18,15 @@
import android.annotation.SuppressLint
import android.content.Context
+import android.content.res.ColorStateList
+import android.graphics.Color.TRANSPARENT
import android.util.AttributeSet
import android.view.LayoutInflater
-import android.widget.LinearLayout
import androidx.core.view.setPadding
import com.android.launcher3.R
import com.android.launcher3.Utilities.dpToPx
import com.android.launcher3.taskbar.TaskbarActivityContext
+import com.android.launcher3.taskbar.TaskbarViewCallbacks
import com.android.launcher3.views.ActivityContext
import com.android.launcher3.views.IconButtonView
@@ -35,31 +37,30 @@
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
-) : LinearLayout(context, attrs), TaskbarContainer {
-
- private val taskbarDivider: IconButtonView =
- LayoutInflater.from(context).inflate(R.layout.taskbar_divider, this, false)
- as IconButtonView
+) : IconButtonView(context, attrs), TaskbarContainer {
private val activityContext: TaskbarActivityContext = ActivityContext.lookupContext(context)
override val spaceNeeded: Int
get() {
- return dpToPx(activityContext.taskbarSpecsEvaluator!!.taskbarIconSize.size.toFloat())
+ return dpToPx(activityContext.taskbarSpecsEvaluator.taskbarIconSize.size.toFloat())
}
init {
+ LayoutInflater.from(context).inflate(R.layout.taskbar_divider, null, false)
setUpIcon()
}
@SuppressLint("UseCompatLoadingForDrawables")
fun setUpIcon() {
+ backgroundTintList = ColorStateList.valueOf(TRANSPARENT)
val drawable = resources.getDrawable(R.drawable.taskbar_divider_button)
- val padding = activityContext.taskbarSpecsEvaluator!!.taskbarIconPadding
+ setIconDrawable(drawable)
+ setPadding(dpToPx(activityContext.taskbarSpecsEvaluator.taskbarIconPadding.toFloat()))
+ }
- taskbarDivider.setIconDrawable(drawable)
- taskbarDivider.setPadding(padding)
-
- // TODO(b/356465292):: add click listeners in future cl
- addView(taskbarDivider)
+ @SuppressLint("ClickableViewAccessibility")
+ fun setUpCallbacks(callbacks: TaskbarViewCallbacks) {
+ setOnLongClickListener(callbacks.taskbarDividerLongClickListener)
+ setOnTouchListener(callbacks.taskbarDividerRightClickListener)
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarIconSpecs.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarIconSpecs.kt
index 887eb01..6be0828 100644
--- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarIconSpecs.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarIconSpecs.kt
@@ -19,10 +19,11 @@
/** Taskbar Icon Specs */
object TaskbarIconSpecs {
- val iconSize40dp = TaskbarIconSize(40)
- val iconSize44dp = TaskbarIconSize(44)
- val iconSize48dp = TaskbarIconSize(48)
- val iconSize52dp = TaskbarIconSize(52)
+ // Mapping of visual icon size to icon specs value http://b/235886078
+ val iconSize40dp = TaskbarIconSize(44)
+ val iconSize44dp = TaskbarIconSize(48)
+ val iconSize48dp = TaskbarIconSize(52)
+ val iconSize52dp = TaskbarIconSize(57)
val transientTaskbarIconSizes = arrayOf(iconSize44dp, iconSize48dp, iconSize52dp)
diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarSpecsEvaluator.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarSpecsEvaluator.kt
index 761b47e..f37b2c1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarSpecsEvaluator.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarSpecsEvaluator.kt
@@ -25,14 +25,14 @@
numRows: Int = taskbarActivityContext.deviceProfile.inv.numRows,
numColumns: Int = taskbarActivityContext.deviceProfile.inv.numColumns,
) {
- var taskbarIconSize: TaskbarIconSize = getIconSizeByGrid(numRows, numColumns)
+ var taskbarIconSize: TaskbarIconSize = getIconSizeByGrid(numColumns, numRows)
// TODO(b/341146605) : initialize it to taskbar container in later cl.
private var taskbarContainer: List<TaskbarContainer> = emptyList()
val taskbarIconPadding: Int =
- if (TaskbarIconSpecs.minimumTaskbarIconTouchSize.size > taskbarIconSize.size) {
- (TaskbarIconSpecs.minimumTaskbarIconTouchSize.size - taskbarIconSize.size) / 2
+ if (TaskbarIconSpecs.iconSize52dp.size > taskbarIconSize.size) {
+ (TaskbarIconSpecs.iconSize52dp.size - taskbarIconSize.size) / 2
} else {
0
}
@@ -44,10 +44,10 @@
TaskbarIconSpecs.defaultPersistentIconMargin
}
- fun getIconSizeByGrid(row: Int, column: Int): TaskbarIconSize {
+ fun getIconSizeByGrid(columns: Int, rows: Int): TaskbarIconSize {
return if (taskbarFeatureEvaluator.isTransient) {
TaskbarIconSpecs.transientTaskbarIconSizeByGridSize.getOrDefault(
- TransientTaskbarIconSizeKey(row, column, taskbarFeatureEvaluator.isLandscape),
+ TransientTaskbarIconSizeKey(columns, rows, taskbarFeatureEvaluator.isLandscape),
TaskbarIconSpecs.defaultTransientIconSize,
)
} else {
@@ -102,6 +102,6 @@
data class TaskbarIconSize(val size: Int)
-data class TransientTaskbarIconSizeKey(val row: Int, val column: Int, val isLandscape: Boolean)
+data class TransientTaskbarIconSizeKey(val columns: Int, val rows: Int, val isLandscape: Boolean)
data class TaskbarIconMarginSize(val size: Int)
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index 37e0034..4590efe 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -379,12 +379,7 @@
}
@Override
- public void setIconVisible(boolean visible) {
- setForceHideRing(!visible);
- super.setIconVisible(visible);
- }
-
- private void setForceHideRing(boolean forceHideRing) {
+ public void setForceHideRing(boolean forceHideRing) {
if (mForceHideRing == forceHideRing) {
return;
}
@@ -417,7 +412,7 @@
private void drawEffect(Canvas canvas) {
// Don't draw ring effect if item is about to be dragged or if the icon is not visible.
- if (mDrawForDrag || !mIsIconVisible) {
+ if (mDrawForDrag || !mIsIconVisible || mForceHideRing) {
return;
}
mIconRingPaint.setColor(RING_SHADOW_COLOR);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 17735e1..55c1885 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -22,7 +22,6 @@
import static com.android.app.animation.Interpolators.EMPHASIZED;
import static com.android.internal.jank.Cuj.CUJ_LAUNCHER_LAUNCH_APP_PAIR_FROM_WORKSPACE;
-import static com.android.launcher3.Flags.enablePredictiveBackGesture;
import static com.android.launcher3.Flags.enableUnfoldStateAnimation;
import static com.android.launcher3.LauncherConstants.SavedInstanceKeys.PENDING_SPLIT_SELECT_INFO;
import static com.android.launcher3.LauncherConstants.SavedInstanceKeys.RUNTIME_STATE;
@@ -499,10 +498,8 @@
boolean started = ((getActivityFlags() & ACTIVITY_STATE_STARTED)) != 0;
if (started) {
DeviceProfile profile = getDeviceProfile();
- boolean willUserBeActive =
- (getActivityFlags() & ACTIVITY_STATE_USER_WILL_BE_ACTIVE) != 0;
boolean visible = (state == NORMAL || state == OVERVIEW)
- && (willUserBeActive || isUserActive())
+ && isUserActive()
&& !profile.isVerticalBarLayout()
&& !mIsOverlayVisible;
SystemUiProxy.INSTANCE.get(this)
@@ -696,9 +693,7 @@
// 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()
- if (Utilities.ATLEAST_U && enablePredictiveBackGesture()) {
- getApplicationInfo().setEnableOnBackInvokedCallback(true);
- }
+ getApplicationInfo().setEnableOnBackInvokedCallback(true);
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
mPendingSplitSelectInfo = ObjectWrapper.unwrap(
@@ -910,8 +905,7 @@
// event won't go through ViewRootImpl#InputStage#onProcess.
// So when receive back key, try to do the same check thing in
// ViewRootImpl#NativePreImeInputStage#onProcess
- if (!Utilities.ATLEAST_U || !enablePredictiveBackGesture()
- || event.getKeyCode() != KeyEvent.KEYCODE_BACK
+ if (event.getKeyCode() != KeyEvent.KEYCODE_BACK
|| event.getAction() != KeyEvent.ACTION_UP || event.isCanceled()) {
return false;
}
@@ -922,10 +916,6 @@
@Override
protected void registerBackDispatcher() {
- if (!enablePredictiveBackGesture()) {
- super.registerBackDispatcher();
- return;
- }
getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
OnBackInvokedDispatcher.PRIORITY_DEFAULT,
new OnBackAnimationCallback() {
@@ -1279,10 +1269,6 @@
return ObjectWrapper.wrap(new Integer(info.id));
}
- public void setHintUserWillBeActive() {
- addActivityFlags(ACTIVITY_STATE_USER_WILL_BE_ACTIVE);
- }
-
@Override
public void onDisplayInfoChanged(Context context, DisplayController.Info info, int flags) {
super.onDisplayInfoChanged(context, info, flags);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index 2625646..1ba784b 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -17,7 +17,6 @@
import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
-import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
import android.content.Context;
import android.graphics.Color;
@@ -107,8 +106,7 @@
@Override
public boolean isTaskbarAlignedWithHotseat(Launcher launcher) {
- if (ENABLE_SHELL_TRANSITIONS) return false;
- return super.isTaskbarAlignedWithHotseat(launcher);
+ return false;
}
@Override
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 3d94442..38d08e0 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -97,6 +97,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
+import androidx.annotation.VisibleForTesting;
import com.android.internal.jank.Cuj;
import com.android.internal.util.LatencyTracker;
@@ -170,10 +171,12 @@
/**
* Handles the navigation gestures when Launcher is the default home activity.
*/
-public abstract class AbsSwipeUpHandler<T extends RecentsViewContainer,
- Q extends RecentsView, S extends BaseState<S>>
- extends SwipeUpAnimationLogic implements OnApplyWindowInsetsListener,
- RecentsAnimationCallbacks.RecentsAnimationListener {
+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 {
private static final String TAG = "AbsSwipeUpHandler";
private static final ArrayList<String> STATE_NAMES = new ArrayList<>();
@@ -181,7 +184,7 @@
// Fraction of the scroll and transform animation in which the current task fades out
private static final float KQS_TASK_FADE_ANIMATION_FRACTION = 0.4f;
- protected final BaseContainerInterface<S, T> mContainerInterface;
+ protected final BaseContainerInterface<STATE, RECENTS_CONTAINER> mContainerInterface;
protected final InputConsumerProxy mInputConsumerProxy;
protected final ActivityInitListener mActivityInitListener;
// Callbacks to be made once the recents animation starts
@@ -192,8 +195,8 @@
protected @Nullable RecentsAnimationController mRecentsAnimationController;
protected @Nullable RecentsAnimationController mDeferredCleanupRecentsAnimationController;
protected RecentsAnimationTargets mRecentsAnimationTargets;
- protected @Nullable T mContainer;
- protected @Nullable Q mRecentsView;
+ protected @Nullable RECENTS_CONTAINER mContainer;
+ protected @Nullable RECENTS_VIEW mRecentsView;
protected Runnable mGestureEndCallback;
protected MultiStateCallback mStateCallback;
protected boolean mCanceled;
@@ -486,11 +489,11 @@
return false;
}
- T createdContainer = (T) mContainerInterface.getCreatedContainer();
+ RECENTS_CONTAINER createdContainer = mContainerInterface.getCreatedContainer();
if (createdContainer != null) {
initTransitionEndpoints(createdContainer.getDeviceProfile());
}
- final T container = (T) mContainerInterface.getCreatedContainer();
+ final RECENTS_CONTAINER container = mContainerInterface.getCreatedContainer();
if (mContainer == container) {
return true;
}
@@ -564,7 +567,7 @@
}
private void onLauncherStart() {
- final T container = (T) mContainerInterface.getCreatedContainer();
+ final RECENTS_CONTAINER container = mContainerInterface.getCreatedContainer();
if (container == null || mContainer != container) {
return;
}
@@ -949,7 +952,6 @@
// We will handle the sysui flags based on the centermost task view.
mRecentsAnimationController.setUseLauncherSystemBarFlags(swipeUpThresholdPassed
|| (quickswitchThresholdPassed && centermostTaskFlags != 0));
- mRecentsAnimationController.setSplitScreenMinimized(mContext, swipeUpThresholdPassed);
// Provide a hint to WM the direction that we will be settling in case the animation
// needs to be canceled
mRecentsAnimationController.setWillFinishToHome(swipeUpThresholdPassed);
@@ -1097,7 +1099,7 @@
*/
@UiThread
private void notifyGestureStarted() {
- final T curActivity = mContainer;
+ final RECENTS_CONTAINER curActivity = mContainer;
if (curActivity != null) {
// Once the gesture starts, we can no longer transition home through the button, so
// reset the force override of the activity visibility
@@ -1168,7 +1170,8 @@
/**
* Called if the end target has been set and the recents animation is started.
*/
- private void onCalculateEndTarget() {
+ @VisibleForTesting
+ protected void onCalculateEndTarget() {
final GestureEndTarget endTarget = mGestureState.getEndTarget();
switch (endTarget) {
@@ -1181,7 +1184,8 @@
}
}
- private void onSettledOnEndTarget() {
+ @VisibleForTesting
+ protected void onSettledOnEndTarget() {
// Fast-finish the attaching animation if it's still running.
maybeUpdateRecentsAttachedState(false);
final GestureEndTarget endTarget = mGestureState.getEndTarget();
@@ -1417,7 +1421,7 @@
}
}
Interpolator interpolator;
- S state = mContainerInterface.stateFromGestureEndTarget(endTarget);
+ STATE state = mContainerInterface.stateFromGestureEndTarget(endTarget);
if (isKeyboardTaskFocusPending()) {
interpolator = EMPHASIZED;
} else if (state.displayOverviewTasksAsGrid(mDp)) {
@@ -1767,8 +1771,7 @@
private int calculateWindowRotation(RemoteAnimationTarget runningTaskTarget,
RecentsOrientedState orientationState) {
- if (runningTaskTarget.rotationChange != 0
- && TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
+ if (runningTaskTarget.rotationChange != 0) {
return Math.abs(runningTaskTarget.rotationChange) == ROTATION_90
? ROTATION_270 : ROTATION_90;
} else {
@@ -2499,7 +2502,7 @@
}
private void animateSplashScreenExit(
- @NonNull T activity,
+ @NonNull RECENTS_CONTAINER activity,
@NonNull RemoteAnimationTarget[] appearedTaskTargets,
@NonNull RemoteAnimationTarget[] animatingTargets) {
ViewGroup splashView = activity.getDragLayer();
diff --git a/quickstep/src/com/android/quickstep/BaseContainerInterface.java b/quickstep/src/com/android/quickstep/BaseContainerInterface.java
index 3a8c141..777761b 100644
--- a/quickstep/src/com/android/quickstep/BaseContainerInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseContainerInterface.java
@@ -68,8 +68,6 @@
public abstract void onAssistantVisibilityChanged(float assistantVisibility);
- public abstract boolean allowMinimizeSplitScreen();
-
public abstract boolean isResumed();
public abstract boolean isStarted();
diff --git a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
index 89fbf4a..94a4527 100644
--- a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
@@ -133,12 +133,6 @@
}
@Override
- public boolean allowMinimizeSplitScreen() {
- // TODO: Remove this once b/77875376 is fixed
- return false;
- }
-
- @Override
public boolean allowAllAppsFromOverview() {
return false;
}
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index b564fa7..e9fe2f7 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -271,11 +271,6 @@
}
@Override
- public boolean allowMinimizeSplitScreen() {
- return true;
- }
-
- @Override
public boolean allowAllAppsFromOverview() {
return FeatureFlags.ENABLE_ALL_APPS_FROM_OVERVIEW.get()
// If floating search bar would not show in overview, don't allow all apps gesture.
diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index e17cdcd..f653e60 100644
--- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -62,8 +62,8 @@
/**
* Temporary class to allow easier refactoring
*/
-public class LauncherSwipeHandlerV2 extends
- AbsSwipeUpHandler<QuickstepLauncher, RecentsView, LauncherState> {
+public class LauncherSwipeHandlerV2 extends AbsSwipeUpHandler<
+ QuickstepLauncher, RecentsView<QuickstepLauncher, LauncherState>, LauncherState> {
public LauncherSwipeHandlerV2(Context context, RecentsAnimationDeviceState deviceState,
TaskAnimationManager taskAnimationManager, GestureState gestureState, long touchTimeMs,
@@ -107,9 +107,6 @@
|| !mContainer.getDesktopVisibilityController().areDesktopTasksVisible());
mContainer.getRootView().setForceHideBackArrow(true);
- if (!TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
- mContainer.setHintUserWillBeActive();
- }
if (!canUseWorkspaceView || appCanEnterPip || mIsSwipeForSplit) {
return new LauncherHomeAnimationFactory() {
diff --git a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
index 84f6b55..a03c0f8 100644
--- a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
+++ b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
@@ -37,6 +37,7 @@
import com.android.launcher3.util.DisplayController.Info;
import com.android.launcher3.util.NavigationMode;
import com.android.launcher3.util.window.CachedDisplayInfo;
+import com.android.systemui.shared.Flags;
import java.io.PrintWriter;
import java.util.HashMap;
@@ -242,7 +243,8 @@
int rotation = display.rotation;
int touchHeight = mNavBarGesturalHeight;
OrientationRectF orientationRectF = new OrientationRectF(0, 0, size.x, size.y, rotation);
- if (mMode == NavigationMode.NO_BUTTON) {
+ if (mMode == NavigationMode.NO_BUTTON
+ || (mMode == NavigationMode.THREE_BUTTONS && Flags.threeButtonCornerSwipe())) {
orientationRectF.top = orientationRectF.bottom - touchHeight;
updateAssistantRegions(orientationRectF);
} else {
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
index 9a2b7ce..8873275 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
@@ -338,7 +338,7 @@
taskAnimationManager.notifyRecentsAnimationState(recentAnimListener)
} else {
val intent =
- Intent(interactionHandler.launchIntent)
+ Intent(interactionHandler.getLaunchIntent())
.putExtra(ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID, gestureState.gestureId)
command.setAnimationCallbacks(
taskAnimationManager.startRecentsAnimation(gestureState, intent, interactionHandler)
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
index 32e2389..7b9b560 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -54,17 +54,14 @@
private final Set<RecentsAnimationListener> mListeners = new ArraySet<>();
private final SystemUiProxy mSystemUiProxy;
- private final boolean mAllowMinimizeSplitScreen;
// TODO(141886704): Remove these references when they are no longer needed
private RecentsAnimationController mController;
private boolean mCancelled;
- public RecentsAnimationCallbacks(SystemUiProxy systemUiProxy,
- boolean allowMinimizeSplitScreen) {
+ public RecentsAnimationCallbacks(SystemUiProxy systemUiProxy) {
mSystemUiProxy = systemUiProxy;
- mAllowMinimizeSplitScreen = allowMinimizeSplitScreen;
}
@UiThread
@@ -122,7 +119,7 @@
}
mController = new RecentsAnimationController(animationController,
- mAllowMinimizeSplitScreen, this::onAnimationFinished);
+ this::onAnimationFinished);
if (mCancelled) {
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(),
mController::finishAnimationToApp);
@@ -219,7 +216,6 @@
public void dump(String prefix, PrintWriter pw) {
pw.println(prefix + "RecentsAnimationCallbacks:");
- pw.println(prefix + "\tmAllowMinimizeSplitScreen=" + mAllowMinimizeSplitScreen);
pw.println(prefix + "\tmCancelled=" + mCancelled);
}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
index 1b05e28..adcf4ef 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.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.FINISH_RECENTS_ANIMATION;
import android.content.Context;
@@ -52,21 +51,17 @@
private static final String TAG = "RecentsAnimationController";
private final RecentsAnimationControllerCompat mController;
private final Consumer<RecentsAnimationController> mOnFinishedListener;
- private final boolean mAllowMinimizeSplitScreen;
private boolean mUseLauncherSysBarFlags = false;
- private boolean mSplitScreenMinimized = false;
private boolean mFinishRequested = false;
// Only valid when mFinishRequested == true.
private boolean mFinishTargetIsLauncher;
private RunnableList mPendingFinishCallbacks = new RunnableList();
public RecentsAnimationController(RecentsAnimationControllerCompat controller,
- boolean allowMinimizeSplitScreen,
Consumer<RecentsAnimationController> onFinishedListener) {
mController = controller;
mOnFinishedListener = onFinishedListener;
- mAllowMinimizeSplitScreen = allowMinimizeSplitScreen;
}
/**
@@ -85,34 +80,17 @@
if (mUseLauncherSysBarFlags != useLauncherSysBarFlags) {
mUseLauncherSysBarFlags = useLauncherSysBarFlags;
UI_HELPER_EXECUTOR.execute(() -> {
- if (!ENABLE_SHELL_TRANSITIONS) {
- mController.setAnimationTargetsBehindSystemBars(!useLauncherSysBarFlags);
- } else {
- try {
- WindowManagerGlobal.getWindowManagerService().setRecentsAppBehindSystemBars(
- useLauncherSysBarFlags);
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to reach window manager", e);
- }
+ try {
+ WindowManagerGlobal.getWindowManagerService().setRecentsAppBehindSystemBars(
+ useLauncherSysBarFlags);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to reach window manager", e);
}
});
}
}
/**
- * Indicates that the gesture has crossed the window boundary threshold and we should minimize
- * if we are in splitscreen.
- */
- public void setSplitScreenMinimized(Context context, boolean splitScreenMinimized) {
- if (!mAllowMinimizeSplitScreen) {
- return;
- }
- if (mSplitScreenMinimized != splitScreenMinimized) {
- mSplitScreenMinimized = splitScreenMinimized;
- }
- }
-
- /**
* Remove task remote animation target from
* {@link RecentsAnimationCallbacks#onTasksAppeared}}.
*/
@@ -272,9 +250,7 @@
public void dump(String prefix, PrintWriter pw) {
pw.println(prefix + "RecentsAnimationController:");
- pw.println(prefix + "\tmAllowMinimizeSplitScreen=" + mAllowMinimizeSplitScreen);
pw.println(prefix + "\tmUseLauncherSysBarFlags=" + mUseLauncherSysBarFlags);
- pw.println(prefix + "\tmSplitScreenMinimized=" + mSplitScreenMinimized);
pw.println(prefix + "\tmFinishRequested=" + mFinishRequested);
pw.println(prefix + "\tmFinishTargetIsLauncher=" + mFinishTargetIsLauncher);
}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index f902284..5131774 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -74,6 +74,7 @@
import com.android.quickstep.util.GestureExclusionManager;
import com.android.quickstep.util.GestureExclusionManager.ExclusionListener;
import com.android.quickstep.util.NavBarPosition;
+import com.android.systemui.shared.Flags;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
@@ -548,6 +549,13 @@
}
/**
+ * @return whether the Assistant gesture can be used in 3 button navigation mode.
+ */
+ public boolean supportsAssistantGestureInButtonNav() {
+ return Flags.threeButtonCornerSwipe();
+ }
+
+ /**
* @param ev An ACTION_DOWN motion event
* @return whether the given motion event can trigger the assistant over the current task.
*/
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index db03dac..a01ceb2 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -43,6 +43,7 @@
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.SafeCloseable;
import com.android.quickstep.recents.data.RecentTasksDataSource;
+import com.android.quickstep.recents.data.TaskVisualsChangeNotifier;
import com.android.quickstep.util.DesktopTask;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.util.TaskVisualsChangeListener;
@@ -65,7 +66,8 @@
*/
@TargetApi(Build.VERSION_CODES.O)
public class RecentsModel implements RecentTasksDataSource, IconChangeListener,
- TaskStackChangeListener, TaskVisualsChangeListener, SafeCloseable {
+ TaskStackChangeListener, TaskVisualsChangeListener, TaskVisualsChangeNotifier,
+ SafeCloseable {
// We do not need any synchronization for this variable as its only written on UI thread.
public static final MainThreadInitializedObject<RecentsModel> INSTANCE =
@@ -287,6 +289,7 @@
/**
* Adds a listener for visuals changes
*/
+ @Override
public void addThumbnailChangeListener(TaskVisualsChangeListener listener) {
mThumbnailChangeListeners.add(listener);
}
@@ -294,6 +297,7 @@
/**
* Removes a previously added listener
*/
+ @Override
public void removeThumbnailChangeListener(TaskVisualsChangeListener listener) {
mThumbnailChangeListeners.remove(listener);
}
diff --git a/quickstep/src/com/android/quickstep/RotationTouchHelper.java b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
index 6f1ab7d..80c07196 100644
--- a/quickstep/src/com/android/quickstep/RotationTouchHelper.java
+++ b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
@@ -41,6 +41,7 @@
import com.android.launcher3.util.NavigationMode;
import com.android.launcher3.util.SafeCloseable;
import com.android.quickstep.util.RecentsOrientedState;
+import com.android.systemui.shared.Flags;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
@@ -157,7 +158,7 @@
// Register for navigation mode changes
mDisplayController.addChangeListener(this);
DisplayController.Info info = mDisplayController.getInfo();
- onDisplayInfoChangedInternal(info, CHANGE_ALL, info.getNavigationMode().hasGestures);
+ onDisplayInfoChangedInternal(info, CHANGE_ALL, hasGestures(info.getNavigationMode()));
runOnDestroy(() -> mDisplayController.removeChangeListener(this));
mOrientationListener = new OrientationEventListener(mContext) {
@@ -229,7 +230,7 @@
* Updates the regions for detecting the swipe up/quickswitch and assistant gestures.
*/
public void updateGestureTouchRegions() {
- if (!mMode.hasGestures) {
+ if (!hasGestures(mMode)) {
return;
}
@@ -268,7 +269,7 @@
| CHANGE_SUPPORTED_BOUNDS)) != 0) {
mDisplayRotation = info.rotation;
- if (mMode.hasGestures) {
+ if (hasGestures(mMode)) {
updateGestureTouchRegions();
mOrientationTouchTransformer.createOrAddTouchRegion(info);
mCurrentAppRotation = mDisplayRotation;
@@ -295,9 +296,9 @@
mOrientationTouchTransformer.setNavigationMode(newMode, mDisplayController.getInfo(),
mContext.getResources());
- if (forceRegister || (!mMode.hasGestures && newMode.hasGestures)) {
+ if (forceRegister || (!hasGestures(mMode) && hasGestures(newMode))) {
setupOrientationSwipeHandler();
- } else if (mMode.hasGestures && !newMode.hasGestures) {
+ } else if (hasGestures(mMode) && !hasGestures(newMode)) {
destroyOrientationSwipeHandlerCallback();
}
@@ -399,7 +400,7 @@
}
public int getCurrentActiveRotation() {
- if (!mMode.hasGestures) {
+ if (!hasGestures(mMode)) {
// touch rotation should always match that of display for 3 button
return mDisplayRotation;
}
@@ -416,4 +417,8 @@
public OrientationTouchTransformer getOrientationTouchTransformer() {
return mOrientationTouchTransformer;
}
+
+ private boolean hasGestures(NavigationMode mode) {
+ return mode.hasGestures || (mode == THREE_BUTTONS && Flags.threeButtonCornerSwipe());
+ }
}
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 4392255..dde16c8 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -81,7 +81,6 @@
import com.android.wm.shell.back.IBackAnimation;
import com.android.wm.shell.bubbles.IBubbles;
import com.android.wm.shell.bubbles.IBubblesListener;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
import com.android.wm.shell.common.pip.IPip;
import com.android.wm.shell.common.pip.IPipAnimationListener;
import com.android.wm.shell.desktopmode.IDesktopMode;
@@ -92,6 +91,7 @@
import com.android.wm.shell.recents.IRecentTasksListener;
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;
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index 85eea3b..49ec597 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -60,9 +60,8 @@
import java.util.HashMap;
public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener {
- public static final boolean ENABLE_SHELL_TRANSITIONS = true;
- public static final boolean SHELL_TRANSITIONS_ROTATION = ENABLE_SHELL_TRANSITIONS
- && SystemProperties.getBoolean("persist.wm.debug.shell_transit_rotate", false);
+ public static final boolean SHELL_TRANSITIONS_ROTATION =
+ SystemProperties.getBoolean("persist.wm.debug.shell_transit_rotate", false);
private final Context mCtx;
private RecentsAnimationController mController;
@@ -160,8 +159,7 @@
final BaseContainerInterface containerInterface = gestureState.getContainerInterface();
mLastGestureState = gestureState;
- RecentsAnimationCallbacks newCallbacks = new RecentsAnimationCallbacks(
- getSystemUiProxy(), containerInterface.allowMinimizeSplitScreen());
+ RecentsAnimationCallbacks newCallbacks = new RecentsAnimationCallbacks(getSystemUiProxy());
mCallbacks = newCallbacks;
mCallbacks.addListener(new RecentsAnimationCallbacks.RecentsAnimationListener() {
@Override
@@ -191,7 +189,7 @@
}
mLastGestureState.updateLastAppearedTaskTargets(mLastAppearedTaskTargets);
- if (ENABLE_SHELL_TRANSITIONS && mTargets.hasRecents
+ if (mTargets.hasRecents
// The filtered (MODE_CLOSING) targets only contain 1 home activity.
&& mTargets.apps.length == 1
&& mTargets.apps[0].windowConfiguration.getActivityType()
diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
index 3c6c3e4..580dcc2 100644
--- a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
+++ b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
@@ -27,6 +27,7 @@
import com.android.launcher3.R;
import com.android.launcher3.util.CancellableTask;
import com.android.launcher3.util.Preconditions;
+import com.android.quickstep.recents.data.HighResLoadingStateNotifier;
import com.android.quickstep.task.thumbnail.data.TaskThumbnailDataSource;
import com.android.quickstep.util.TaskKeyByLastActiveTimeCache;
import com.android.quickstep.util.TaskKeyCache;
@@ -48,7 +49,7 @@
private final boolean mEnableTaskSnapshotPreloading;
private final Context mContext;
- public static class HighResLoadingState {
+ public static class HighResLoadingState implements HighResLoadingStateNotifier {
private boolean mForceHighResThumbnails;
private boolean mVisible;
private boolean mFlingingFast;
@@ -65,11 +66,13 @@
mForceHighResThumbnails = !supportsLowResThumbnails();
}
- public void addCallback(HighResLoadingStateChangedCallback callback) {
+ @Override
+ public void addCallback(@NonNull HighResLoadingStateChangedCallback callback) {
mCallbacks.add(callback);
}
- public void removeCallback(HighResLoadingStateChangedCallback callback) {
+ @Override
+ public void removeCallback(@NonNull HighResLoadingStateChangedCallback callback) {
mCallbacks.remove(callback);
}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 69d1c35..4587bdd 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -89,6 +89,7 @@
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;
@@ -674,8 +675,9 @@
private void initInputMonitor(String reason) {
disposeEventHandlers("Initializing input monitor due to: " + reason);
- if (mDeviceState.isButtonNavMode() && (!ENABLE_TRACKPAD_GESTURE.get()
- || mTrackpadsConnected.isEmpty())) {
+ if (mDeviceState.isButtonNavMode()
+ && !mDeviceState.supportsAssistantGestureInButtonNav()
+ && (!ENABLE_TRACKPAD_GESTURE.get() || mTrackpadsConnected.isEmpty())) {
return;
}
@@ -857,7 +859,9 @@
.append("); cancelling gesture."),
NAVIGATION_MODE_SWITCHED);
event.setAction(ACTION_CANCEL);
- } else if (mDeviceState.isButtonNavMode() && !isTrackpadMotionEvent(event)) {
+ } 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"));
@@ -909,7 +913,22 @@
if (isInSwipeUpTouchRegion && tac != null) {
tac.closeKeyboardQuickSwitchView();
}
- if ((!isOneHandedModeActive && isInSwipeUpTouchRegion)
+ if (mDeviceState.isButtonNavMode()
+ && mDeviceState.supportsAssistantGestureInButtonNav()) {
+ reasonString.append("in three button mode which supports Assistant gesture");
+ // Consume gesture event for Assistant (all other gestures should do nothing).
+ if (mDeviceState.canTriggerAssistantAction(event)) {
+ reasonString.append(" and event can trigger assistant action")
+ .append(", consuming gesture for assistant action");
+ mGestureState =
+ createGestureState(mGestureState, getTrackpadGestureType(event));
+ mUncheckedConsumer = tryCreateAssistantInputConsumer(mGestureState, event);
+ } else {
+ reasonString.append(" but event cannot trigger Assistant")
+ .append(", consuming gesture as no-op");
+ mUncheckedConsumer = InputConsumer.NO_OP;
+ }
+ } else if ((!isOneHandedModeActive && isInSwipeUpTouchRegion)
|| isHoverActionWithoutConsumer || isOnBubbles) {
reasonString.append(!isOneHandedModeActive && isInSwipeUpTouchRegion
? "one handed mode is not active and event is in swipe up region"
@@ -931,8 +950,7 @@
: "event is a trackpad multi-finger swipe")
.append(" and event can trigger assistant action")
.append(", consuming gesture for assistant action");
- mGestureState = createGestureState(mGestureState,
- getTrackpadGestureType(event));
+ mGestureState = createGestureState(mGestureState, getTrackpadGestureType(event));
// Do not change mConsumer as if there is an ongoing QuickSwitch gesture, we
// should not interrupt it. QuickSwitch assumes that interruption can only
// happen if the next gesture is also quick switch.
@@ -1193,7 +1211,8 @@
NavHandle navHandle = tac != null ? tac.getNavHandle()
: SystemUiProxy.INSTANCE.get(this);
if (canStartSystemGesture && !previousGestureState.isRecentsAnimationRunning()
- && navHandle.canNavHandleBeLongPressed()) {
+ && navHandle.canNavHandleBeLongPressed()
+ && !ignoreThreeFingerTrackpadForNavHandleLongPress(mGestureState)) {
reasonString.append(NEWLINE_PREFIX)
.append(reasonPrefix)
.append(SUBSTRING_PREFIX)
@@ -1289,6 +1308,11 @@
return new CompoundString(NEWLINE_PREFIX).append(substring);
}
+ private boolean ignoreThreeFingerTrackpadForNavHandleLongPress(GestureState gestureState) {
+ return Flags.ignoreThreeFingerTrackpadForNavHandleLongPress()
+ && gestureState.isThreeFingerTrackpadGesture();
+ }
+
private void logInputConsumerSelectionReason(
InputConsumer consumer, CompoundString reasonString) {
ActiveGestureLog.INSTANCE.addLog(new CompoundString("setInputConsumer: ")
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/BubbleBarInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/BubbleBarInputConsumer.java
index dbe2068..92031c5 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/BubbleBarInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/BubbleBarInputConsumer.java
@@ -45,6 +45,7 @@
private boolean mSwipeUpOnBubbleHandle;
private boolean mPassedTouchSlop;
+ private boolean mStashedOrCollapsedOnDown;
private final int mTouchSlop;
private final PointF mDownPos = new PointF();
@@ -69,13 +70,13 @@
@Override
public void onMotionEvent(MotionEvent ev) {
- final boolean isStashed = mBubbleStashController.isStashed();
final int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
mActivePointerId = ev.getPointerId(0);
mDownPos.set(ev.getX(), ev.getY());
mLastPos.set(mDownPos);
+ mStashedOrCollapsedOnDown = mBubbleStashController.isStashed() || isCollapsed();
break;
case MotionEvent.ACTION_MOVE:
int pointerIndex = ev.findPointerIndex(mActivePointerId);
@@ -89,7 +90,7 @@
if (!mPassedTouchSlop) {
mPassedTouchSlop = Math.abs(dY) > mTouchSlop || Math.abs(dX) > mTouchSlop;
}
- if ((isCollapsed() || isStashed) && !mSwipeUpOnBubbleHandle && mPassedTouchSlop) {
+ if (mStashedOrCollapsedOnDown && !mSwipeUpOnBubbleHandle && mPassedTouchSlop) {
boolean verticalGesture = Math.abs(dY) > Math.abs(dX);
if (verticalGesture && !mBubbleDragController.isDragging()) {
mSwipeUpOnBubbleHandle = true;
@@ -102,11 +103,10 @@
break;
case MotionEvent.ACTION_UP:
boolean isWithinTapTime = ev.getEventTime() - ev.getDownTime() <= mTimeForTap;
- if (isWithinTapTime && !mSwipeUpOnBubbleHandle && !mPassedTouchSlop) {
+ if (isWithinTapTime && !mSwipeUpOnBubbleHandle && !mPassedTouchSlop
+ && mStashedOrCollapsedOnDown) {
// Taps on the handle / collapsed state should open the bar
- if (isStashed || isCollapsed()) {
- mBubbleStashController.showBubbleBar(/* expandBubbles= */ true);
- }
+ mBubbleStashController.showBubbleBar(/* expandBubbles= */ true);
}
break;
}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index 14f47d1..b66d4cb 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -24,7 +24,6 @@
import static com.android.quickstep.AbsSwipeUpHandler.MIN_PROGRESS_FOR_OVERVIEW;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely;
-import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
import android.animation.Animator;
@@ -212,15 +211,13 @@
// This will come back and cancel the interaction.
startHomeIntentSafely(mContext, mGestureState.getHomeIntent(), null, TAG);
mHomeLaunched = true;
- } else if (ENABLE_SHELL_TRANSITIONS) {
- if (mTaskAnimationManager.getCurrentCallbacks() != null) {
- if (mRecentsAnimationController != null) {
- finishRecentsAnimationForShell(dismissTask);
- } else {
- // the transition of recents animation hasn't started, wait for it
- mCancelWhenRecentsStart = true;
- mDismissTask = dismissTask;
- }
+ } else if (mTaskAnimationManager.getCurrentCallbacks() != null) {
+ if (mRecentsAnimationController != null) {
+ finishRecentsAnimationForShell(dismissTask);
+ } else {
+ // the transition of recents animation hasn't started, wait for it
+ mCancelWhenRecentsStart = true;
+ mDismissTask = dismissTask;
}
}
mStateCallback.setState(STATE_HANDLER_INVALIDATED);
diff --git a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
index 36ea926..acc9959 100644
--- a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
@@ -41,6 +41,7 @@
import com.android.launcher3.logging.StatsLogManager;
import com.android.quickstep.TouchInteractionService.TISBinder;
import com.android.quickstep.interaction.TutorialController.TutorialType;
+import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.util.TISBindHelper;
import java.util.ArrayList;
@@ -54,8 +55,6 @@
static final String KEY_TUTORIAL_TYPE = "tutorial_type";
static final String KEY_GESTURE_COMPLETE = "gesture_complete";
static final String KEY_USE_TUTORIAL_MENU = "use_tutorial_menu";
- public static final double SQUARE_ASPECT_RATIO_BOTTOM_BOUND = 0.95;
- public static final double SQUARE_ASPECT_RATIO_UPPER_BOUND = 1.05;
@Nullable private TutorialType[] mTutorialSteps;
private GestureSandboxFragment mCurrentFragment;
@@ -170,10 +169,7 @@
getApplicationContext()).getDeviceProfile(this);
if (deviceProfile.isTablet) {
// The tutorial will work in either orientation if the height and width are similar
- boolean isAspectRatioSquare =
- deviceProfile.aspectRatio > SQUARE_ASPECT_RATIO_BOTTOM_BOUND
- && deviceProfile.aspectRatio < SQUARE_ASPECT_RATIO_UPPER_BOUND;
- boolean showRotationPrompt = !isAspectRatioSquare
+ boolean showRotationPrompt = !LayoutUtils.isAspectRatioSquare(deviceProfile.aspectRatio)
&& getResources().getConfiguration().orientation
== ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
diff --git a/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt b/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt
index ec04cb7..658975c 100644
--- a/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt
+++ b/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt
@@ -37,6 +37,7 @@
import androidx.annotation.VisibleForTesting
import androidx.core.util.component1
import androidx.core.util.component2
+import androidx.core.view.updateLayoutParams
import com.android.launcher3.DeviceProfile
import com.android.launcher3.Flags
import com.android.launcher3.LauncherAnimUtils
@@ -242,7 +243,30 @@
lp.height = ViewGroup.LayoutParams.WRAP_CONTENT
}
- override fun getDwbLayoutTranslations(
+ override fun updateDwbBannerLayout(
+ taskViewWidth: Int,
+ taskViewHeight: Int,
+ isGroupedTaskView: Boolean,
+ deviceProfile: DeviceProfile,
+ snapshotViewWidth: Int,
+ snapshotViewHeight: Int,
+ banner: View
+ ) {
+ banner.pivotX = 0f
+ banner.pivotY = 0f
+ banner.rotation = degreesRotated
+ banner.updateLayoutParams<FrameLayout.LayoutParams> {
+ gravity = Gravity.TOP or if (banner.isLayoutRtl) Gravity.END else Gravity.START
+ width =
+ if (isGroupedTaskView) {
+ snapshotViewHeight
+ } else {
+ taskViewHeight - deviceProfile.overviewTaskThumbnailTopMarginPx
+ }
+ }
+ }
+
+ override fun getDwbBannerTranslations(
taskViewWidth: Int,
taskViewHeight: Int,
splitBounds: SplitBounds?,
@@ -252,39 +276,25 @@
banner: View
): Pair<Float, Float> {
val snapshotParams = thumbnailViews[0].layoutParams as FrameLayout.LayoutParams
- val isRtl = banner.layoutDirection == View.LAYOUT_DIRECTION_RTL
val translationX = banner.height.toFloat()
-
- val bannerParams = banner.layoutParams as FrameLayout.LayoutParams
- bannerParams.gravity = Gravity.TOP or if (isRtl) Gravity.END else Gravity.START
- banner.pivotX = 0f
- banner.pivotY = 0f
- banner.rotation = degreesRotated
-
- if (splitBounds == null) {
- // Single, fullscreen case
- bannerParams.width = taskViewHeight - snapshotParams.topMargin
- return Pair(translationX, snapshotParams.topMargin.toFloat())
- }
-
- // Set correct width and translations
val translationY: Float
- if (desiredTaskId == splitBounds.leftTopTaskId) {
- bannerParams.width = thumbnailViews[0].measuredHeight
+ if (splitBounds == null) {
translationY = snapshotParams.topMargin.toFloat()
} else {
- bannerParams.width = thumbnailViews[1].measuredHeight
- val topLeftTaskPlusDividerPercent =
- if (splitBounds.appsStackedVertically) {
- splitBounds.topTaskPercent + splitBounds.dividerHeightPercent
- } else {
- splitBounds.leftTaskPercent + splitBounds.dividerWidthPercent
- }
- translationY =
- snapshotParams.topMargin +
- (taskViewHeight - snapshotParams.topMargin) * topLeftTaskPlusDividerPercent
+ if (desiredTaskId == splitBounds.leftTopTaskId) {
+ translationY = snapshotParams.topMargin.toFloat()
+ } else {
+ val topLeftTaskPlusDividerPercent =
+ if (splitBounds.appsStackedVertically) {
+ splitBounds.topTaskPercent + splitBounds.dividerHeightPercent
+ } else {
+ splitBounds.leftTaskPercent + splitBounds.dividerWidthPercent
+ }
+ translationY =
+ snapshotParams.topMargin +
+ (taskViewHeight - snapshotParams.topMargin) * topLeftTaskPlusDividerPercent
+ }
}
-
return Pair(translationX, translationY)
}
@@ -300,6 +310,7 @@
if (isRtl) displacement < 0 else displacement > 0
override fun getTaskDragDisplacementFactor(isRtl: Boolean): Int = if (isRtl) 1 else -1
+
/* -------------------- */
override fun getChildBounds(
diff --git a/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java b/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java
index eeacee1..32d9052 100644
--- a/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java
+++ b/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java
@@ -243,54 +243,54 @@
}
@Override
- public Pair<Float, Float> getDwbLayoutTranslations(int taskViewWidth,
- int taskViewHeight, SplitBounds splitBounds, DeviceProfile deviceProfile,
- View[] thumbnailViews, int desiredTaskId, View banner) {
- float translationX = 0;
- float translationY = 0;
+ public void updateDwbBannerLayout(int taskViewWidth, int taskViewHeight,
+ boolean isGroupedTaskView, @NonNull DeviceProfile deviceProfile,
+ int snapshotViewWidth, int snapshotViewHeight, @NonNull View banner) {
FrameLayout.LayoutParams bannerParams = (FrameLayout.LayoutParams) banner.getLayoutParams();
banner.setPivotX(0);
banner.setPivotY(0);
banner.setRotation(getDegreesRotated());
- if (splitBounds == null) {
- // Single, fullscreen case
+ if (isGroupedTaskView) {
+ bannerParams.gravity =
+ BOTTOM | (deviceProfile.isLeftRightSplit ? START : CENTER_HORIZONTAL);
+ bannerParams.width = snapshotViewWidth;
+ } else {
bannerParams.width = MATCH_PARENT;
bannerParams.gravity = BOTTOM | CENTER_HORIZONTAL;
- return new Pair<>(translationX, translationY);
}
+ banner.setLayoutParams(bannerParams);
+ }
- bannerParams.gravity =
- BOTTOM | (deviceProfile.isLeftRightSplit ? START : CENTER_HORIZONTAL);
-
- // Set correct width
- if (desiredTaskId == splitBounds.leftTopTaskId) {
- bannerParams.width = thumbnailViews[0].getMeasuredWidth();
- } else {
- bannerParams.width = thumbnailViews[1].getMeasuredWidth();
- }
-
- // Set translations
- if (deviceProfile.isLeftRightSplit) {
- if (desiredTaskId == splitBounds.rightBottomTaskId) {
- float leftTopTaskPercent = splitBounds.appsStackedVertically
- ? splitBounds.topTaskPercent
- : splitBounds.leftTaskPercent;
- float dividerThicknessPercent = splitBounds.appsStackedVertically
- ? splitBounds.dividerHeightPercent
- : splitBounds.dividerWidthPercent;
- translationX = ((taskViewWidth * leftTopTaskPercent)
- + (taskViewWidth * dividerThicknessPercent));
- }
- } else {
- if (desiredTaskId == splitBounds.leftTopTaskId) {
- FrameLayout.LayoutParams snapshotParams =
- (FrameLayout.LayoutParams) thumbnailViews[0]
- .getLayoutParams();
- float bottomRightTaskPlusDividerPercent = splitBounds.appsStackedVertically
- ? (1f - splitBounds.topTaskPercent)
- : (1f - splitBounds.leftTaskPercent);
- translationY = -((taskViewHeight - snapshotParams.topMargin)
- * bottomRightTaskPlusDividerPercent);
+ @NonNull
+ @Override
+ public Pair<Float, Float> getDwbBannerTranslations(int taskViewWidth,
+ int taskViewHeight, SplitBounds splitBounds, @NonNull DeviceProfile deviceProfile,
+ @NonNull View[] thumbnailViews, int desiredTaskId, @NonNull View banner) {
+ float translationX = 0;
+ float translationY = 0;
+ if (splitBounds != null) {
+ if (deviceProfile.isLeftRightSplit) {
+ if (desiredTaskId == splitBounds.rightBottomTaskId) {
+ float leftTopTaskPercent = splitBounds.appsStackedVertically
+ ? splitBounds.topTaskPercent
+ : splitBounds.leftTaskPercent;
+ float dividerThicknessPercent = splitBounds.appsStackedVertically
+ ? splitBounds.dividerHeightPercent
+ : splitBounds.dividerWidthPercent;
+ translationX = ((taskViewWidth * leftTopTaskPercent)
+ + (taskViewWidth * dividerThicknessPercent));
+ }
+ } else {
+ if (desiredTaskId == splitBounds.leftTopTaskId) {
+ FrameLayout.LayoutParams snapshotParams =
+ (FrameLayout.LayoutParams) thumbnailViews[0]
+ .getLayoutParams();
+ float bottomRightTaskPlusDividerPercent = splitBounds.appsStackedVertically
+ ? (1f - splitBounds.topTaskPercent)
+ : (1f - splitBounds.leftTaskPercent);
+ translationY = -((taskViewHeight - snapshotParams.topMargin)
+ * bottomRightTaskPlusDividerPercent);
+ }
}
}
return new Pair<>(translationX, translationY);
diff --git a/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.kt b/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.kt
index df4b030..06a0685 100644
--- a/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.kt
+++ b/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.kt
@@ -199,6 +199,7 @@
parentWidth: Int,
parentHeight: Int
): Pair<Point, Point>
+
// Overview TaskMenuView methods
/** Sets layout params on a task's app icon. Only use this when app chip is disabled. */
fun setTaskIconParams(
@@ -294,13 +295,24 @@
deviceProfile: DeviceProfile
)
+ /** Layout a Digital Wellbeing Banner on its parent. TaskView. */
+ fun updateDwbBannerLayout(
+ taskViewWidth: Int,
+ taskViewHeight: Int,
+ isGroupedTaskView: Boolean,
+ deviceProfile: DeviceProfile,
+ snapshotViewWidth: Int,
+ snapshotViewHeight: Int,
+ banner: View
+ )
+
/**
- * Calculates the position where a Digital Wellbeing Banner should be placed on its parent
+ * Calculates the translations where a Digital Wellbeing Banner should be apply on its parent
* TaskView.
*
* @return A Pair of Floats representing the proper x and y translations.
*/
- fun getDwbLayoutTranslations(
+ fun getDwbBannerTranslations(
taskViewWidth: Int,
taskViewHeight: Int,
splitBounds: SplitConfigurationOptions.SplitBounds?,
@@ -309,6 +321,7 @@
desiredTaskId: Int,
banner: View
): Pair<Float, Float>
+
// The following are only used by TaskViewTouchHandler.
/** @return Either VERTICAL or HORIZONTAL. */
diff --git a/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt b/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt
index 333359f..a972e8c 100644
--- a/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt
+++ b/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt
@@ -28,6 +28,7 @@
import android.widget.FrameLayout
import androidx.core.util.component1
import androidx.core.util.component2
+import androidx.core.view.updateLayoutParams
import com.android.launcher3.DeviceProfile
import com.android.launcher3.Flags
import com.android.launcher3.R
@@ -125,7 +126,30 @@
}
}
- override fun getDwbLayoutTranslations(
+ override fun updateDwbBannerLayout(
+ taskViewWidth: Int,
+ taskViewHeight: Int,
+ isGroupedTaskView: Boolean,
+ deviceProfile: DeviceProfile,
+ snapshotViewWidth: Int,
+ snapshotViewHeight: Int,
+ banner: View
+ ) {
+ banner.pivotX = 0f
+ banner.pivotY = 0f
+ banner.rotation = degreesRotated
+ banner.updateLayoutParams<FrameLayout.LayoutParams> {
+ gravity = Gravity.BOTTOM or if (banner.isLayoutRtl) Gravity.END else Gravity.START
+ width =
+ if (isGroupedTaskView) {
+ snapshotViewHeight
+ } else {
+ taskViewHeight - deviceProfile.overviewTaskThumbnailTopMarginPx
+ }
+ }
+ }
+
+ override fun getDwbBannerTranslations(
taskViewWidth: Int,
taskViewHeight: Int,
splitBounds: SplitBounds?,
@@ -135,39 +159,26 @@
banner: View
): Pair<Float, Float> {
val snapshotParams = thumbnailViews[0].layoutParams as FrameLayout.LayoutParams
- val isRtl = banner.layoutDirection == View.LAYOUT_DIRECTION_RTL
-
- val bannerParams = banner.layoutParams as FrameLayout.LayoutParams
- bannerParams.gravity = Gravity.BOTTOM or if (isRtl) Gravity.END else Gravity.START
- banner.pivotX = 0f
- banner.pivotY = 0f
- banner.rotation = degreesRotated
-
val translationX: Float = (taskViewWidth - banner.height).toFloat()
- if (splitBounds == null) {
- // Single, fullscreen case
- bannerParams.width = taskViewHeight - snapshotParams.topMargin
- return Pair(translationX, banner.height.toFloat())
- }
-
- // Set correct width and translations
val translationY: Float
- if (desiredTaskId == splitBounds.leftTopTaskId) {
- bannerParams.width = thumbnailViews[0].measuredHeight
- val bottomRightTaskPlusDividerPercent =
- if (splitBounds.appsStackedVertically) {
- 1f - splitBounds.topTaskPercent
- } else {
- 1f - splitBounds.leftTaskPercent
- }
- translationY =
- banner.height -
- (taskViewHeight - snapshotParams.topMargin) * bottomRightTaskPlusDividerPercent
- } else {
- bannerParams.width = thumbnailViews[1].measuredHeight
+ if (splitBounds == null) {
translationY = banner.height.toFloat()
+ } else {
+ if (desiredTaskId == splitBounds.leftTopTaskId) {
+ val bottomRightTaskPlusDividerPercent =
+ if (splitBounds.appsStackedVertically) {
+ 1f - splitBounds.topTaskPercent
+ } else {
+ 1f - splitBounds.leftTaskPercent
+ }
+ translationY =
+ banner.height -
+ (taskViewHeight - snapshotParams.topMargin) *
+ bottomRightTaskPlusDividerPercent
+ } else {
+ translationY = banner.height.toFloat()
+ }
}
-
return Pair(translationX, translationY)
}
@@ -339,6 +350,7 @@
if (isRtl) displacement > 0 else displacement < 0
override fun getTaskDragDisplacementFactor(isRtl: Boolean): Int = if (isRtl) -1 else 1
+
/* -------------------- */
override fun getSplitIconsPosition(
diff --git a/quickstep/src/com/android/quickstep/recents/data/HighResLoadingStateNotifier.kt b/quickstep/src/com/android/quickstep/recents/data/HighResLoadingStateNotifier.kt
new file mode 100644
index 0000000..df546ca
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/recents/data/HighResLoadingStateNotifier.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.recents.data
+
+import com.android.quickstep.TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback
+
+/** Notifies added callbacks that high res state has changed */
+interface HighResLoadingStateNotifier {
+ /** Adds a callback for high res loading state */
+ fun addCallback(callback: HighResLoadingStateChangedCallback)
+
+ /** Removes a callback for high res loading state */
+ fun removeCallback(callback: HighResLoadingStateChangedCallback)
+}
diff --git a/quickstep/src/com/android/quickstep/recents/data/RecentTasksRepository.kt b/quickstep/src/com/android/quickstep/recents/data/RecentTasksRepository.kt
index 4f7a541..9c4248c 100644
--- a/quickstep/src/com/android/quickstep/recents/data/RecentTasksRepository.kt
+++ b/quickstep/src/com/android/quickstep/recents/data/RecentTasksRepository.kt
@@ -41,10 +41,4 @@
* populated e.g. icons/thumbnails etc.
*/
fun setVisibleTasks(visibleTaskIdList: List<Int>)
-
- /**
- * Override [ThumbnailData] with a map of taskId to [ThumbnailData]. The override only applies
- * if the tasks are already visible, and will be invalidated when tasks become invisible.
- */
- fun addOrUpdateThumbnailOverride(thumbnailOverride: Map<Int, ThumbnailData>)
}
diff --git a/quickstep/src/com/android/quickstep/recents/data/TaskVisualsChangeNotifier.kt b/quickstep/src/com/android/quickstep/recents/data/TaskVisualsChangeNotifier.kt
new file mode 100644
index 0000000..6e7789d
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/recents/data/TaskVisualsChangeNotifier.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.recents.data
+
+import com.android.quickstep.util.TaskVisualsChangeListener
+
+/** Notifies added listeners that task visuals have changed */
+interface TaskVisualsChangeNotifier {
+ /** Adds a listener for visuals changes */
+ fun addThumbnailChangeListener(listener: TaskVisualsChangeListener)
+
+ /** Removes a listener for visuals changes */
+ fun removeThumbnailChangeListener(listener: TaskVisualsChangeListener)
+}
diff --git a/quickstep/src/com/android/quickstep/recents/data/TaskVisualsChangedDelegate.kt b/quickstep/src/com/android/quickstep/recents/data/TaskVisualsChangedDelegate.kt
new file mode 100644
index 0000000..a141e89
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/recents/data/TaskVisualsChangedDelegate.kt
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.recents.data
+
+import android.os.UserHandle
+import com.android.quickstep.TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback
+import com.android.quickstep.recents.data.TaskVisualsChangedDelegate.TaskIconChangedCallback
+import com.android.quickstep.recents.data.TaskVisualsChangedDelegate.TaskThumbnailChangedCallback
+import com.android.quickstep.util.TaskVisualsChangeListener
+import com.android.systemui.shared.recents.model.Task
+import com.android.systemui.shared.recents.model.ThumbnailData
+
+/** Delegates the checking of task visuals (thumbnails, high res changes, icons) */
+interface TaskVisualsChangedDelegate :
+ TaskVisualsChangeListener, HighResLoadingStateChangedCallback {
+ /** Registers a callback for visuals relating to icons */
+ fun registerTaskIconChangedCallback(
+ taskKey: Task.TaskKey,
+ taskIconChangedCallback: TaskIconChangedCallback
+ )
+
+ /** Unregisters a callback for visuals relating to icons */
+ fun unregisterTaskIconChangedCallback(taskKey: Task.TaskKey)
+
+ /** Registers a callback for visuals relating to thumbnails */
+ fun registerTaskThumbnailChangedCallback(
+ taskKey: Task.TaskKey,
+ taskThumbnailChangedCallback: TaskThumbnailChangedCallback
+ )
+
+ /** Unregisters a callback for visuals relating to thumbnails */
+ fun unregisterTaskThumbnailChangedCallback(taskKey: Task.TaskKey)
+
+ /** A callback for task icon changes */
+ interface TaskIconChangedCallback {
+ /** Informs the listener that the task icon has changed */
+ fun onTaskIconChanged()
+ }
+
+ /** A callback for task thumbnail changes */
+ interface TaskThumbnailChangedCallback {
+ /** Informs the listener that the task thumbnail data has changed to [thumbnailData] */
+ fun onTaskThumbnailChanged(thumbnailData: ThumbnailData?)
+
+ /** Informs the listener that the default resolution for loading thumbnails has changed */
+ fun onHighResLoadingStateChanged()
+ }
+}
+
+class TaskVisualsChangedDelegateImpl(
+ private val taskVisualsChangeNotifier: TaskVisualsChangeNotifier,
+ private val highResLoadingStateNotifier: HighResLoadingStateNotifier,
+) : TaskVisualsChangedDelegate {
+ private val taskIconChangedCallbacks =
+ mutableMapOf<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
+ }
+
+ override fun onTaskIconChanged(taskId: Int) {
+ taskIconChangedCallbacks[taskId]?.let { (_, callback) -> callback.onTaskIconChanged() }
+ }
+
+ override fun onTaskIconChanged(pkg: String, user: UserHandle) {
+ taskIconChangedCallbacks.values
+ .filter { (taskKey, _) ->
+ pkg == taskKey.packageName && user.identifier == taskKey.userId
+ }
+ .forEach { (_, callback) -> callback.onTaskIconChanged() }
+ }
+
+ override fun onTaskThumbnailChanged(taskId: Int, thumbnailData: ThumbnailData?): Task? {
+ taskThumbnailChangedCallbacks[taskId]?.let { (_, callback) ->
+ callback.onTaskThumbnailChanged(thumbnailData)
+ }
+ return null
+ }
+
+ override fun onHighResLoadingStateChanged(enabled: Boolean) {
+ taskThumbnailChangedCallbacks.values.forEach { (_, callback) ->
+ callback.onHighResLoadingStateChanged()
+ }
+ }
+
+ override fun registerTaskIconChangedCallback(
+ taskKey: Task.TaskKey,
+ taskIconChangedCallback: TaskIconChangedCallback
+ ) {
+ taskIconChangedCallbacks[taskKey.id] = taskKey to taskIconChangedCallback
+ onCallbackRegistered()
+ }
+
+ override fun unregisterTaskIconChangedCallback(taskKey: Task.TaskKey) {
+ taskIconChangedCallbacks.remove(taskKey.id)
+ onCallbackUnregistered()
+ }
+
+ override fun registerTaskThumbnailChangedCallback(
+ taskKey: Task.TaskKey,
+ taskThumbnailChangedCallback: TaskThumbnailChangedCallback
+ ) {
+ taskThumbnailChangedCallbacks[taskKey.id] = taskKey to taskThumbnailChangedCallback
+ onCallbackRegistered()
+ }
+
+ override fun unregisterTaskThumbnailChangedCallback(taskKey: Task.TaskKey) {
+ taskThumbnailChangedCallbacks.remove(taskKey.id)
+ onCallbackUnregistered()
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt b/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
index 6f9d157..eb3c2d1 100644
--- a/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
+++ b/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
@@ -18,6 +18,8 @@
import android.graphics.drawable.Drawable
import com.android.launcher3.util.coroutines.DispatcherProvider
+import com.android.quickstep.recents.data.TaskVisualsChangedDelegate.TaskIconChangedCallback
+import com.android.quickstep.recents.data.TaskVisualsChangedDelegate.TaskThumbnailChangedCallback
import com.android.quickstep.task.thumbnail.data.TaskIconDataSource
import com.android.quickstep.task.thumbnail.data.TaskThumbnailDataSource
import com.android.quickstep.util.GroupTask
@@ -26,18 +28,20 @@
import kotlin.coroutines.resume
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.distinctUntilChangedBy
import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext
@@ -46,12 +50,12 @@
private val recentsModel: RecentTasksDataSource,
private val taskThumbnailDataSource: TaskThumbnailDataSource,
private val taskIconDataSource: TaskIconDataSource,
+ private val taskVisualsChangedDelegate: TaskVisualsChangedDelegate,
recentsCoroutineScope: CoroutineScope,
private val dispatcherProvider: DispatcherProvider,
) : RecentTasksRepository {
private val groupedTaskData = MutableStateFlow(emptyList<GroupTask>())
private val visibleTaskIds = MutableStateFlow(emptySet<Int>())
- private val thumbnailOverride = MutableStateFlow(mapOf<Int, ThumbnailData>())
private val taskData =
groupedTaskData.map { groupTaskList -> groupTaskList.flatMap { it.tasks } }
@@ -85,15 +89,13 @@
.distinctUntilChanged()
private val augmentedTaskData: Flow<List<Task>> =
- combine(taskData, thumbnailQueryResults, iconQueryResults, thumbnailOverride) {
+ combine(taskData, thumbnailQueryResults, iconQueryResults) {
tasks,
thumbnailQueryResults,
- iconQueryResults,
- thumbnailOverride ->
+ iconQueryResults ->
tasks.onEach { task ->
// Add retrieved thumbnails + remove unnecessary thumbnails (e.g. invisible)
- task.thumbnail =
- thumbnailOverride[task.key.id] ?: thumbnailQueryResults[task.key.id]
+ task.thumbnail = thumbnailQueryResults[task.key.id]
// TODO(b/352331675) don't load icons for DesktopTaskView
// Add retrieved icons + remove unnecessary icons
@@ -121,61 +123,76 @@
override fun setVisibleTasks(visibleTaskIdList: List<Int>) {
this.visibleTaskIds.value = visibleTaskIdList.toSet()
- addOrUpdateThumbnailOverride(emptyMap())
- }
-
- override fun addOrUpdateThumbnailOverride(thumbnailOverride: Map<Int, ThumbnailData>) {
- this.thumbnailOverride.value =
- this.thumbnailOverride.value
- .toMutableMap()
- .apply { putAll(thumbnailOverride) }
- .filterKeys(this.visibleTaskIds.value::contains)
}
/** Flow wrapper for [TaskThumbnailDataSource.getThumbnailInBackground] api */
- private fun getThumbnailDataRequest(task: Task): ThumbnailDataRequest = flow {
- emit(task.key.id to task.thumbnail)
- val thumbnailDataResult: ThumbnailData? =
- withContext(dispatcherProvider.main) {
- suspendCancellableCoroutine { continuation ->
- val cancellableTask =
- taskThumbnailDataSource.getThumbnailInBackground(task) {
- continuation.resume(it)
- }
- continuation.invokeOnCancellation { cancellableTask?.cancel() }
+ private fun getThumbnailDataRequest(task: Task): ThumbnailDataRequest = callbackFlow {
+ trySend(task.key.id to task.thumbnail)
+ trySend(task.key.id to getThumbnailFromDataSource(task))
+
+ val callback =
+ object : TaskThumbnailChangedCallback {
+ override fun onTaskThumbnailChanged(thumbnailData: ThumbnailData?) {
+ trySend(task.key.id to thumbnailData)
+ }
+
+ override fun onHighResLoadingStateChanged() {
+ launch { trySend(task.key.id to getThumbnailFromDataSource(task)) }
}
}
- emit(task.key.id to thumbnailDataResult)
+ taskVisualsChangedDelegate.registerTaskThumbnailChangedCallback(task.key, callback)
+ awaitClose { taskVisualsChangedDelegate.unregisterTaskThumbnailChangedCallback(task.key) }
}
- /** Flow wrapper for [TaskThumbnailDataSource.getThumbnailInBackground] api */
+ /** Flow wrapper for [TaskIconDataSource.getIconInBackground] api */
private fun getIconDataRequest(task: Task): IconDataRequest =
- flow {
- emit(task.key.id to task.getTaskIconQueryResponse())
- val iconDataResponse: TaskIconQueryResponse? =
- withContext(dispatcherProvider.main) {
- suspendCancellableCoroutine { continuation ->
- val cancellableTask =
- taskIconDataSource.getIconInBackground(task) {
- icon,
- contentDescription,
- title ->
- icon.constantState?.let {
- continuation.resume(
- TaskIconQueryResponse(
- it.newDrawable().mutate(),
- contentDescription,
- title
- )
- )
- }
- }
- continuation.invokeOnCancellation { cancellableTask?.cancel() }
+ callbackFlow {
+ trySend(task.key.id to task.getTaskIconQueryResponse())
+ trySend(task.key.id to getIconFromDataSource(task))
+
+ val callback =
+ object : TaskIconChangedCallback {
+ override fun onTaskIconChanged() {
+ launch { trySend(task.key.id to getIconFromDataSource(task)) }
}
}
- emit(task.key.id to iconDataResponse)
+ taskVisualsChangedDelegate.registerTaskIconChangedCallback(task.key, callback)
+ awaitClose {
+ taskVisualsChangedDelegate.unregisterTaskIconChangedCallback(task.key)
+ }
}
.distinctUntilChanged()
+
+ private suspend fun getThumbnailFromDataSource(task: Task) =
+ withContext(dispatcherProvider.main) {
+ suspendCancellableCoroutine { continuation ->
+ val cancellableTask =
+ taskThumbnailDataSource.getThumbnailInBackground(task) {
+ continuation.resume(it)
+ }
+ continuation.invokeOnCancellation { cancellableTask?.cancel() }
+ }
+ }
+
+ private suspend fun getIconFromDataSource(task: Task) =
+ withContext(dispatcherProvider.main) {
+ suspendCancellableCoroutine { continuation ->
+ val cancellableTask =
+ taskIconDataSource.getIconInBackground(task) { icon, contentDescription, title
+ ->
+ icon.constantState?.let {
+ continuation.resume(
+ TaskIconQueryResponse(
+ it.newDrawable().mutate(),
+ contentDescription,
+ title
+ )
+ )
+ }
+ }
+ continuation.invokeOnCancellation { cancellableTask?.cancel() }
+ }
+ }
}
data class TaskIconQueryResponse(
diff --git a/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt b/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
index 0b672d1..0a5544f 100644
--- a/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
+++ b/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
@@ -22,6 +22,8 @@
import com.android.launcher3.util.coroutines.ProductionDispatchers
import com.android.quickstep.RecentsModel
import com.android.quickstep.recents.data.RecentTasksRepository
+import com.android.quickstep.recents.data.TaskVisualsChangedDelegate
+import com.android.quickstep.recents.data.TaskVisualsChangedDelegateImpl
import com.android.quickstep.recents.data.TasksRepository
import com.android.quickstep.recents.usecase.GetThumbnailPositionUseCase
import com.android.quickstep.recents.usecase.GetThumbnailUseCase
@@ -60,14 +62,22 @@
val recentsCoroutineScope =
CoroutineScope(SupervisorJob() + Dispatchers.Main + CoroutineName("RecentsView"))
set(CoroutineScope::class.java.simpleName, recentsCoroutineScope)
+ val recentsModel = RecentsModel.INSTANCE.get(appContext)
+ val taskVisualsChangedDelegate =
+ TaskVisualsChangedDelegateImpl(
+ recentsModel,
+ recentsModel.thumbnailCache.highResLoadingState
+ )
+ set(TaskVisualsChangedDelegate::class.java.simpleName, taskVisualsChangedDelegate)
// Create RecentsTaskRepository singleton
val recentTasksRepository: RecentTasksRepository =
- with(RecentsModel.INSTANCE.get(appContext)) {
+ with(recentsModel) {
TasksRepository(
this,
thumbnailCache,
iconCache,
+ taskVisualsChangedDelegate,
recentsCoroutineScope,
ProductionDispatchers
)
@@ -155,6 +165,7 @@
thumbnailCache,
iconCache,
get(),
+ get(),
ProductionDispatchers
)
}
diff --git a/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewModel.kt b/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewModel.kt
index b1f46a3..1716f2e 100644
--- a/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewModel.kt
+++ b/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewModel.kt
@@ -58,10 +58,6 @@
recentsViewData.thumbnailSplashProgress.value = taskThumbnailSplashAlpha
}
- fun addOrUpdateThumbnailOverride(thumbnailOverride: Map<Int, ThumbnailData>) {
- recentsTasksRepository.addOrUpdateThumbnailOverride(thumbnailOverride)
- }
-
suspend fun waitForThumbnailsToUpdate(updatedThumbnails: Map<Int, ThumbnailData>) {
combine(
updatedThumbnails.map {
diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
index ec1eeb1..b9338a3 100644
--- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java
+++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
@@ -28,6 +28,8 @@
public class LayoutUtils {
+ private static final float SQUARE_ASPECT_RATIO_TOLERANCE = 0.05f;
+
/**
* The height for the swipe up motion
*/
@@ -61,4 +63,13 @@
}
}
}
+
+ /**
+ * Returns true iff the device's aspect ratio is within
+ * {@link LayoutUtils#SQUARE_ASPECT_RATIO_TOLERANCE} of 1:1
+ */
+ public static boolean isAspectRatioSquare(float aspectRatio) {
+ return Float.compare(aspectRatio, 1f - SQUARE_ASPECT_RATIO_TOLERANCE) >= 0
+ && Float.compare(aspectRatio, 1f + SQUARE_ASPECT_RATIO_TOLERANCE) <= 0;
+ }
}
diff --git a/quickstep/src/com/android/quickstep/util/RecentsViewUtils.kt b/quickstep/src/com/android/quickstep/util/RecentsViewUtils.kt
index 0a01d8b..cf08391 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsViewUtils.kt
+++ b/quickstep/src/com/android/quickstep/util/RecentsViewUtils.kt
@@ -17,9 +17,11 @@
package com.android.quickstep.util
import com.android.launcher3.Flags.enableLargeDesktopWindowingTile
+import com.android.quickstep.RecentsAnimationController
import com.android.quickstep.views.DesktopTaskView
import com.android.quickstep.views.TaskView
import com.android.quickstep.views.TaskViewType
+import com.android.systemui.shared.recents.model.ThumbnailData
/**
* Helper class for [com.android.quickstep.views.RecentsView]. This util class contains refactored
@@ -68,4 +70,12 @@
if (taskView?.isLargeTile == true) taskView else null
}
}
+
+ fun screenshotTasks(
+ taskView: TaskView,
+ recentsAnimationController: RecentsAnimationController
+ ): Map<Int, ThumbnailData> =
+ taskView.taskContainers.associate {
+ it.task.key.id to recentsAnimationController.screenshotTask(it.task.key.id)
+ }
}
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index ae6757f..1af12f1 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -886,8 +886,7 @@
Log.w(TAG, "Package not found: " + packageName, e);
}
RecentsAnimationCallbacks callbacks = new RecentsAnimationCallbacks(
- SystemUiProxy.INSTANCE.get(mLauncher.getApplicationContext()),
- false /* allowMinimizeSplitScreen */);
+ SystemUiProxy.INSTANCE.get(mLauncher.getApplicationContext()));
DesktopSplitRecentsAnimationListener listener =
new DesktopSplitRecentsAnimationListener(splitPosition, taskBounds);
diff --git a/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
index 1c417eb..4c6e4ff 100644
--- a/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
@@ -84,8 +84,7 @@
return;
}
RecentsAnimationCallbacks callbacks = new RecentsAnimationCallbacks(
- SystemUiProxy.INSTANCE.get(mLauncher.getApplicationContext()),
- false /* allowMinimizeSplitScreen */);
+ SystemUiProxy.INSTANCE.get(mLauncher.getApplicationContext()));
SplitWithKeyboardShortcutRecentsAnimationListener listener =
new SplitWithKeyboardShortcutRecentsAnimationListener(leftOrTop);
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index e5c54bb..c7777d8 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -24,7 +24,6 @@
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
import static com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
-import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
import static com.android.quickstep.util.RecentsOrientedState.postDisplayRotation;
import static com.android.quickstep.util.RecentsOrientedState.preDisplayRotation;
@@ -535,21 +534,12 @@
// If mDrawsBelowRecents is unset, no reordering will be enforced.
if (mDrawsBelowRecents != null) {
- // In legacy transitions, the animation leashes remain in same hierarchy in the
- // TaskDisplayArea, so we don't want to bump the layer too high otherwise it will
- // conflict with layers that WM core positions (ie. the input consumers). For shell
- // transitions, the animation leashes are reparented to an animation container so we
- // can bump layers as needed.
- if (ENABLE_SHELL_TRANSITIONS) {
- builder.setLayer(mDrawsBelowRecents
- ? Integer.MIN_VALUE + app.prefixOrderIndex
- // 1000 is an arbitrary number to give room for multiple layers.
- : Integer.MAX_VALUE - 1000 + app.prefixOrderIndex);
- } else {
- builder.setLayer(mDrawsBelowRecents
- ? Integer.MIN_VALUE + app.prefixOrderIndex
- : 0);
- }
+ // In shell transitions, the animation leashes are reparented to an animation container
+ // so we can bump layers as needed.
+ builder.setLayer(mDrawsBelowRecents
+ ? Integer.MIN_VALUE + app.prefixOrderIndex
+ // 1000 is an arbitrary number to give room for multiple layers.
+ : Integer.MAX_VALUE - 1000 + app.prefixOrderIndex);
}
}
diff --git a/quickstep/src/com/android/quickstep/util/TaskVisualsChangeListener.java b/quickstep/src/com/android/quickstep/util/TaskVisualsChangeListener.java
index 66bff73..519ef60 100644
--- a/quickstep/src/com/android/quickstep/util/TaskVisualsChangeListener.java
+++ b/quickstep/src/com/android/quickstep/util/TaskVisualsChangeListener.java
@@ -16,6 +16,7 @@
package com.android.quickstep.util;
+import android.annotation.NonNull;
import android.os.UserHandle;
import com.android.systemui.shared.recents.model.Task;
@@ -36,7 +37,7 @@
/**
* Called when the icon for a task changes
*/
- default void onTaskIconChanged(String pkg, UserHandle user) {}
+ default void onTaskIconChanged(@NonNull String pkg, @NonNull UserHandle user) {}
/**
* Called when the icon for a task changes
diff --git a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.kt b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.kt
index 731b008..f0fdd81 100644
--- a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.kt
+++ b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.kt
@@ -15,8 +15,10 @@
*/
package com.android.quickstep.views
+import android.annotation.SuppressLint
import android.app.ActivityOptions
import android.content.ActivityNotFoundException
+import android.content.Context
import android.content.Intent
import android.content.pm.LauncherApps
import android.content.pm.LauncherApps.AppUsageLimit
@@ -27,46 +29,58 @@
import android.icu.util.MeasureUnit
import android.os.UserHandle
import android.provider.Settings
+import android.util.AttributeSet
import android.util.Log
import android.view.View
-import android.view.ViewGroup.MarginLayoutParams
import android.view.ViewOutlineProvider
import android.view.accessibility.AccessibilityNodeInfo
-import android.widget.FrameLayout
import android.widget.TextView
import androidx.annotation.StringRes
+import androidx.annotation.VisibleForTesting
import androidx.core.util.component1
import androidx.core.util.component2
-import androidx.core.view.updateLayoutParams
import com.android.launcher3.R
import com.android.launcher3.Utilities
import com.android.launcher3.util.Executors
import com.android.launcher3.util.SplitConfigurationOptions
+import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT
+import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED
+import com.android.launcher3.util.SplitConfigurationOptions.StagePosition
import com.android.quickstep.TaskUtils
import com.android.systemui.shared.recents.model.Task
import java.time.Duration
import java.util.Locale
-class DigitalWellBeingToast(
- private val container: RecentsViewContainer,
- private val taskView: TaskView
-) {
- private val launcherApps: LauncherApps? =
- container.asContext().getSystemService(LauncherApps::class.java)
+@SuppressLint("AppCompatCustomView")
+class DigitalWellBeingToast
+@JvmOverloads
+constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+ defStyleRes: Int = 0
+) : TextView(context, attrs, defStyleAttr, defStyleRes) {
+ private val recentsViewContainer =
+ RecentsViewContainer.containerFromContext<RecentsViewContainer>(context)
+
+ private val launcherApps: LauncherApps? = context.getSystemService(LauncherApps::class.java)
private val bannerHeight =
- container
- .asContext()
- .resources
- .getDimensionPixelSize(R.dimen.digital_wellbeing_toast_height)
+ context.resources.getDimensionPixelSize(R.dimen.digital_wellbeing_toast_height)
private lateinit var task: Task
+ private lateinit var taskView: TaskView
+ private lateinit var snapshotView: View
+ @StagePosition private var stagePosition = STAGE_POSITION_UNDEFINED
private var appRemainingTimeMs: Long = 0
- private var banner: View? = null
- private var oldBannerOutlineProvider: ViewOutlineProvider? = null
private var splitOffsetTranslationY = 0f
- private var splitOffsetTranslationX = 0f
+ set(value) {
+ if (field != value) {
+ field = value
+ updateTranslationY()
+ }
+ }
private var isDestroyed = false
@@ -76,65 +90,63 @@
set(value) {
if (field != value) {
field = value
- banner?.let {
- updateTranslationY()
- it.invalidateOutline()
- }
+ updateTranslationY()
}
}
+ init {
+ setOnClickListener(::openAppUsageSettings)
+ outlineProvider =
+ object : ViewOutlineProvider() {
+ override fun getOutline(view: View, outline: Outline) {
+ BACKGROUND.getOutline(view, outline)
+ val verticalTranslation = splitOffsetTranslationY - translationY
+ outline.offset(0, Math.round(verticalTranslation))
+ }
+ }
+ clipToOutline = true
+ }
+
private fun setNoLimit() {
hasLimit = false
- taskView.contentDescription = task.titleDescription
- replaceBanner(null)
+ setContentDescription(appUsageLimitTimeMs = -1, appRemainingTimeMs = -1)
+ visibility = INVISIBLE
appRemainingTimeMs = -1
}
private fun setLimit(appUsageLimitTimeMs: Long, appRemainingTimeMs: Long) {
this.appRemainingTimeMs = appRemainingTimeMs
hasLimit = true
- val toast =
- container.viewCache
- .getView<TextView>(
- R.layout.digital_wellbeing_toast,
- container.asContext(),
- taskView
- )
- .apply {
- text =
- Utilities.prefixTextWithIcon(
- container.asContext(),
- R.drawable.ic_hourglass_top,
- getBannerText()
- )
- setOnClickListener(::openAppUsageSettings)
- }
- replaceBanner(toast)
-
- taskView.contentDescription =
- getContentDescriptionForTask(task, appUsageLimitTimeMs, appRemainingTimeMs)
+ text = Utilities.prefixTextWithIcon(context, R.drawable.ic_hourglass_top, getBannerText())
+ visibility = VISIBLE
+ setContentDescription(appUsageLimitTimeMs, appRemainingTimeMs)
}
- fun initialize(task: Task) {
+ private fun setContentDescription(appUsageLimitTimeMs: Long, appRemainingTimeMs: Long) {
+ val contentDescription =
+ getContentDescriptionForTask(task, appUsageLimitTimeMs, appRemainingTimeMs)
+ snapshotView.contentDescription = contentDescription
+ }
+
+ fun initialize() {
check(!isDestroyed) { "Cannot re-initialize a destroyed toast" }
- this.task = task
+ setupTranslations()
Executors.ORDERED_BG_EXECUTOR.execute {
var usageLimit: AppUsageLimit? = null
try {
usageLimit =
launcherApps?.getAppUsageLimit(
- this.task.topComponent.packageName,
- UserHandle.of(this.task.key.userId)
+ task.topComponent.packageName,
+ UserHandle.of(task.key.userId)
)
} catch (e: Exception) {
Log.e(TAG, "Error initializing digital well being toast", e)
}
val appUsageLimitTimeMs = usageLimit?.totalUsageLimit ?: -1
val appRemainingTimeMs = usageLimit?.usageRemaining ?: -1
+
taskView.post {
- if (isDestroyed) {
- return@post
- }
+ if (isDestroyed) return@post
if (appUsageLimitTimeMs < 0 || appRemainingTimeMs < 0) {
setNoLimit()
} else {
@@ -144,20 +156,36 @@
}
}
- /** Mark the DWB toast as destroyed and remove banner from TaskView. */
+ /** Bind the DWB toast to its dependencies. */
+ fun bind(
+ task: Task,
+ taskView: TaskView,
+ snapshotView: View,
+ @StagePosition stagePosition: Int
+ ) {
+ this.task = task
+ this.taskView = taskView
+ this.snapshotView = snapshotView
+ this.stagePosition = stagePosition
+ isDestroyed = false
+ }
+
+ /** Mark the DWB toast as destroyed and hide it. */
fun destroy() {
+ visibility = INVISIBLE
isDestroyed = true
- taskView.post { replaceBanner(null) }
}
private fun getSplitBannerConfig(): SplitBannerConfig {
val splitBounds = splitBounds
return when {
- splitBounds == null || !container.deviceProfile.isTablet || taskView.isLargeTile ->
- SplitBannerConfig.SPLIT_BANNER_FULLSCREEN
+ splitBounds == null ||
+ !recentsViewContainer.deviceProfile.isTablet ||
+ taskView.isLargeTile -> SplitBannerConfig.SPLIT_BANNER_FULLSCREEN
// For portrait grid only height of task changes, not width. So we keep the text the
// same
- !container.deviceProfile.isLeftRightSplit -> SplitBannerConfig.SPLIT_GRID_BANNER_LARGE
+ !recentsViewContainer.deviceProfile.isLeftRightSplit ->
+ SplitBannerConfig.SPLIT_GRID_BANNER_LARGE
// For landscape grid, for 30% width we only show icon, otherwise show icon and time
task.key.id == splitBounds.leftTopTaskId ->
if (splitBounds.leftTaskPercent < THRESHOLD_LEFT_ICON_ONLY)
@@ -193,8 +221,7 @@
MeasureFormat.getInstance(Locale.getDefault(), MeasureFormat.FormatWidth.WIDE)
.formatMeasures(Measure(minutes, MeasureUnit.MINUTE))
// Use a specific string for usage less than one minute but non-zero.
- duration > Duration.ZERO ->
- container.asContext().getString(durationLessThanOneMinuteStringId)
+ duration > Duration.ZERO -> context.getString(durationLessThanOneMinuteStringId)
// Otherwise, return 0-minute string.
else ->
MeasureFormat.getInstance(Locale.getDefault(), MeasureFormat.FormatWidth.WIDE)
@@ -208,6 +235,7 @@
* [.SPLIT_BANNER_FULLSCREEN]
*/
@JvmOverloads
+ @VisibleForTesting
fun getBannerText(
remainingTime: Long = appRemainingTimeMs,
forContentDesc: Boolean = false
@@ -226,7 +254,7 @@
val splitBannerConfig = getSplitBannerConfig()
return when {
forContentDesc || splitBannerConfig == SplitBannerConfig.SPLIT_BANNER_FULLSCREEN ->
- container.asContext().getString(R.string.time_left_for_app, readableDuration)
+ context.getString(R.string.time_left_for_app, readableDuration)
// show no text
splitBannerConfig == SplitBannerConfig.SPLIT_GRID_BANNER_SMALL -> ""
// SPLIT_GRID_BANNER_LARGE only show time
@@ -241,7 +269,7 @@
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
try {
val options = ActivityOptions.makeScaleUpAnimation(view, 0, 0, view.width, view.height)
- container.asContext().startActivity(intent, options.toBundle())
+ context.startActivity(intent, options.toBundle())
// TODO: add WW logging on the app usage settings click.
} catch (e: ActivityNotFoundException) {
@@ -257,101 +285,79 @@
task: Task,
appUsageLimitTimeMs: Long,
appRemainingTimeMs: Long
- ): String =
+ ): String? =
if (appUsageLimitTimeMs >= 0 && appRemainingTimeMs >= 0)
- container
- .asContext()
- .getString(
- R.string.task_contents_description_with_remaining_time,
- task.titleDescription,
- getBannerText(appRemainingTimeMs, true /* forContentDesc */)
- )
+ context.getString(
+ R.string.task_contents_description_with_remaining_time,
+ task.titleDescription,
+ getBannerText(appRemainingTimeMs, true /* forContentDesc */)
+ )
else task.titleDescription
- private fun replaceBanner(view: View?) {
- resetOldBanner()
- setBanner(view)
- }
-
- private fun resetOldBanner() {
- val banner = banner ?: return
- banner.outlineProvider = oldBannerOutlineProvider
- taskView.removeView(banner)
- banner.setOnClickListener(null)
- container.viewCache.recycleView(R.layout.digital_wellbeing_toast, banner)
- }
-
- private fun setBanner(banner: View?) {
- this.banner = banner
- if (banner != null && taskView.recentsView != null) {
- setupAndAddBanner()
- setBannerOutline()
+ fun setupLayout() {
+ val snapshotWidth: Int
+ val snapshotHeight: Int
+ val splitBounds = splitBounds
+ if (splitBounds == null) {
+ snapshotWidth = taskView.layoutParams.width
+ snapshotHeight =
+ taskView.layoutParams.height -
+ recentsViewContainer.deviceProfile.overviewTaskThumbnailTopMarginPx
+ } else {
+ val groupedTaskSize =
+ taskView.pagedOrientationHandler.getGroupedTaskViewSizes(
+ recentsViewContainer.deviceProfile,
+ splitBounds,
+ taskView.layoutParams.width,
+ taskView.layoutParams.height
+ )
+ if (stagePosition == STAGE_POSITION_TOP_OR_LEFT) {
+ snapshotWidth = groupedTaskSize.first.x
+ snapshotHeight = groupedTaskSize.first.y
+ } else {
+ snapshotWidth = groupedTaskSize.second.x
+ snapshotHeight = groupedTaskSize.second.y
+ }
}
+ taskView.pagedOrientationHandler.updateDwbBannerLayout(
+ taskView.layoutParams.width,
+ taskView.layoutParams.height,
+ taskView is GroupedTaskView,
+ recentsViewContainer.deviceProfile,
+ snapshotWidth,
+ snapshotHeight,
+ this
+ )
}
- private fun setupAndAddBanner() {
- val banner = banner ?: return
- banner.updateLayoutParams<FrameLayout.LayoutParams> {
- bottomMargin =
- (taskView.firstSnapshotView.layoutParams as MarginLayoutParams).bottomMargin
- }
+ private fun setupTranslations() {
val (translationX, translationY) =
- taskView.pagedOrientationHandler.getDwbLayoutTranslations(
- taskView.measuredWidth,
- taskView.measuredHeight,
+ taskView.pagedOrientationHandler.getDwbBannerTranslations(
+ taskView.layoutParams.width,
+ taskView.layoutParams.height,
splitBounds,
- container.deviceProfile,
+ recentsViewContainer.deviceProfile,
taskView.snapshotViews,
task.key.id,
- banner
+ this
)
- splitOffsetTranslationX = translationX
- splitOffsetTranslationY = translationY
- updateTranslationY()
- updateTranslationX()
- taskView.addView(banner)
- }
-
- private fun setBannerOutline() {
- val banner = banner ?: return
- // TODO(b\273367585) to investigate why mBanner.getOutlineProvider() can be null
- val oldBannerOutlineProvider =
- if (banner.outlineProvider != null) banner.outlineProvider
- else ViewOutlineProvider.BACKGROUND
- this.oldBannerOutlineProvider = oldBannerOutlineProvider
-
- banner.outlineProvider =
- object : ViewOutlineProvider() {
- override fun getOutline(view: View, outline: Outline) {
- oldBannerOutlineProvider.getOutline(view, outline)
- val verticalTranslation = -view.translationY + splitOffsetTranslationY
- outline.offset(0, Math.round(verticalTranslation))
- }
- }
- banner.clipToOutline = true
+ this.translationX = translationX
+ this.splitOffsetTranslationY = translationY
}
private fun updateTranslationY() {
- banner?.translationY = bannerOffsetPercentage * bannerHeight + splitOffsetTranslationY
+ translationY = bannerOffsetPercentage * bannerHeight + splitOffsetTranslationY
+ invalidateOutline()
}
- private fun updateTranslationX() {
- banner?.translationX = splitOffsetTranslationX
- }
-
- fun setBannerColorTint(color: Int, amount: Float) {
- val banner = banner ?: return
+ fun setColorTint(color: Int, amount: Float) {
if (amount == 0f) {
- banner.setLayerType(View.LAYER_TYPE_NONE, null)
+ setLayerType(View.LAYER_TYPE_NONE, null)
}
val layerPaint = Paint()
layerPaint.setColorFilter(Utilities.makeColorTintingColorFilter(color, amount))
- banner.setLayerType(View.LAYER_TYPE_HARDWARE, layerPaint)
- banner.setLayerPaint(layerPaint)
- }
-
- fun setBannerVisibility(visibility: Int) {
- banner?.visibility = visibility
+ setLayerType(View.LAYER_TYPE_HARDWARE, layerPaint)
+ setLayerPaint(layerPaint)
}
private fun getAccessibilityActionId(): Int =
@@ -361,9 +367,8 @@
fun getDWBAccessibilityAction(): AccessibilityNodeInfo.AccessibilityAction? {
if (!hasLimit) return null
- val context = container.asContext()
val label =
- if ((taskView.containsMultipleTasks()))
+ if (taskView.containsMultipleTasks())
context.getString(
R.string.split_app_usage_settings,
TaskUtils.getTitle(context, task)
diff --git a/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java b/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java
index 4dde635..bdca596 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java
@@ -123,8 +123,8 @@
@Override
public void onGlobalLayout() {
if (isUninitialized()) return;
- positionViews();
- if (mOnTargetChangeRunnable != null) {
+ boolean positionsChanged = positionViews();
+ if (mOnTargetChangeRunnable != null && positionsChanged) {
mOnTargetChangeRunnable.run();
}
}
@@ -212,21 +212,43 @@
onGlobalLayout();
}
- /** Sets the layout parameters of the floating view and its background view child. */
- private void positionViews() {
+ /**
+ * Sets the layout parameters of the floating view and its background view child.
+ * @return true if any of the views positions change due to this call.
+ */
+ private boolean positionViews() {
+ boolean positionsChanged = false;
+
LayoutParams layoutParams = (LayoutParams) getLayoutParams();
- layoutParams.setMargins(0, 0, 0, 0);
- setLayoutParams(layoutParams);
+
+ if (layoutParams.topMargin != 0 || layoutParams.bottomMargin != 0
+ || layoutParams.rightMargin != 0 || layoutParams.leftMargin != 0) {
+ positionsChanged = true;
+ layoutParams.setMargins(0, 0, 0, 0);
+ setLayoutParams(layoutParams);
+ }
// FloatingWidgetView layout is forced LTR
- mBackgroundView.setTranslationX(mBackgroundPosition.left);
- mBackgroundView.setTranslationY(mBackgroundPosition.top + mIconOffsetY);
+ float targetY = mBackgroundPosition.top + mIconOffsetY;
+ if (mBackgroundView.getTranslationX() != mBackgroundPosition.left
+ || mBackgroundView.getTranslationY() != targetY) {
+ positionsChanged = true;
+ mBackgroundView.setTranslationX(mBackgroundPosition.left);
+ mBackgroundView.setTranslationY(targetY);
+ }
+
LayoutParams backgroundParams = (LayoutParams) mBackgroundView.getLayoutParams();
- backgroundParams.leftMargin = 0;
- backgroundParams.topMargin = 0;
- backgroundParams.width = (int) mBackgroundPosition.width();
- backgroundParams.height = (int) mBackgroundPosition.height();
- mBackgroundView.setLayoutParams(backgroundParams);
+ if (backgroundParams.leftMargin != 0 || backgroundParams.topMargin != 0
+ || backgroundParams.width != Math.round(mBackgroundPosition.width())
+ || backgroundParams.height != Math.round(mBackgroundPosition.height())) {
+ positionsChanged = true;
+
+ backgroundParams.leftMargin = 0;
+ backgroundParams.topMargin = 0;
+ backgroundParams.width = Math.round(mBackgroundPosition.width());
+ backgroundParams.height = Math.round(mBackgroundPosition.height());
+ mBackgroundView.setLayoutParams(backgroundParams);
+ }
if (mForegroundOverlayView != null) {
sTmpMatrix.reset();
@@ -237,8 +259,15 @@
sTmpMatrix.postScale(foregroundScale, foregroundScale);
sTmpMatrix.postTranslate(mBackgroundPosition.left, mBackgroundPosition.top
+ mIconOffsetY);
- mForegroundOverlayView.setMatrix(sTmpMatrix);
+
+ // We use the animation matrix here, because calling setMatrix on the GhostView
+ // actually sets the animation matrix, not the regular one.
+ if (!sTmpMatrix.equals(mForegroundOverlayView.getAnimationMatrix())) {
+ positionsChanged = true;
+ mForegroundOverlayView.setMatrix(sTmpMatrix);
+ }
}
+ return positionsChanged;
}
private void finish(DragLayer dragLayer) {
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt b/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
index ba4d786..4fae01e 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
@@ -115,6 +115,7 @@
R.id.snapshot,
R.id.icon,
R.id.show_windows,
+ R.id.digital_wellbeing_toast,
STAGE_POSITION_TOP_OR_LEFT,
taskOverlayFactory
),
@@ -123,6 +124,7 @@
R.id.bottomright_snapshot,
R.id.bottomRight_icon,
R.id.show_windows_right,
+ R.id.bottomRight_digital_wellbeing_toast,
STAGE_POSITION_BOTTOM_OR_RIGHT,
taskOverlayFactory
)
@@ -211,7 +213,7 @@
splitBoundsConfig = splitBounds
taskContainers.forEach {
it.digitalWellBeingToast?.splitBounds = splitBoundsConfig
- it.digitalWellBeingToast?.initialize(it.task)
+ it.digitalWellBeingToast?.initialize()
}
invalidate()
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 69a9690..226ecf5 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -1079,7 +1079,6 @@
@Nullable
public Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData) {
if (enableRefactorTaskThumbnail()) {
- mHelper.onTaskThumbnailChanged(taskId, thumbnailData);
return null;
}
if (mHandleTaskStackChanges) {
@@ -1100,8 +1099,7 @@
}
@Override
- public void onTaskIconChanged(String pkg, UserHandle user) {
- // TODO(b/342560598): Listen in TaskRepository and reload.
+ public void onTaskIconChanged(@NonNull String pkg, @NonNull UserHandle user) {
for (int i = 0; i < getTaskViewCount(); i++) {
TaskView tv = requireTaskViewAt(i);
Task task = tv.getFirstTask();
@@ -2549,7 +2547,6 @@
}
if (enableRefactorTaskThumbnail()) {
- // TODO(b/342560598): Listen in TaskRepository and reload.
return;
}
@@ -3061,14 +3058,15 @@
}
private void setRunningTaskViewShowScreenshot(boolean showScreenshot) {
+ setRunningTaskViewShowScreenshot(showScreenshot, /*updatedThumbnails=*/null);
+ }
+
+ private void setRunningTaskViewShowScreenshot(boolean showScreenshot,
+ @Nullable Map<Integer, ThumbnailData> updatedThumbnails) {
mRunningTaskShowScreenshot = showScreenshot;
TaskView runningTaskView = getRunningTaskView();
if (runningTaskView != null) {
- runningTaskView.setShouldShowScreenshot(mRunningTaskShowScreenshot);
- if (!enableRefactorTaskThumbnail()) {
- runningTaskView.getTaskContainers().forEach(
- taskContainer -> taskContainer.getThumbnailViewDeprecated().refresh());
- }
+ runningTaskView.setShouldShowScreenshot(mRunningTaskShowScreenshot, updatedThumbnails);
}
if (enableRefactorTaskThumbnail()) {
mRecentsViewModel.setRunningTaskShowScreenshot(showScreenshot);
@@ -6157,20 +6155,12 @@
return;
}
+ Map<Integer, ThumbnailData> updatedThumbnails = mRecentsViewUtils.screenshotTasks(taskView,
+ mRecentsAnimationController);
if (enableRefactorTaskThumbnail()) {
- mHelper.switchToScreenshot(taskView, mRecentsAnimationController, onFinishRunnable);
+ mHelper.switchToScreenshot(taskView, updatedThumbnails, onFinishRunnable);
} else {
- setRunningTaskViewShowScreenshot(true);
- for (TaskContainer container : taskView.getTaskContainers()) {
- ThumbnailData thumbnailData =
- mRecentsAnimationController.screenshotTask(container.getTask().key.id);
- TaskThumbnailViewDeprecated thumbnailView = container.getThumbnailViewDeprecated();
- if (thumbnailData != null) {
- thumbnailView.setThumbnail(container.getTask(), thumbnailData);
- } else {
- thumbnailView.refresh();
- }
- }
+ setRunningTaskViewShowScreenshot(true, updatedThumbnails);
ViewUtils.postFrameDrawn(taskView, onFinishRunnable);
}
}
@@ -6189,8 +6179,7 @@
if (enableRefactorTaskThumbnail()) {
mHelper.switchToScreenshot(taskView, thumbnailDatas, onFinishRunnable);
} else {
- taskView.setShouldShowScreenshot(true);
- taskView.refreshThumbnails(thumbnailDatas);
+ taskView.setShouldShowScreenshot(true, thumbnailDatas);
ViewUtils.postFrameDrawn(taskView, onFinishRunnable);
}
} else {
diff --git a/quickstep/src/com/android/quickstep/views/RecentsViewModelHelper.kt b/quickstep/src/com/android/quickstep/views/RecentsViewModelHelper.kt
index 5b71da1..4604b70 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsViewModelHelper.kt
+++ b/quickstep/src/com/android/quickstep/views/RecentsViewModelHelper.kt
@@ -16,7 +16,6 @@
package com.android.quickstep.views
-import com.android.quickstep.RecentsAnimationController
import com.android.quickstep.ViewUtils
import com.android.quickstep.recents.viewmodel.RecentsViewModel
import com.android.systemui.shared.recents.model.ThumbnailData
@@ -42,18 +41,6 @@
fun switchToScreenshot(
taskView: TaskView,
- recentsAnimationController: RecentsAnimationController,
- onFinishRunnable: Runnable,
- ) {
- val updatedThumbnails =
- taskView.taskContainers.associate {
- it.task.key.id to recentsAnimationController.screenshotTask(it.task.key.id)
- }
- switchToScreenshot(taskView, updatedThumbnails, onFinishRunnable)
- }
-
- fun switchToScreenshot(
- taskView: TaskView,
updatedThumbnails: Map<Int, ThumbnailData>?,
onFinishRunnable: Runnable,
) {
@@ -68,8 +55,4 @@
ViewUtils.postFrameDrawn(taskView, onFinishRunnable)
}
}
-
- fun onTaskThumbnailChanged(taskId: Int, thumbnailData: ThumbnailData) {
- recentsViewModel.addOrUpdateThumbnailOverride(mapOf(taskId to thumbnailData))
- }
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskContainer.kt b/quickstep/src/com/android/quickstep/views/TaskContainer.kt
index af32ba2..d34a93b 100644
--- a/quickstep/src/com/android/quickstep/views/TaskContainer.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskContainer.kt
@@ -146,6 +146,7 @@
}
fun bind() {
+ digitalWellBeingToast?.bind(task, taskView, snapshotView, stagePosition)
if (enableRefactorTaskThumbnail()) {
bindThumbnailView()
} else {
@@ -171,4 +172,18 @@
thumbnailViewDeprecated.setOverlayEnabled(enabled)
}
}
+
+ fun addChildForAccessibility(outChildren: ArrayList<View>) {
+ addAccessibleChildToList(iconView.asView(), outChildren)
+ addAccessibleChildToList(snapshotView, outChildren)
+ showWindowsView?.let { addAccessibleChildToList(it, outChildren) }
+ }
+
+ private fun addAccessibleChildToList(view: View, outChildren: ArrayList<View>) {
+ if (view.includeForAccessibility()) {
+ outChildren.add(view)
+ } else {
+ view.addChildrenForAccessibility(outChildren)
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.kt b/quickstep/src/com/android/quickstep/views/TaskView.kt
index fc12b73..5614af6 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskView.kt
@@ -79,7 +79,6 @@
import com.android.launcher3.views.ActivityContext
import com.android.quickstep.RecentsModel
import com.android.quickstep.RemoteAnimationTargets
-import com.android.quickstep.TaskAnimationManager
import com.android.quickstep.TaskOverlayFactory
import com.android.quickstep.TaskViewUtils
import com.android.quickstep.orientation.RecentsPagedOrientationHandler
@@ -408,6 +407,7 @@
protected var shouldShowScreenshot = false
get() = !isRunningTask || field
+ private set
/** Enable or disable showing border on hover and focus change */
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
@@ -702,6 +702,7 @@
R.id.snapshot,
R.id.icon,
R.id.show_windows,
+ R.id.digital_wellbeing_toast,
STAGE_POSITION_UNDEFINED,
taskOverlayFactory
)
@@ -715,6 +716,7 @@
@IdRes thumbnailViewId: Int,
@IdRes iconViewId: Int,
@IdRes showWindowViewId: Int,
+ @IdRes digitalWellbeingBannerId: Int,
@StagePosition stagePosition: Int,
taskOverlayFactory: TaskOverlayFactory,
): TaskContainer {
@@ -730,6 +732,7 @@
thumbnailViewDeprecated
}
val iconView = getOrInflateIconView(iconViewId)
+ val digitalWellBeingToast = findViewById<DigitalWellBeingToast>(digitalWellbeingBannerId)!!
return TaskContainer(
this,
task,
@@ -737,7 +740,7 @@
iconView,
TransformingTouchDelegate(iconView.asView()),
stagePosition,
- DigitalWellBeingToast(container, this),
+ digitalWellBeingToast,
findViewById(showWindowViewId)!!,
taskOverlayFactory
)
@@ -775,7 +778,7 @@
protected open fun setThumbnailOrientation(orientationState: RecentsOrientedState) {
taskContainers.forEach {
it.overlay.updateOrientationState(orientationState)
- it.digitalWellBeingToast?.initialize(it.task)
+ it.digitalWellBeingToast?.initialize()
}
}
@@ -827,10 +830,8 @@
} else {
nonGridScale = 1f
boxTranslationY = 0f
- expectedWidth = if (enableOverviewIconMenu()) taskWidth else LayoutParams.MATCH_PARENT
- expectedHeight =
- if (enableOverviewIconMenu()) taskHeight + thumbnailPadding
- else LayoutParams.MATCH_PARENT
+ expectedWidth = taskWidth
+ expectedHeight = taskHeight + thumbnailPadding
}
this.nonGridScale = nonGridScale
this.boxTranslationY = boxTranslationY
@@ -847,6 +848,7 @@
taskContainers[0].snapshotView.updateLayoutParams<LayoutParams> {
topMargin = container.deviceProfile.overviewTaskThumbnailTopMarginPx
}
+ taskContainers.forEach { it.digitalWellBeingToast?.setupLayout() }
}
/** Returns the thumbnail's bounds, optionally relative to the screen. */
@@ -939,7 +941,7 @@
if (enableOverviewIconMenu()) {
setText(taskContainer.iconView, taskContainer.task.title)
}
- taskContainer.digitalWellBeingToast?.initialize(taskContainer.task)
+ taskContainer.digitalWellBeingToast?.initialize()
}
protected open fun onIconUnloaded(taskContainer: TaskContainer) {
@@ -974,7 +976,13 @@
iconView.setText(text)
}
- open fun refreshThumbnails(thumbnailDatas: Map<Int, ThumbnailData?>?) {
+ @JvmOverloads
+ open fun setShouldShowScreenshot(
+ shouldShowScreenshot: Boolean,
+ thumbnailDatas: Map<Int, ThumbnailData?>? = null
+ ) {
+ if (this.shouldShowScreenshot == shouldShowScreenshot) return
+ this.shouldShowScreenshot = shouldShowScreenshot
if (enableRefactorTaskThumbnail()) {
return
}
@@ -1044,14 +1052,12 @@
// triggered by QuickstepTransitionManager.AppLaunchAnimationRunner.
return RunnableList().also { recentsView.addSideTaskLaunchCallback(it) }
}
- if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
- // If the recents transition is running (ie. in live tile mode), then the start
- // of a new task will merge into the existing transition and it currently will
- // not be run independently, so we need to rely on the onTaskAppeared() call
- // for the new task to trigger the side launch callback to flush this runnable
- // list (which is usually flushed when the app launch animation finishes)
- recentsView.addSideTaskLaunchCallback(opts.onEndCallback)
- }
+ // If the recents transition is running (ie. in live tile mode), then the start
+ // of a new task will merge into the existing transition and it currently will
+ // not be run independently, so we need to rely on the onTaskAppeared() call
+ // for the new task to trigger the side launch callback to flush this runnable
+ // list (which is usually flushed when the app launch animation finishes)
+ recentsView.addSideTaskLaunchCallback(opts.onEndCallback)
return opts.onEndCallback
} else {
notifyTaskLaunchFailed()
@@ -1443,7 +1449,7 @@
it.thumbnailViewDeprecated.dimAlpha = amount
}
it.iconView.setIconColorTint(tintColor, amount)
- it.digitalWellBeingToast?.setBannerColorTint(tintColor, amount)
+ it.digitalWellBeingToast?.setColorTint(tintColor, amount)
}
}
@@ -1457,7 +1463,7 @@
taskContainers.forEach {
if (visibility == VISIBLE || it.task.key.id == taskId) {
it.snapshotView.visibility = visibility
- it.digitalWellBeingToast?.setBannerVisibility(visibility)
+ it.digitalWellBeingToast?.visibility = visibility
it.showWindowsView?.visibility = visibility
it.overlay.setVisibility(visibility)
}
@@ -1661,6 +1667,12 @@
return thumbnailBounds.contains(x.toInt(), y.toInt())
}
+ override fun addChildrenForAccessibility(outChildren: ArrayList<View>) {
+ (if (isLayoutRtl) taskContainers.reversed() else taskContainers).forEach {
+ it.addChildForAccessibility(outChildren)
+ }
+ }
+
companion object {
private const val TAG = "TaskView"
const val FLAG_UPDATE_ICON = 1
diff --git a/quickstep/tests/multivalentScreenshotTests/src/com/android/launcher3/taskbar/bubbles/BubbleViewScreenshotTest.kt b/quickstep/tests/multivalentScreenshotTests/src/com/android/launcher3/taskbar/bubbles/BubbleViewScreenshotTest.kt
index d77ac5c..eb459ff 100644
--- a/quickstep/tests/multivalentScreenshotTests/src/com/android/launcher3/taskbar/bubbles/BubbleViewScreenshotTest.kt
+++ b/quickstep/tests/multivalentScreenshotTests/src/com/android/launcher3/taskbar/bubbles/BubbleViewScreenshotTest.kt
@@ -25,7 +25,7 @@
import android.view.LayoutInflater
import androidx.test.core.app.ApplicationProvider
import com.android.launcher3.R
-import com.android.wm.shell.common.bubbles.BubbleInfo
+import com.android.wm.shell.shared.bubbles.BubbleInfo
import com.google.android.apps.nexuslauncher.imagecomparison.goldenpathmanager.ViewScreenshotGoldenPathManager
import org.junit.Rule
import org.junit.Test
@@ -99,7 +99,7 @@
val flags =
if (suppressNotification) Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION else 0
val bubbleInfo =
- BubbleInfo("key", flags, null, null, 0, context.packageName, null, null, false)
+ BubbleInfo("key", flags, null, null, 0, context.packageName, null, null, false, true)
val bubbleView = inflater.inflate(R.layout.bubblebar_item_view, null) as BubbleView
val dotPath =
PathParser.createPathFromPathData(
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarDesktopModeControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarDesktopModeControllerTest.kt
new file mode 100644
index 0000000..72bbfc9
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarDesktopModeControllerTest.kt
@@ -0,0 +1,53 @@
+/*
+ * 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 androidx.test.platform.app.InstrumentationRegistry
+import com.android.launcher3.taskbar.TaskbarBackgroundRenderer.Companion.MAX_ROUNDNESS
+import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule
+import com.android.launcher3.taskbar.rules.TaskbarWindowSandboxContext
+import com.android.launcher3.util.LauncherMultivalentJUnit
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(LauncherMultivalentJUnit::class)
+@LauncherMultivalentJUnit.EmulatedDevices(["pixelFoldable2023", "pixelTablet2023"])
+class TaskbarDesktopModeControllerTest {
+
+ private val context =
+ TaskbarWindowSandboxContext.create(
+ InstrumentationRegistry.getInstrumentation().targetContext
+ )
+
+ @get:Rule(order = 0) val taskbarUnitTestRule = TaskbarUnitTestRule(this, context)
+
+ @TaskbarUnitTestRule.InjectController
+ lateinit var taskbarDesktopModeController: TaskbarDesktopModeController
+
+ @Test
+ fun whenTaskbarRequiresCornerRoundness_shouldReturnDefaultCornerRoundness() {
+ assertThat(taskbarDesktopModeController.getTaskbarCornerRoundness(true))
+ .isEqualTo(MAX_ROUNDNESS)
+ }
+
+ @Test
+ fun whenTaskbarRequiresCornerRoundness_shouldReturnZeroAsCornerRoundness() {
+ assertThat(taskbarDesktopModeController.getTaskbarCornerRoundness(false)).isEqualTo(0f)
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/BubbleViewTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/BubbleViewTest.kt
index 8bad3b9..94f9cf5 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/BubbleViewTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/BubbleViewTest.kt
@@ -27,7 +27,7 @@
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.launcher3.R
-import com.android.wm.shell.common.bubbles.BubbleInfo
+import com.android.wm.shell.shared.bubbles.BubbleInfo
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
@@ -65,7 +65,7 @@
overflowView.setOverflow(BubbleBarOverflow(overflowView), bitmap)
val bubbleInfo =
- BubbleInfo("key", 0, null, null, 0, context.packageName, null, null, false)
+ BubbleInfo("key", 0, null, null, 0, context.packageName, null, null, false, true)
bubbleView = inflater.inflate(R.layout.bubblebar_item_view, null, false) as BubbleView
bubble =
BubbleBarBubble(bubbleInfo, bubbleView, bitmap, bitmap, Color.WHITE, Path(), "")
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
index 7928ce9..84e872d 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
@@ -37,9 +37,9 @@
import com.android.launcher3.taskbar.bubbles.BubbleBarView
import com.android.launcher3.taskbar.bubbles.BubbleView
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController
-import com.android.wm.shell.common.bubbles.BubbleInfo
import com.android.wm.shell.shared.animation.PhysicsAnimator
import com.android.wm.shell.shared.animation.PhysicsAnimatorTestUtils
+import com.android.wm.shell.shared.bubbles.BubbleInfo
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Rule
@@ -870,7 +870,7 @@
bubbleBarView.addView(overflowView)
val bubbleInfo =
- BubbleInfo("key", 0, null, null, 0, context.packageName, null, null, false)
+ BubbleInfo("key", 0, null, null, 0, context.packageName, null, null, false, true)
bubbleView =
inflater.inflate(R.layout.bubblebar_item_view, bubbleBarView, false) as BubbleView
bubble =
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashControllerTest.kt
index c0a5dfa..4106a2c 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashControllerTest.kt
@@ -251,7 +251,7 @@
whenever(bubbleBarViewController.hasBubbles()).thenReturn(true)
whenever(bubbleBarViewController.bubbleBarTranslationY).thenReturn(translationY)
- whenever(bubbleBarViewController.bubbleBarScale).thenReturn(scale)
+ whenever(bubbleBarViewController.bubbleBarScaleY).thenReturn(scale)
whenever(bubbleBarViewController.bubbleBarAlpha).thenReturn(alpha)
whenever(bubbleBarViewController.bubbleBarCollapsedHeight).thenReturn(BUBBLE_BAR_HEIGHT)
}
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 b5809c2..262d8da 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
@@ -31,7 +31,6 @@
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.stashing.BubbleStashController.Companion.STASHED_BAR_SCALE
import com.android.launcher3.util.MultiValueAlpha
import com.android.wm.shell.shared.animation.PhysicsAnimator
import com.android.wm.shell.shared.animation.PhysicsAnimatorTestUtils
@@ -55,11 +54,13 @@
companion object {
const val TASKBAR_BOTTOM_SPACE = 5
+ const val BUBBLE_BAR_WIDTH = 200f
const val BUBBLE_BAR_HEIGHT = 100f
const val HOTSEAT_TRANSLATION_Y = -45f
const val TASK_BAR_TRANSLATION_Y = -TASKBAR_BOTTOM_SPACE
+ const val HANDLE_VIEW_WIDTH = 150
const val HANDLE_VIEW_HEIGHT = 4
- const val BUBBLE_BAR_STASHED_TRANSLATION_Y = 48
+ const val BUBBLE_BAR_STASHED_TRANSLATION_Y = -2.5f
}
@get:Rule val animatorTestRule: AnimatorTestRule = AnimatorTestRule(this)
@@ -76,7 +77,8 @@
private lateinit var bubbleBarView: BubbleBarView
private lateinit var stashedHandleView: StashedHandleView
private lateinit var barTranslationY: AnimatedFloat
- private lateinit var barScale: AnimatedFloat
+ private lateinit var barScaleX: AnimatedFloat
+ private lateinit var barScaleY: AnimatedFloat
private lateinit var barAlpha: MultiValueAlpha
private lateinit var stashedHandleAlpha: MultiValueAlpha
private lateinit var stashedHandleScale: AnimatedFloat
@@ -90,7 +92,7 @@
val taskbarHotseatDimensionsProvider =
DefaultDimensionsProvider(taskBarBottomSpace = TASKBAR_BOTTOM_SPACE)
mTransientBubbleStashController =
- TransientBubbleStashController(taskbarHotseatDimensionsProvider, context.resources)
+ TransientBubbleStashController(taskbarHotseatDimensionsProvider, context)
setUpBubbleBarView()
setUpBubbleBarController()
setUpStashedHandleView()
@@ -174,8 +176,8 @@
assertThat(mTransientBubbleStashController.isStashed).isTrue()
assertThat(bubbleBarView.translationY).isEqualTo(BUBBLE_BAR_STASHED_TRANSLATION_Y)
assertThat(bubbleBarView.alpha).isEqualTo(0f)
- assertThat(bubbleBarView.scaleX).isEqualTo(STASHED_BAR_SCALE)
- assertThat(bubbleBarView.scaleY).isEqualTo(STASHED_BAR_SCALE)
+ assertThat(bubbleBarView.scaleX).isEqualTo(mTransientBubbleStashController.getStashScaleX())
+ assertThat(bubbleBarView.scaleY).isEqualTo(mTransientBubbleStashController.getStashScaleY())
// Handle view is visible
assertThat(stashedHandleView.translationY).isEqualTo(0)
assertThat(stashedHandleView.alpha).isEqualTo(1)
@@ -196,7 +198,7 @@
// Then
assertThat(barTranslationY.isAnimating).isTrue()
- assertThat(barScale.isAnimating).isTrue()
+ assertThat(barScaleX.isAnimating).isTrue()
// Wait until animation ends
advanceTimeBy(BubbleStashController.BAR_STASH_DURATION)
@@ -243,8 +245,8 @@
// Then all property values are updated
assertThat(bubbleBarView.translationY).isEqualTo(BUBBLE_BAR_STASHED_TRANSLATION_Y)
assertThat(bubbleBarView.alpha).isEqualTo(0)
- assertThat(bubbleBarView.scaleX).isEqualTo(STASHED_BAR_SCALE)
- assertThat(bubbleBarView.scaleY).isEqualTo(STASHED_BAR_SCALE)
+ assertThat(bubbleBarView.scaleX).isEqualTo(mTransientBubbleStashController.getStashScaleX())
+ assertThat(bubbleBarView.scaleY).isEqualTo(mTransientBubbleStashController.getStashScaleY())
// Handle is visible at correct Y position
assertThat(stashedHandleView.alpha).isEqualTo(1)
assertThat(stashedHandleView.translationY).isEqualTo(0)
@@ -294,20 +296,16 @@
private fun setUpBubbleBarController() {
barTranslationY =
AnimatedFloat(Runnable { bubbleBarView.translationY = barTranslationY.value })
- barScale =
- AnimatedFloat(
- Runnable {
- val scale: Float = barScale.value
- bubbleBarView.scaleX = scale
- bubbleBarView.scaleY = scale
- }
- )
+ barScaleX = AnimatedFloat { value -> bubbleBarView.scaleX = value }
+ barScaleY = AnimatedFloat { value -> bubbleBarView.scaleY = value }
barAlpha = MultiValueAlpha(bubbleBarView, 1 /* num alpha channels */)
whenever(bubbleBarViewController.hasBubbles()).thenReturn(true)
whenever(bubbleBarViewController.bubbleBarTranslationY).thenReturn(barTranslationY)
- whenever(bubbleBarViewController.bubbleBarScale).thenReturn(barScale)
+ whenever(bubbleBarViewController.bubbleBarScaleX).thenReturn(barScaleX)
+ whenever(bubbleBarViewController.bubbleBarScaleY).thenReturn(barScaleY)
whenever(bubbleBarViewController.bubbleBarAlpha).thenReturn(barAlpha)
+ whenever(bubbleBarViewController.bubbleBarCollapsedWidth).thenReturn(BUBBLE_BAR_WIDTH)
whenever(bubbleBarViewController.bubbleBarCollapsedHeight).thenReturn(BUBBLE_BAR_HEIGHT)
}
@@ -317,7 +315,7 @@
stashedHandleScale =
AnimatedFloat(
Runnable {
- val scale: Float = barScale.value
+ val scale: Float = barScaleX.value
bubbleBarView.scaleX = scale
bubbleBarView.scaleY = scale
}
@@ -327,6 +325,7 @@
whenever(bubbleStashedHandleViewController.stashedHandleAlpha)
.thenReturn(stashedHandleAlpha)
whenever(bubbleStashedHandleViewController.physicsAnimator).thenReturn(stashPhysicsAnimator)
+ whenever(bubbleStashedHandleViewController.stashedWidth).thenReturn(HANDLE_VIEW_WIDTH)
whenever(bubbleStashedHandleViewController.stashedHeight).thenReturn(HANDLE_VIEW_HEIGHT)
whenever(bubbleStashedHandleViewController.setTranslationYForSwipe(any())).thenAnswer {
invocation ->
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java
new file mode 100644
index 0000000..2a0aa4c
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java
@@ -0,0 +1,276 @@
+/*
+ * 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 org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
+import android.view.ViewTreeObserver;
+
+import androidx.annotation.NonNull;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.launcher3.DeviceProfile;
+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.util.SystemUiController;
+import com.android.quickstep.util.ActivityInitListener;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.RecentsViewContainer;
+import com.android.systemui.shared.system.InputConsumerController;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.Collections;
+import java.util.HashMap;
+
+public abstract class AbsSwipeUpHandlerTestCase<
+ RECENTS_CONTAINER extends Context & RecentsViewContainer,
+ 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 =
+ InputConsumerController.getRecentsAnimationInputConsumer();
+ protected final ActivityManager.RunningTaskInfo mRunningTaskInfo =
+ new ActivityManager.RunningTaskInfo();
+ protected final TopTaskTracker.CachedTaskInfo mCachedTaskInfo =
+ new TopTaskTracker.CachedTaskInfo(Collections.singletonList(mRunningTaskInfo));
+ protected final RemoteAnimationTarget mRemoteAnimationTarget = new RemoteAnimationTarget(
+ /* taskId= */ 0,
+ /* mode= */ RemoteAnimationTarget.MODE_CLOSING,
+ /* leash= */ new SurfaceControl(),
+ /* isTranslucent= */ false,
+ /* clipRect= */ null,
+ /* contentInsets= */ null,
+ /* prefixOrderIndex= */ 0,
+ /* position= */ null,
+ /* localBounds= */ null,
+ /* screenSpaceBounds= */ null,
+ new Configuration().windowConfiguration,
+ /* isNotInRecents= */ false,
+ /* startLeash= */ null,
+ /* startBounds= */ null,
+ /* taskInfo= */ mRunningTaskInfo,
+ /* allowEnterPip= */ false);
+ protected final RecentsAnimationTargets mRecentsAnimationTargets = new RecentsAnimationTargets(
+ new RemoteAnimationTarget[] {mRemoteAnimationTarget},
+ new RemoteAnimationTarget[] {mRemoteAnimationTarget},
+ new RemoteAnimationTarget[] {mRemoteAnimationTarget},
+ /* homeContentInsets= */ new Rect(),
+ /* minimizedHomeBounds= */ null,
+ new Bundle());
+
+ @Mock protected ACTIVITY_INTERFACE mActivityInterface;
+ @Mock protected ActivityInitListener<?> mActivityInitListener;
+ @Mock protected RecentsAnimationController mRecentsAnimationController;
+ @Mock protected STATE mState;
+ @Mock protected ViewTreeObserver mViewTreeObserver;
+ @Mock protected DragLayer mDragLayer;
+ @Mock protected LauncherRootView mRootView;
+ @Mock protected SystemUiController mSystemUiController;
+ @Mock protected GestureState mGestureState;
+
+ @Rule
+ public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+ @Before
+ public void setUpRunningTaskInfo() {
+ mRunningTaskInfo.baseIntent = new Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_HOME)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ }
+
+ @Before
+ public void setUpGestureState() {
+ when(mGestureState.getRunningTask()).thenReturn(mCachedTaskInfo);
+ when(mGestureState.getLastAppearedTaskIds()).thenReturn(new int[0]);
+ when(mGestureState.getLastStartedTaskIds()).thenReturn(new int[1]);
+ when(mGestureState.getHomeIntent()).thenReturn(new Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_HOME)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ doReturn(mActivityInterface).when(mGestureState).getContainerInterface();
+ }
+
+ @Before
+ public void setUpRecentsView() {
+ RECENTS_VIEW recentsView = getRecentsView();
+ when(recentsView.getViewTreeObserver()).thenReturn(mViewTreeObserver);
+ doAnswer(answer -> {
+ runOnMainSync(() -> answer.<Runnable>getArgument(0).run());
+ return this;
+ }).when(recentsView).runOnPageScrollsInitialized(any());
+ }
+
+ @Before
+ public void setUpRecentsContainer() {
+ RecentsViewContainer recentsContainer = getRecentsContainer();
+ RECENTS_VIEW recentsView = getRecentsView();
+
+ when(recentsContainer.getDeviceProfile()).thenReturn(new DeviceProfile());
+ when(recentsContainer.getOverviewPanel()).thenReturn(recentsView);
+ when(recentsContainer.getDragLayer()).thenReturn(mDragLayer);
+ when(recentsContainer.getRootView()).thenReturn(mRootView);
+ when(recentsContainer.getSystemUiController()).thenReturn(mSystemUiController);
+ when(mActivityInterface.createActivityInitListener(any()))
+ .thenReturn(mActivityInitListener);
+ doReturn(recentsContainer).when(mActivityInterface).getCreatedContainer();
+ doAnswer(answer -> {
+ answer.<Runnable>getArgument(0).run();
+ return this;
+ }).when(recentsContainer).runOnBindToTouchInteractionService(any());
+ }
+
+ @Test
+ public void testInitWhenReady_registersActivityInitListener() {
+ String reasonString = "because i said so";
+
+ createSwipeHandler().initWhenReady(reasonString);
+ verify(mActivityInitListener).register(eq(reasonString));
+ }
+
+ @Test
+ public void testOnRecentsAnimationCanceled_unregistersActivityInitListener() {
+ createSwipeHandler()
+ .onRecentsAnimationCanceled(new HashMap<>());
+
+ runOnMainSync(() -> verify(mActivityInitListener)
+ .unregister(eq("AbsSwipeUpHandler.onRecentsAnimationCanceled")));
+ }
+
+ @Test
+ public void testOnConsumerAboutToBeSwitched_unregistersActivityInitListener() {
+ createSwipeHandler().onConsumerAboutToBeSwitched();
+
+ runOnMainSync(() -> verify(mActivityInitListener)
+ .unregister("AbsSwipeUpHandler.invalidateHandler"));
+ }
+
+ @Test
+ public void testOnConsumerAboutToBeSwitched_midQuickSwitch_unregistersActivityInitListener() {
+ createSwipeUpHandlerForGesture(GestureState.GestureEndTarget.NEW_TASK)
+ .onConsumerAboutToBeSwitched();
+
+ runOnMainSync(() -> verify(mActivityInitListener)
+ .unregister(eq("AbsSwipeUpHandler.cancelCurrentAnimation")));
+ }
+
+ @Test
+ public void testStartNewTask_finishesRecentsAnimationController() {
+ SWIPE_HANDLER absSwipeUpHandler = createSwipeHandler();
+
+ onRecentsAnimationStart(absSwipeUpHandler);
+
+ runOnMainSync(() -> {
+ absSwipeUpHandler.startNewTask(unused -> {});
+ verify(mRecentsAnimationController).finish(anyBoolean(), any());
+ });
+ }
+
+ @Test
+ public void testHomeGesture_finishesRecentsAnimationController() {
+ createSwipeUpHandlerForGesture(GestureState.GestureEndTarget.HOME);
+
+ runOnMainSync(() -> {
+ verify(mRecentsAnimationController).detachNavigationBarFromApp(true);
+ verify(mRecentsAnimationController).finish(anyBoolean(), any(), anyBoolean());
+ });
+ }
+
+ private SWIPE_HANDLER createSwipeUpHandlerForGesture(GestureState.GestureEndTarget endTarget) {
+ boolean isQuickSwitch = endTarget == GestureState.GestureEndTarget.NEW_TASK;
+
+ doReturn(mState).when(mActivityInterface).stateFromGestureEndTarget(any());
+
+ SWIPE_HANDLER swipeHandler = createSwipeHandler(SystemClock.uptimeMillis(), isQuickSwitch);
+
+ swipeHandler.onActivityInit(/* alreadyOnHome= */ false);
+ swipeHandler.onGestureStarted(isQuickSwitch);
+ onRecentsAnimationStart(swipeHandler);
+
+ when(mGestureState.getRunningTaskIds(anyBoolean())).thenReturn(new int[0]);
+ runOnMainSync(swipeHandler::switchToScreenshot);
+
+ when(mGestureState.getEndTarget()).thenReturn(endTarget);
+ when(mGestureState.isRecentsAnimationRunning()).thenReturn(isQuickSwitch);
+ float xVelocityPxPerMs = isQuickSwitch ? 100 : 0;
+ float yVelocityPxPerMs = isQuickSwitch ? 0 : -100;
+ swipeHandler.onGestureEnded(
+ yVelocityPxPerMs, new PointF(xVelocityPxPerMs, yVelocityPxPerMs));
+ swipeHandler.onCalculateEndTarget();
+ runOnMainSync(swipeHandler::onSettledOnEndTarget);
+
+ return swipeHandler;
+ }
+
+ private void onRecentsAnimationStart(SWIPE_HANDLER absSwipeUpHandler) {
+ when(mActivityInterface.getOverviewWindowBounds(any(), any())).thenReturn(new Rect());
+ doNothing().when(mActivityInterface).setOnDeferredActivityLaunchCallback(any());
+
+ runOnMainSync(() -> absSwipeUpHandler.onRecentsAnimationStart(
+ mRecentsAnimationController, mRecentsAnimationTargets));
+ }
+
+ private static void runOnMainSync(Runnable runnable) {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(runnable);
+ }
+
+ @NonNull
+ private SWIPE_HANDLER createSwipeHandler() {
+ return createSwipeHandler(SystemClock.uptimeMillis(), false);
+ }
+
+ @NonNull
+ protected abstract SWIPE_HANDLER createSwipeHandler(
+ long touchTimeMs, boolean continuingLastGesture);
+
+ @NonNull
+ protected abstract RecentsViewContainer getRecentsContainer();
+
+ @NonNull
+ protected abstract RECENTS_VIEW getRecentsView();
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/FallbackSwipeHandlerTestCase.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/FallbackSwipeHandlerTestCase.java
new file mode 100644
index 0000000..dd0b4b3
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/FallbackSwipeHandlerTestCase.java
@@ -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.quickstep;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.launcher3.util.LauncherMultivalentJUnit;
+import com.android.quickstep.fallback.FallbackRecentsView;
+import com.android.quickstep.fallback.RecentsState;
+
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+@SmallTest
+@RunWith(LauncherMultivalentJUnit.class)
+public class FallbackSwipeHandlerTestCase extends AbsSwipeUpHandlerTestCase<
+ RecentsActivity,
+ RecentsState,
+ FallbackRecentsView,
+ RecentsActivity,
+ FallbackActivityInterface,
+ FallbackSwipeHandler> {
+
+ @Mock private RecentsActivity mRecentsActivity;
+ @Mock private FallbackRecentsView mRecentsView;
+
+
+ @Override
+ protected FallbackSwipeHandler createSwipeHandler(
+ long touchTimeMs, boolean continuingLastGesture) {
+ return new FallbackSwipeHandler(
+ mContext,
+ mRecentsAnimationDeviceState,
+ mTaskAnimationManager,
+ mGestureState,
+ touchTimeMs,
+ continuingLastGesture,
+ mInputConsumerController);
+ }
+
+ @Override
+ protected RecentsActivity getRecentsContainer() {
+ return mRecentsActivity;
+ }
+
+ @Override
+ protected FallbackRecentsView getRecentsView() {
+ return mRecentsView;
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2TestCase.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2TestCase.java
new file mode 100644
index 0000000..653dc01
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2TestCase.java
@@ -0,0 +1,92 @@
+/*
+ * 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 org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.launcher3.Hotseat;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.Workspace;
+import com.android.launcher3.WorkspaceStateTransitionAnimation;
+import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.launcher3.util.LauncherMultivalentJUnit;
+import com.android.quickstep.views.RecentsView;
+
+import org.junit.Before;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+@SmallTest
+@RunWith(LauncherMultivalentJUnit.class)
+public class LauncherSwipeHandlerV2TestCase extends AbsSwipeUpHandlerTestCase<
+ QuickstepLauncher,
+ LauncherState,
+ RecentsView<QuickstepLauncher, LauncherState>,
+ QuickstepLauncher,
+ LauncherActivityInterface,
+ LauncherSwipeHandlerV2> {
+
+ @Mock private QuickstepLauncher mQuickstepLauncher;
+ @Mock private RecentsView<QuickstepLauncher, LauncherState> mRecentsView;
+ @Mock private Workspace<?> mWorkspace;
+ @Mock private Hotseat mHotseat;
+ @Mock private WorkspaceStateTransitionAnimation mTransitionAnimation;
+
+ @Before
+ public void setUpQuickStepLauncher() {
+ when(mQuickstepLauncher.createAtomicAnimationFactory())
+ .thenReturn(new AtomicAnimationFactory<>(0));
+ when(mQuickstepLauncher.getHotseat()).thenReturn(mHotseat);
+ doReturn(mWorkspace).when(mQuickstepLauncher).getWorkspace();
+ doReturn(new StateManager(mQuickstepLauncher, LauncherState.NORMAL))
+ .when(mQuickstepLauncher).getStateManager();
+
+ }
+
+ @Before
+ public void setUpWorkspace() {
+ when(mWorkspace.getStateTransitionAnimation()).thenReturn(mTransitionAnimation);
+ }
+
+ @Override
+ protected LauncherSwipeHandlerV2 createSwipeHandler(
+ long touchTimeMs, boolean continuingLastGesture) {
+ return new LauncherSwipeHandlerV2(
+ mContext,
+ mRecentsAnimationDeviceState,
+ mTaskAnimationManager,
+ mGestureState,
+ touchTimeMs,
+ continuingLastGesture,
+ mInputConsumerController);
+ }
+
+ @Override
+ protected QuickstepLauncher getRecentsContainer() {
+ return mQuickstepLauncher;
+ }
+
+ @Override
+ protected RecentsView<QuickstepLauncher, LauncherState> getRecentsView() {
+ return mRecentsView;
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeHighResLoadingStateNotifier.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeHighResLoadingStateNotifier.kt
new file mode 100644
index 0000000..7d09efd
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeHighResLoadingStateNotifier.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.quickstep.recents.data
+
+import com.android.quickstep.TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback
+
+class FakeHighResLoadingStateNotifier : HighResLoadingStateNotifier {
+ val listeners = mutableListOf<HighResLoadingStateChangedCallback>()
+
+ override fun addCallback(callback: HighResLoadingStateChangedCallback) {
+ listeners.add(callback)
+ }
+
+ override fun removeCallback(callback: HighResLoadingStateChangedCallback) {
+ listeners.remove(callback)
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskIconDataSource.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskIconDataSource.kt
index fee4979..5de876a 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskIconDataSource.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskIconDataSource.kt
@@ -27,7 +27,8 @@
class FakeTaskIconDataSource : TaskIconDataSource {
- val taskIdToDrawable: Map<Int, Drawable> = (0..10).associateWith { mockCopyableDrawable() }
+ val taskIdToDrawable: MutableMap<Int, Drawable> =
+ (0..10).associateWith { mockCopyableDrawable() }.toMutableMap()
val taskIdToUpdatingTask: MutableMap<Int, () -> Unit> = mutableMapOf()
var shouldLoadSynchronously: Boolean = true
@@ -52,15 +53,17 @@
return null
}
- private fun mockCopyableDrawable(): Drawable {
- val mutableDrawable = mock<Drawable>()
- val immutableDrawable =
- mock<Drawable>().apply { whenever(mutate()).thenReturn(mutableDrawable) }
- val constantState =
- mock<Drawable.ConstantState>().apply {
- whenever(newDrawable()).thenReturn(immutableDrawable)
- }
- return mutableDrawable.apply { whenever(this.constantState).thenReturn(constantState) }
+ companion object {
+ fun mockCopyableDrawable(): Drawable {
+ val mutableDrawable = mock<Drawable>()
+ val immutableDrawable =
+ mock<Drawable>().apply { whenever(mutate()).thenReturn(mutableDrawable) }
+ val constantState =
+ mock<Drawable.ConstantState>().apply {
+ whenever(newDrawable()).thenReturn(immutableDrawable)
+ }
+ return mutableDrawable.apply { whenever(this.constantState).thenReturn(constantState) }
+ }
}
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskThumbnailDataSource.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskThumbnailDataSource.kt
index 30fc491..d12c0b0 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskThumbnailDataSource.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskThumbnailDataSource.kt
@@ -27,7 +27,8 @@
class FakeTaskThumbnailDataSource : TaskThumbnailDataSource {
- val taskIdToBitmap: Map<Int, Bitmap> = (0..10).associateWith { mock() }
+ val taskIdToBitmap: MutableMap<Int, Bitmap> =
+ (0..10).associateWith { mock<Bitmap>() }.toMutableMap()
val taskIdToUpdatingTask: MutableMap<Int, () -> Unit> = mutableMapOf()
var shouldLoadSynchronously: Boolean = true
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskVisualsChangeNotifier.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskVisualsChangeNotifier.kt
new file mode 100644
index 0000000..765f0d1
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskVisualsChangeNotifier.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.quickstep.recents.data
+
+import com.android.quickstep.util.TaskVisualsChangeListener
+
+class FakeTaskVisualsChangeNotifier : TaskVisualsChangeNotifier {
+ val listeners = mutableListOf<TaskVisualsChangeListener>()
+
+ override fun addThumbnailChangeListener(listener: TaskVisualsChangeListener) {
+ listeners.add(listener)
+ }
+
+ override fun removeThumbnailChangeListener(listener: TaskVisualsChangeListener) {
+ listeners.remove(listener)
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTasksRepository.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTasksRepository.kt
index d94a351..7a17872 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTasksRepository.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTasksRepository.kt
@@ -28,7 +28,6 @@
private var taskIconDataMap: Map<Int, TaskIconQueryResponse> = emptyMap()
private var tasks: MutableStateFlow<List<Task>> = MutableStateFlow(emptyList())
private var visibleTasks: MutableStateFlow<List<Int>> = MutableStateFlow(emptyList())
- private var thumbnailOverrideMap: Map<Int, ThumbnailData> = emptyMap()
override fun getAllTaskData(forceRefresh: Boolean): Flow<List<Task>> = tasks
@@ -39,7 +38,7 @@
.map { taskList ->
val task = taskList.firstOrNull { it.key.id == taskId } ?: return@map null
Task(task).apply {
- thumbnail = thumbnailOverrideMap[taskId] ?: task.thumbnail
+ thumbnail = task.thumbnail
icon = task.icon
titleDescription = task.titleDescription
title = task.title
@@ -62,16 +61,6 @@
}
}
}
- setThumbnailOverrideInternal(thumbnailOverrideMap)
- }
-
- override fun addOrUpdateThumbnailOverride(thumbnailOverride: Map<Int, ThumbnailData>) {
- setThumbnailOverrideInternal(thumbnailOverride)
- }
-
- private fun setThumbnailOverrideInternal(thumbnailOverride: Map<Int, ThumbnailData>) {
- thumbnailOverrideMap =
- thumbnailOverride.filterKeys(this.visibleTasks.value::contains).toMap()
}
fun seedTasks(tasks: List<Task>) {
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TaskVisualsChangedDelegateTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TaskVisualsChangedDelegateTest.kt
new file mode 100644
index 0000000..41f6bfd
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TaskVisualsChangedDelegateTest.kt
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.recents.data
+
+import android.content.ComponentName
+import android.content.Intent
+import android.os.UserHandle
+import com.android.quickstep.recents.data.TaskVisualsChangedDelegate.TaskIconChangedCallback
+import com.android.quickstep.recents.data.TaskVisualsChangedDelegate.TaskThumbnailChangedCallback
+import com.android.systemui.shared.recents.model.Task.TaskKey
+import com.android.systemui.shared.recents.model.ThumbnailData
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.verifyNoMoreInteractions
+
+class TaskVisualsChangedDelegateTest {
+ private val taskVisualsChangeNotifier = FakeTaskVisualsChangeNotifier()
+ private val highResLoadingStateNotifier = FakeHighResLoadingStateNotifier()
+
+ val systemUnderTest =
+ TaskVisualsChangedDelegateImpl(taskVisualsChangeNotifier, highResLoadingStateNotifier)
+
+ @Test
+ fun addingFirstListener_addsListenerToNotifiers() {
+ systemUnderTest.registerTaskThumbnailChangedCallback(createTaskKey(id = 1), mock())
+
+ assertThat(taskVisualsChangeNotifier.listeners.single()).isEqualTo(systemUnderTest)
+ assertThat(highResLoadingStateNotifier.listeners.single()).isEqualTo(systemUnderTest)
+ }
+
+ @Test
+ fun addingAndRemovingListener_removesListenerFromNotifiers() {
+ systemUnderTest.registerTaskThumbnailChangedCallback(createTaskKey(id = 1), mock())
+ systemUnderTest.unregisterTaskThumbnailChangedCallback(createTaskKey(id = 1))
+
+ assertThat(taskVisualsChangeNotifier.listeners).isEmpty()
+ assertThat(highResLoadingStateNotifier.listeners).isEmpty()
+ }
+
+ @Test
+ fun addingTwoAndRemovingOneListener_doesNotRemoveListenerFromNotifiers() {
+ systemUnderTest.registerTaskThumbnailChangedCallback(createTaskKey(id = 1), mock())
+ systemUnderTest.registerTaskThumbnailChangedCallback(createTaskKey(id = 2), mock())
+ systemUnderTest.unregisterTaskThumbnailChangedCallback(createTaskKey(id = 1))
+
+ assertThat(taskVisualsChangeNotifier.listeners.single()).isEqualTo(systemUnderTest)
+ assertThat(highResLoadingStateNotifier.listeners.single()).isEqualTo(systemUnderTest)
+ }
+
+ @Test
+ fun onTaskIconChangedWithTaskId_notifiesCorrectListenerOnly() {
+ val expectedListener = mock<TaskIconChangedCallback>()
+ val additionalListener = mock<TaskIconChangedCallback>()
+ systemUnderTest.registerTaskIconChangedCallback(createTaskKey(id = 1), expectedListener)
+ systemUnderTest.registerTaskIconChangedCallback(createTaskKey(id = 2), additionalListener)
+
+ systemUnderTest.onTaskIconChanged(1)
+
+ verify(expectedListener).onTaskIconChanged()
+ verifyNoMoreInteractions(additionalListener)
+ }
+
+ @Test
+ fun onTaskIconChangedWithoutTaskId_notifiesCorrectListenerOnly() {
+ val expectedListener = mock<TaskIconChangedCallback>()
+ val listener = mock<TaskIconChangedCallback>()
+ // Correct match
+ systemUnderTest.registerTaskIconChangedCallback(
+ createTaskKey(id = 1, pkg = ALTERNATIVE_PACKAGE_NAME, userId = 1),
+ expectedListener
+ )
+ // 1 out of 2 match
+ systemUnderTest.registerTaskIconChangedCallback(
+ createTaskKey(id = 2, pkg = PACKAGE_NAME, userId = 1),
+ listener
+ )
+ systemUnderTest.registerTaskIconChangedCallback(
+ createTaskKey(id = 3, pkg = ALTERNATIVE_PACKAGE_NAME, userId = 2),
+ listener
+ )
+ // 0 out of 2 match
+ systemUnderTest.registerTaskIconChangedCallback(
+ createTaskKey(id = 4, pkg = PACKAGE_NAME, userId = 2),
+ listener
+ )
+
+ systemUnderTest.onTaskIconChanged(ALTERNATIVE_PACKAGE_NAME, UserHandle(1))
+
+ verify(expectedListener).onTaskIconChanged()
+ verifyNoMoreInteractions(listener)
+ }
+
+ @Test
+ fun replacedTaskIconChangedCallbacks_notCalled() {
+ val replacedListener = mock<TaskIconChangedCallback>()
+ val newListener = mock<TaskIconChangedCallback>()
+ systemUnderTest.registerTaskIconChangedCallback(
+ createTaskKey(id = 1, pkg = ALTERNATIVE_PACKAGE_NAME, userId = 1),
+ replacedListener
+ )
+ systemUnderTest.registerTaskIconChangedCallback(
+ createTaskKey(id = 1, pkg = ALTERNATIVE_PACKAGE_NAME, userId = 1),
+ newListener
+ )
+
+ systemUnderTest.onTaskIconChanged(ALTERNATIVE_PACKAGE_NAME, UserHandle(1))
+
+ verifyNoMoreInteractions(replacedListener)
+ verify(newListener).onTaskIconChanged()
+ }
+
+ @Test
+ fun onTaskThumbnailChanged_notifiesCorrectListenerOnly() {
+ val expectedListener = mock<TaskThumbnailChangedCallback>()
+ val additionalListener = mock<TaskThumbnailChangedCallback>()
+ val expectedThumbnailData = ThumbnailData(snapshotId = 12345)
+ systemUnderTest.registerTaskThumbnailChangedCallback(
+ createTaskKey(id = 1),
+ expectedListener
+ )
+ systemUnderTest.registerTaskThumbnailChangedCallback(
+ createTaskKey(id = 2),
+ additionalListener
+ )
+
+ systemUnderTest.onTaskThumbnailChanged(1, expectedThumbnailData)
+
+ verify(expectedListener).onTaskThumbnailChanged(expectedThumbnailData)
+ verifyNoMoreInteractions(additionalListener)
+ }
+
+ @Test
+ fun onHighResLoadingStateChanged_notifiesAllListeners() {
+ val expectedListener = mock<TaskThumbnailChangedCallback>()
+ val additionalListener = mock<TaskThumbnailChangedCallback>()
+ systemUnderTest.registerTaskThumbnailChangedCallback(
+ createTaskKey(id = 1),
+ expectedListener
+ )
+ systemUnderTest.registerTaskThumbnailChangedCallback(
+ createTaskKey(id = 2),
+ additionalListener
+ )
+
+ systemUnderTest.onHighResLoadingStateChanged(true)
+
+ verify(expectedListener).onHighResLoadingStateChanged()
+ verify(additionalListener).onHighResLoadingStateChanged()
+ }
+
+ @Test
+ fun replacedTaskThumbnailChangedCallbacks_notCalled() {
+ val replacedListener1 = mock<TaskThumbnailChangedCallback>()
+ val newListener1 = mock<TaskThumbnailChangedCallback>()
+ val expectedThumbnailData = ThumbnailData(snapshotId = 12345)
+ systemUnderTest.registerTaskThumbnailChangedCallback(
+ createTaskKey(id = 1),
+ replacedListener1
+ )
+ systemUnderTest.registerTaskThumbnailChangedCallback(createTaskKey(id = 1), newListener1)
+
+ systemUnderTest.onTaskThumbnailChanged(1, expectedThumbnailData)
+
+ verifyNoMoreInteractions(replacedListener1)
+ verify(newListener1).onTaskThumbnailChanged(expectedThumbnailData)
+ }
+
+ private fun createTaskKey(id: Int = 1, pkg: String = PACKAGE_NAME, userId: Int = 1) =
+ TaskKey(id, 0, Intent().setPackage(pkg), ComponentName("", ""), userId, 0)
+
+ private companion object {
+ const val PACKAGE_NAME = "com.test.test"
+ const val ALTERNATIVE_PACKAGE_NAME = "com.test.test2"
+ }
+}
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 e6534eb..f31467f 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt
@@ -19,7 +19,7 @@
import android.content.ComponentName
import android.content.Intent
import android.graphics.Bitmap
-import android.view.Surface
+import android.graphics.drawable.Drawable
import com.android.launcher3.util.TestDispatcherProvider
import com.android.quickstep.task.thumbnail.TaskThumbnailViewModelTest
import com.android.quickstep.util.DesktopTask
@@ -29,6 +29,9 @@
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.toList
+import kotlinx.coroutines.launch
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
@@ -48,6 +51,10 @@
private val recentsModel = FakeRecentTasksDataSource()
private val taskThumbnailDataSource = FakeTaskThumbnailDataSource()
private val taskIconDataSource = FakeTaskIconDataSource()
+ private val taskVisualsChangeNotifier = FakeTaskVisualsChangeNotifier()
+ private val highResLoadingStateNotifier = FakeHighResLoadingStateNotifier()
+ private val taskVisualsChangedDelegate =
+ TaskVisualsChangedDelegateImpl(taskVisualsChangeNotifier, highResLoadingStateNotifier)
private val dispatcher = UnconfinedTestDispatcher()
private val testScope = TestScope(dispatcher)
@@ -56,6 +63,7 @@
recentsModel,
taskThumbnailDataSource,
taskIconDataSource,
+ taskVisualsChangedDelegate,
testScope.backgroundScope,
TestDispatcherProvider(dispatcher)
)
@@ -203,87 +211,81 @@
}
@Test
- fun addThumbnailOverrideOverrideThumbnails() =
+ fun onTaskThumbnailChanged_setsNewThumbnailDataOnTask() =
testScope.runTest {
recentsModel.seedTasks(defaultTaskList)
- val bitmap1 = taskThumbnailDataSource.taskIdToBitmap[1]
- val thumbnailOverride2 = createThumbnailData()
- systemUnderTest.getAllTaskData(forceRefresh = true)
-
- systemUnderTest.setVisibleTasks(listOf(1, 2))
- systemUnderTest.addOrUpdateThumbnailOverride(mapOf(2 to thumbnailOverride2))
-
- assertThat(systemUnderTest.getThumbnailById(1).first()!!.thumbnail).isEqualTo(bitmap1)
- assertThat(systemUnderTest.getThumbnailById(2).first()!!.thumbnail)
- .isEqualTo(thumbnailOverride2.thumbnail)
- }
-
- @Test
- fun addThumbnailOverrideMultipleOverrides() =
- testScope.runTest {
- recentsModel.seedTasks(defaultTaskList)
- val thumbnailOverride1 = createThumbnailData()
- val thumbnailOverride2 = createThumbnailData()
- val thumbnailOverride3 = createThumbnailData()
- systemUnderTest.getAllTaskData(forceRefresh = true)
-
- systemUnderTest.setVisibleTasks(listOf(1, 2))
- systemUnderTest.addOrUpdateThumbnailOverride(mapOf(1 to thumbnailOverride1))
- systemUnderTest.addOrUpdateThumbnailOverride(mapOf(2 to thumbnailOverride2))
- systemUnderTest.addOrUpdateThumbnailOverride(mapOf(2 to thumbnailOverride3))
-
- assertThat(systemUnderTest.getThumbnailById(1).first()!!.thumbnail)
- .isEqualTo(thumbnailOverride1.thumbnail)
- assertThat(systemUnderTest.getThumbnailById(2).first()!!.thumbnail)
- .isEqualTo(thumbnailOverride3.thumbnail)
- }
-
- @Test
- fun addThumbnailOverrideClearedWhenTaskBecomeInvisible() =
- testScope.runTest {
- recentsModel.seedTasks(defaultTaskList)
- val bitmap2 = taskThumbnailDataSource.taskIdToBitmap[2]
- val thumbnailOverride1 = createThumbnailData()
- val thumbnailOverride2 = createThumbnailData()
- systemUnderTest.getAllTaskData(forceRefresh = true)
-
- systemUnderTest.setVisibleTasks(listOf(1, 2))
- systemUnderTest.addOrUpdateThumbnailOverride(mapOf(1 to thumbnailOverride1))
- systemUnderTest.addOrUpdateThumbnailOverride(mapOf(2 to thumbnailOverride2))
- // Making task 2 invisible and visible again should clear the override
- systemUnderTest.setVisibleTasks(listOf(1))
- systemUnderTest.setVisibleTasks(listOf(1, 2))
-
- assertThat(systemUnderTest.getThumbnailById(1).first()!!.thumbnail)
- .isEqualTo(thumbnailOverride1.thumbnail)
- assertThat(systemUnderTest.getThumbnailById(2).first()!!.thumbnail).isEqualTo(bitmap2)
- }
-
- @Test
- fun addThumbnailOverrideDoesNotOverrideInvisibleTasks() =
- testScope.runTest {
- recentsModel.seedTasks(defaultTaskList)
- val bitmap1 = taskThumbnailDataSource.taskIdToBitmap[1]
- val bitmap2 = taskThumbnailDataSource.taskIdToBitmap[2]
- val thumbnailOverride = createThumbnailData()
systemUnderTest.getAllTaskData(forceRefresh = true)
systemUnderTest.setVisibleTasks(listOf(1))
- systemUnderTest.addOrUpdateThumbnailOverride(mapOf(2 to thumbnailOverride))
- systemUnderTest.setVisibleTasks(listOf(1, 2))
- assertThat(systemUnderTest.getThumbnailById(1).first()!!.thumbnail).isEqualTo(bitmap1)
- assertThat(systemUnderTest.getThumbnailById(2).first()!!.thumbnail).isEqualTo(bitmap2)
+ val expectedThumbnailData = createThumbnailData()
+ val expectedPreviousBitmap = taskThumbnailDataSource.taskIdToBitmap[1]
+ val taskDataFlow = systemUnderTest.getTaskDataById(1)
+
+ val task1ThumbnailValues = mutableListOf<ThumbnailData?>()
+ testScope.backgroundScope.launch {
+ taskDataFlow.map { it?.thumbnail }.toList(task1ThumbnailValues)
+ }
+ taskVisualsChangedDelegate.onTaskThumbnailChanged(1, expectedThumbnailData)
+
+ assertThat(task1ThumbnailValues[1]!!.thumbnail).isEqualTo(expectedPreviousBitmap)
+ assertThat(task1ThumbnailValues.last()).isEqualTo(expectedThumbnailData)
+ }
+
+ @Test
+ fun onHighResLoadingStateChanged_setsNewThumbnailDataOnTask() =
+ testScope.runTest {
+ recentsModel.seedTasks(defaultTaskList)
+ systemUnderTest.getAllTaskData(forceRefresh = true)
+
+ systemUnderTest.setVisibleTasks(listOf(1))
+
+ val expectedBitmap = mock<Bitmap>()
+ val expectedPreviousBitmap = taskThumbnailDataSource.taskIdToBitmap[1]
+ val taskDataFlow = systemUnderTest.getTaskDataById(1)
+
+ val task1ThumbnailValues = mutableListOf<Bitmap?>()
+ testScope.backgroundScope.launch {
+ taskDataFlow.map { it?.thumbnail?.thumbnail }.toList(task1ThumbnailValues)
+ }
+ taskThumbnailDataSource.taskIdToBitmap[1] = expectedBitmap
+ taskVisualsChangedDelegate.onHighResLoadingStateChanged(true)
+
+ assertThat(task1ThumbnailValues[1]).isEqualTo(expectedPreviousBitmap)
+ assertThat(task1ThumbnailValues.last()).isEqualTo(expectedBitmap)
+ }
+
+ @Test
+ fun onTaskIconChanged_setsNewIconOnTask() =
+ testScope.runTest {
+ recentsModel.seedTasks(defaultTaskList)
+ systemUnderTest.getAllTaskData(forceRefresh = true)
+
+ systemUnderTest.setVisibleTasks(listOf(1))
+
+ val expectedIcon = FakeTaskIconDataSource.mockCopyableDrawable()
+ val expectedPreviousIcon = taskIconDataSource.taskIdToDrawable[1]
+ val taskDataFlow = systemUnderTest.getTaskDataById(1)
+
+ val task1IconValues = mutableListOf<Drawable?>()
+ testScope.backgroundScope.launch {
+ taskDataFlow.map { it?.icon }.toList(task1IconValues)
+ }
+ taskIconDataSource.taskIdToDrawable[1] = expectedIcon
+ taskVisualsChangedDelegate.onTaskIconChanged(1)
+
+ assertThat(task1IconValues[1]).isEqualTo(expectedPreviousIcon)
+ assertThat(task1IconValues.last()).isEqualTo(expectedIcon)
}
private fun createTaskWithId(taskId: Int) =
Task(Task.TaskKey(taskId, 0, Intent(), ComponentName("", ""), 0, 2000))
- private fun createThumbnailData(rotation: Int = Surface.ROTATION_0): ThumbnailData {
+ private fun createThumbnailData(): ThumbnailData {
val bitmap = mock<Bitmap>()
whenever(bitmap.width).thenReturn(TaskThumbnailViewModelTest.THUMBNAIL_WIDTH)
whenever(bitmap.height).thenReturn(TaskThumbnailViewModelTest.THUMBNAIL_HEIGHT)
- return ThumbnailData(thumbnail = bitmap, rotation = rotation)
+ return ThumbnailData(thumbnail = bitmap)
}
}
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 dc16475..fe67313 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,44 +70,6 @@
assertThat(thumbnailDataFlow2.first()).isNull()
}
- @Test
- fun thumbnailOverrideWaitAndReset() = runTest {
- val thumbnailData1 = createThumbnailData().apply { snapshotId = 1 }
- val thumbnailData2 = createThumbnailData().apply { snapshotId = 2 }
- tasksRepository.seedTasks(tasks)
- tasksRepository.seedThumbnailData(mapOf(1 to thumbnailData1, 2 to thumbnailData2))
-
- val thumbnailDataFlow1 = tasksRepository.getThumbnailById(1)
- val thumbnailDataFlow2 = tasksRepository.getThumbnailById(2)
-
- systemUnderTest.refreshAllTaskData()
- systemUnderTest.updateVisibleTasks(listOf(1, 2))
-
- assertThat(thumbnailDataFlow1.first()).isEqualTo(thumbnailData1)
- assertThat(thumbnailDataFlow2.first()).isEqualTo(thumbnailData2)
-
- systemUnderTest.setRunningTaskShowScreenshot(true)
- val thumbnailOverride = mapOf(2 to createThumbnailData().apply { snapshotId = 3 })
- systemUnderTest.addOrUpdateThumbnailOverride(thumbnailOverride)
-
- systemUnderTest.waitForRunningTaskShowScreenshotToUpdate()
- val expectedUpdate = mapOf(2 to createThumbnailData().apply { snapshotId = 3 })
- systemUnderTest.waitForThumbnailsToUpdate(expectedUpdate)
-
- assertThat(thumbnailDataFlow1.first()).isEqualTo(thumbnailData1)
- assertThat(thumbnailDataFlow2.first()?.snapshotId).isEqualTo(3)
-
- systemUnderTest.onReset()
-
- assertThat(thumbnailDataFlow1.first()).isNull()
- assertThat(thumbnailDataFlow2.first()).isNull()
-
- systemUnderTest.updateVisibleTasks(listOf(1, 2))
-
- assertThat(thumbnailDataFlow1.first()).isEqualTo(thumbnailData1)
- assertThat(thumbnailDataFlow2.first()).isEqualTo(thumbnailData2)
- }
-
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/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt
index 15b1e53..d064f4a 100644
--- a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt
@@ -57,6 +57,7 @@
@Mock lateinit var keyboardQuickSwitchController: KeyboardQuickSwitchController
@Mock lateinit var taskbarPinningController: TaskbarPinningController
@Mock lateinit var optionalBubbleControllers: Optional<BubbleControllers>
+ @Mock lateinit var taskbarDesktopModeController: TaskbarDesktopModeController
lateinit var taskbarControllers: TaskbarControllers
@@ -98,6 +99,7 @@
keyboardQuickSwitchController,
taskbarPinningController,
optionalBubbleControllers,
+ taskbarDesktopModeController
)
}
}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplOverviewIconTest.java b/quickstep/tests/src/com/android/quickstep/TaplOverviewIconTest.java
index 2087016..9bc1c59 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplOverviewIconTest.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplOverviewIconTest.java
@@ -15,20 +15,18 @@
*/
package com.android.quickstep;
-import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
-import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
-
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import android.content.Intent;
import android.platform.test.annotations.PlatinumTest;
+import com.android.launcher3.tapl.Overview;
import com.android.launcher3.tapl.OverviewTask.OverviewSplitTask;
import com.android.launcher3.tapl.OverviewTaskMenu;
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.uioverrides.QuickstepLauncher;
-import com.android.launcher3.util.rule.TestStabilityRule;
+import com.android.quickstep.util.SplitScreenTestUtils;
import org.junit.Test;
@@ -70,39 +68,17 @@
@Test
public void testSplitTaskTapBothIconMenus() {
- createAndLaunchASplitPair();
+ Overview overview = SplitScreenTestUtils.createAndLaunchASplitPairInOverview(mLauncher);
- OverviewTaskMenu taskMenu =
- mLauncher.goHome().switchToOverview().getCurrentTask().tapMenu();
+ OverviewTaskMenu taskMenu = overview.getCurrentTask().tapMenu();
assertTrue("App info item not appearing in expanded task menu.",
taskMenu.hasMenuItem("App info"));
taskMenu.touchOutsideTaskMenuToDismiss();
- OverviewTaskMenu splitMenu =
- mLauncher.goHome().switchToOverview().getCurrentTask().tapMenu(
+ OverviewTaskMenu splitMenu = overview.getCurrentTask().tapMenu(
OverviewSplitTask.SPLIT_BOTTOM_OR_RIGHT);
assertTrue("App info item not appearing in expanded split task's menu.",
splitMenu.hasMenuItem("App info"));
splitMenu.touchOutsideTaskMenuToDismiss();
}
-
- private void createAndLaunchASplitPair() {
- clearAllRecentTasks();
-
- startTestActivity(2);
- startTestActivity(3);
-
- if (mLauncher.isTablet() && !mLauncher.isGridOnlyOverviewEnabled()) {
- mLauncher.goHome().switchToOverview().getOverviewActions()
- .clickSplit()
- .getTestActivityTask(2)
- .open();
- } else {
- mLauncher.goHome().switchToOverview().getCurrentTask()
- .tapMenu()
- .tapSplitMenuItem()
- .getCurrentTask()
- .open();
- }
- }
-}
+}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 597227a..943c1bd 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -266,9 +266,6 @@
return launcher.<RecentsView>getOverviewPanel().getBottomRowTaskCountForTablet();
}
- // Staging; will be promoted to presubmit if stable
- @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT)
-
@Test
@NavigationModeSwitch
@PortraitLandscape
@@ -293,9 +290,6 @@
}
}
- // Staging; will be promoted to presubmit if stable
- @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT)
-
@Test
@NavigationModeSwitch
@PortraitLandscape
@@ -484,7 +478,8 @@
// assertTrue("Launcher internal state didn't remain in Overview",
// isInState(() -> LauncherState.OVERVIEW));
// overview.getCurrentTask().dismiss();
-// executeOnLauncher(launcher -> assertTrue("Grid did not rebalance after multiple dismissals",
+// executeOnLauncher(launcher -> assertTrue("Grid did not rebalance after multiple
+// dismissals",
// (Math.abs(getTopRowTaskCountForTablet(launcher) - getBottomRowTaskCountForTablet(
// launcher)) <= 1)));
@@ -595,7 +590,8 @@
if (overview.hasTasks()) {
currentTask = overview.getCurrentTask();
assertFalse("Found ExcludeFromRecentsTestActivity after entering Overview from Home",
- currentTask.containsContentDescription("ExcludeFromRecents")
+ currentTask.containsContentDescription(
+ "ExcludeFromRecents")
|| currentTask.containsContentDescription(null));
} else {
// Presumably the test started with 0 tasks and remains that way after going home.
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
index 733ea4e..daa4ec3 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
@@ -17,8 +17,6 @@
import static com.android.launcher3.config.FeatureFlags.enableSplitContextually;
-import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
-import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -33,7 +31,7 @@
import com.android.launcher3.tapl.Overview;
import com.android.launcher3.tapl.Taskbar;
import com.android.launcher3.tapl.TaskbarAppIcon;
-import com.android.launcher3.util.rule.TestStabilityRule;
+import com.android.quickstep.util.SplitScreenTestUtils;
import com.android.wm.shell.Flags;
import org.junit.After;
@@ -110,9 +108,8 @@
assumeTrue("App pairs feature is currently not enabled, no test needed",
Flags.enableAppPairs());
- createAndLaunchASplitPair();
+ Overview overview = SplitScreenTestUtils.createAndLaunchASplitPairInOverview(mLauncher);
- Overview overview = mLauncher.goHome().switchToOverview();
if (mLauncher.isGridOnlyOverviewEnabled() || !mLauncher.isTablet()) {
assertTrue("Save app pair menu item is missing",
overview.getCurrentTask()
@@ -156,24 +153,4 @@
TaskbarAppIcon firstApp = taskbar.getAppIcon(firstAppName);
firstApp.launchIntoSplitScreen();
}
-
- private void createAndLaunchASplitPair() {
- clearAllRecentTasks();
-
- startTestActivity(2);
- startTestActivity(3);
-
- if (mLauncher.isTablet() && !mLauncher.isGridOnlyOverviewEnabled()) {
- mLauncher.goHome().switchToOverview().getOverviewActions()
- .clickSplit()
- .getTestActivityTask(2)
- .open();
- } else {
- mLauncher.goHome().switchToOverview().getCurrentTask()
- .tapMenu()
- .tapSplitMenuItem()
- .getCurrentTask()
- .open();
- }
- }
}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsTrackpad.java b/quickstep/tests/src/com/android/quickstep/TaplTestsTrackpad.java
index 2c23f86..710ad6f 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsTrackpad.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsTrackpad.java
@@ -16,8 +16,6 @@
package com.android.quickstep;
-import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
-import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
import static com.android.quickstep.NavigationModeSwitchRule.Mode.ZERO_BUTTON;
import static org.junit.Assert.assertNotNull;
@@ -32,8 +30,6 @@
import com.android.launcher3.tapl.LauncherInstrumentation.TrackpadGestureType;
import com.android.launcher3.tapl.Workspace;
import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
-import com.android.launcher3.util.rule.ScreenRecordRule;
-import com.android.launcher3.util.rule.TestStabilityRule;
import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
import org.junit.After;
@@ -95,8 +91,6 @@
@Test
@PortraitLandscape
@NavigationModeSwitch
- @ScreenRecordRule.ScreenRecord // b/336606166
- @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/336606166
public void switchToOverview() throws Exception {
assumeTrue(mLauncher.isTablet());
diff --git a/quickstep/tests/src/com/android/quickstep/TaskAnimationManagerTest.java b/quickstep/tests/src/com/android/quickstep/TaskAnimationManagerTest.java
index 3a83ae3..28c8a4a 100644
--- a/quickstep/tests/src/com/android/quickstep/TaskAnimationManagerTest.java
+++ b/quickstep/tests/src/com/android/quickstep/TaskAnimationManagerTest.java
@@ -59,8 +59,6 @@
@Test
public void startRecentsActivity_allowBackgroundLaunch() {
- assumeTrue(TaskAnimationManager.ENABLE_SHELL_TRANSITIONS);
-
final LauncherActivityInterface activityInterface = mock(LauncherActivityInterface.class);
final GestureState gestureState = mock(GestureState.class);
final RecentsAnimationCallbacks.RecentsAnimationListener listener =
diff --git a/quickstep/tests/src/com/android/quickstep/util/SplitScreenTestUtils.kt b/quickstep/tests/src/com/android/quickstep/util/SplitScreenTestUtils.kt
new file mode 100644
index 0000000..82361aa
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/util/SplitScreenTestUtils.kt
@@ -0,0 +1,60 @@
+/*
+ * 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.test.uiautomator.By
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.launcher3.tapl.Overview
+import com.android.launcher3.tapl.OverviewTask
+import com.android.launcher3.ui.AbstractLauncherUiTest
+
+object SplitScreenTestUtils {
+
+ /** Creates 2 tasks and makes a split mode pair. Also asserts the accessibility labels. */
+ @JvmStatic
+ fun createAndLaunchASplitPairInOverview(launcher: LauncherInstrumentation): Overview {
+ clearAllRecentTasks(launcher)
+
+ AbstractLauncherUiTest.startTestActivity(2)
+ AbstractLauncherUiTest.startTestActivity(3)
+
+ val overView = launcher.goHome().switchToOverview()
+ if (launcher.isTablet && !launcher.isGridOnlyOverviewEnabled) {
+ overView.overviewActions.clickSplit().getTestActivityTask(2).open()
+ } else {
+ overView.currentTask.tapMenu().tapSplitMenuItem().currentTask.open()
+ }
+
+ val overviewWithSplitPair = launcher.goHome().switchToOverview()
+ val currentTask = overviewWithSplitPair.currentTask
+ currentTask.containsContentDescription(
+ By.pkg(AbstractLauncherUiTest.getAppPackageName()).text("TestActivity3").toString(),
+ OverviewTask.OverviewSplitTask.SPLIT_TOP_OR_LEFT
+ )
+ currentTask.containsContentDescription(
+ By.pkg(AbstractLauncherUiTest.getAppPackageName()).text("TestActivity2").toString(),
+ OverviewTask.OverviewSplitTask.SPLIT_BOTTOM_OR_RIGHT
+ )
+ return overviewWithSplitPair
+ }
+
+ private fun clearAllRecentTasks(launcher: LauncherInstrumentation) {
+ if (launcher.recentTasks.isNotEmpty()) {
+ launcher.goHome().switchToOverview().dismissAllTasks()
+ }
+ }
+}
diff --git a/res/values/styles.xml b/res/values/styles.xml
index ee7ed26..2d7808b 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -77,6 +77,10 @@
<item name="materialColorError">@color/system_error_light</item>
</style>
+ <style name="DynamicColorsBaseLauncherTheme.NoActionBar">
+ <item name="windowActionBar">false</item>
+ <item name="windowNoTitle">true</item>
+ </style>
<style name="LauncherTheme" parent="@style/DynamicColorsBaseLauncherTheme">
<item name="android:textColorSecondary">#DE000000</item>
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index ef56246..b51e850 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -37,8 +37,6 @@
import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.celllayout.CellPosMapper.CellPos;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.debug.TestEvent;
-import com.android.launcher3.debug.TestEventEmitter;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.logging.InstanceId;
@@ -223,9 +221,6 @@
dl.addView(frame);
frame.mIsOpen = true;
frame.post(() -> frame.snapToWidget(false));
- TestEventEmitter.INSTANCE.get(widget.getContext()).sendEvent(
- TestEvent.RESIZE_FRAME_SHOWING
- );
}
private void setCornerRadiusFromWidget() {
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 633091d..fec94fe 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -110,11 +110,6 @@
public static final int ACTIVITY_STATE_USER_ACTIVE = 1 << 4;
/**
- * State flag indicating if the user will be active shortly.
- */
- public static final int ACTIVITY_STATE_USER_WILL_BE_ACTIVE = 1 << 5;
-
- /**
* State flag indicating that a state transition is in progress
*/
public static final int ACTIVITY_STATE_TRANSITION_ACTIVE = 1 << 6;
@@ -316,7 +311,6 @@
*/
public void setResumed() {
addActivityFlags(ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_USER_ACTIVE);
- removeActivityFlags(ACTIVITY_STATE_USER_WILL_BE_ACTIVE);
}
public boolean isUserActive() {
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 26e900d..8121e53 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -89,7 +89,7 @@
import com.android.launcher3.util.ShortcutUtil;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ActivityContext;
-import com.android.launcher3.views.IconLabelDotView;
+import com.android.launcher3.views.FloatingIconViewCompanion;
import java.text.NumberFormat;
import java.util.HashMap;
@@ -101,7 +101,7 @@
* too aggressive.
*/
public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
- IconLabelDotView, DraggableView, Reorderable {
+ FloatingIconViewCompanion, DraggableView, Reorderable {
public static final String TAG = "BubbleTextView";
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 5cbf6fb..8ae6d73 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -316,6 +316,74 @@
// DragController
public int flingToDeleteThresholdVelocity;
+ /** Used only as an alternative to mocking when null values cannot be used. */
+ @VisibleForTesting
+ public DeviceProfile() {
+ inv = null;
+ mInfo = null;
+ mMetrics = null;
+ mIconSizeSteps = null;
+ isTablet = false;
+ isPhone = false;
+ transposeLayoutWithOrientation = false;
+ isMultiDisplay = false;
+ isTwoPanels = false;
+ isPredictiveBackSwipe = false;
+ isQsbInline = false;
+ isLandscape = false;
+ isMultiWindowMode = false;
+ isGestureMode = false;
+ isLeftRightSplit = false;
+ windowX = 0;
+ windowY = 0;
+ widthPx = 0;
+ heightPx = 0;
+ availableWidthPx = 0;
+ availableHeightPx = 0;
+ rotationHint = 0;
+ aspectRatio = 1;
+ mIsScalableGrid = false;
+ mTypeIndex = 0;
+ mIsResponsiveGrid = false;
+ desiredWorkspaceHorizontalMarginOriginalPx = 0;
+ edgeMarginPx = 0;
+ workspaceContentScale = 0;
+ workspaceSpringLoadedMinNextPageVisiblePx = 0;
+ extraSpace = 0;
+ workspacePageIndicatorHeight = 0;
+ mWorkspacePageIndicatorOverlapWorkspace = 0;
+ numFolderRows = 0;
+ numFolderColumns = 0;
+ folderLabelTextScale = 0;
+ areNavButtonsInline = false;
+ mHotseatBarEdgePaddingPx = 0;
+ mHotseatBarWorkspaceSpacePx = 0;
+ hotseatQsbWidth = 0;
+ hotseatQsbHeight = 0;
+ hotseatQsbVisualHeight = 0;
+ hotseatQsbShadowHeight = 0;
+ hotseatBorderSpace = 0;
+ mMinHotseatIconSpacePx = 0;
+ mMinHotseatQsbWidthPx = 0;
+ mMaxHotseatIconSpacePx = 0;
+ inlineNavButtonsEndSpacingPx = 0;
+ mBubbleBarSpaceThresholdPx = 0;
+ numShownAllAppsColumns = 0;
+ overviewActionsHeight = 0;
+ overviewActionsTopMarginPx = 0;
+ overviewActionsButtonSpacing = 0;
+ mViewScaleProvider = null;
+ mDotRendererWorkSpace = null;
+ mDotRendererAllApps = null;
+ taskbarHeight = 0;
+ stashedTaskbarHeight = 0;
+ taskbarBottomMargin = 0;
+ taskbarIconSize = 0;
+ mTransientTaskbarClaimedSpace = 0;
+ startAlignTaskbar = false;
+ isTransientTaskbar = false;
+ }
+
/** TODO: Once we fully migrate to staged split, remove "isMultiWindowMode" */
DeviceProfile(Context context, InvariantDeviceProfile inv, Info info, WindowBounds windowBounds,
SparseArray<DotRenderer> dotRendererCache, boolean isMultiWindowMode,
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 3ca6099..bafb528 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -435,6 +435,10 @@
mIsColdStartupAfterReboot = sIsNewProcess
&& !LockedUserState.get(this).isUserUnlockedAtLauncherStartup();
if (mIsColdStartupAfterReboot) {
+ /*
+ * This trace is used to calculate the time from create to the point that icons are
+ * visible.
+ */
Trace.beginAsyncSection(
COLD_STARTUP_TRACE_METHOD_NAME, COLD_STARTUP_TRACE_COOKIE);
}
@@ -2384,10 +2388,6 @@
.logEnd(LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION)
.log()
.reset();
- if (mIsColdStartupAfterReboot) {
- Trace.endAsyncSection(COLD_STARTUP_TRACE_METHOD_NAME,
- COLD_STARTUP_TRACE_COOKIE);
- }
});
}
@@ -2396,6 +2396,10 @@
RunnableList onCompleteSignal, int workspaceItemCount, boolean isBindSync) {
mModelCallbacks.onInitialBindComplete(boundPages, pendingTasks, onCompleteSignal,
workspaceItemCount, isBindSync);
+ if (mIsColdStartupAfterReboot) {
+ Trace.endAsyncSection(COLD_STARTUP_TRACE_METHOD_NAME,
+ COLD_STARTUP_TRACE_COOKIE);
+ }
}
/**
diff --git a/src/com/android/launcher3/LauncherApplication.java b/src/com/android/launcher3/LauncherApplication.java
index 40873be..8969b60 100644
--- a/src/com/android/launcher3/LauncherApplication.java
+++ b/src/com/android/launcher3/LauncherApplication.java
@@ -17,14 +17,23 @@
import android.app.Application;
+import com.android.launcher3.dagger.DaggerLauncherAppComponent;
+import com.android.launcher3.dagger.LauncherBaseAppComponent;
+
/**
* Main application class for Launcher
*/
public class LauncherApplication extends Application {
+ private LauncherBaseAppComponent mAppComponent;
@Override
public void onCreate() {
super.onCreate();
MainProcessInitializer.initialize(this);
+ mAppComponent = DaggerLauncherAppComponent.builder().build();
+ }
+
+ public LauncherBaseAppComponent getAppComponent() {
+ return mAppComponent;
}
}
diff --git a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
new file mode 100644
index 0000000..3488c95
--- /dev/null
+++ b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.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;
+
+/**
+ * Launcher 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 by AOSP.
+ */
+public interface LauncherBaseAppComponent {
+ /** Builder for LauncherBaseAppComponent. */
+ interface Builder {
+ LauncherBaseAppComponent build();
+ }
+}
diff --git a/src/com/android/launcher3/debug/TestEventsEmitterProduction.kt b/src/com/android/launcher3/debug/TestEventsEmitterProduction.kt
index e218b4d..52b454f 100644
--- a/src/com/android/launcher3/debug/TestEventsEmitterProduction.kt
+++ b/src/com/android/launcher3/debug/TestEventsEmitterProduction.kt
@@ -27,6 +27,8 @@
WORKSPACE_ON_DROP("WORKSPACE_ON_DROP"),
RESIZE_FRAME_SHOWING("RESIZE_FRAME_SHOWING"),
WORKSPACE_FINISH_LOADING("WORKSPACE_FINISH_LOADING"),
+ SPRING_LOADED_STATE_STARTED("SPRING_LOADED_STATE_STARTED"),
+ SPRING_LOADED_STATE_COMPLETED("SPRING_LOADED_STATE_COMPLETED"),
}
/** Interface to create TestEventEmitters. */
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index a0b695a..de1bcc3 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -83,7 +83,7 @@
import com.android.launcher3.util.MultiTranslateDelegate;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.views.ActivityContext;
-import com.android.launcher3.views.IconLabelDotView;
+import com.android.launcher3.views.FloatingIconViewCompanion;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import java.util.ArrayList;
@@ -93,7 +93,7 @@
/**
* An icon that can appear on in the workspace representing an {@link Folder}.
*/
-public class FolderIcon extends FrameLayout implements FolderListener, IconLabelDotView,
+public class FolderIcon extends FrameLayout implements FolderListener, FloatingIconViewCompanion,
DraggableView, Reorderable {
private final MultiTranslateDelegate mTranslateDelegate = new MultiTranslateDelegate(this);
diff --git a/src/com/android/launcher3/views/BubbleTextHolder.java b/src/com/android/launcher3/views/BubbleTextHolder.java
index 84f8049..d2ae93b 100644
--- a/src/com/android/launcher3/views/BubbleTextHolder.java
+++ b/src/com/android/launcher3/views/BubbleTextHolder.java
@@ -20,7 +20,7 @@
/**
* Views that contain {@link BubbleTextView} should implement this interface.
*/
-public interface BubbleTextHolder extends IconLabelDotView {
+public interface BubbleTextHolder extends FloatingIconViewCompanion {
BubbleTextView getBubbleText();
@Override
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index 37482ac..4ee6aff 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -22,7 +22,7 @@
import static com.android.launcher3.Utilities.getFullDrawable;
import static com.android.launcher3.Utilities.mapToRange;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-import static com.android.launcher3.views.IconLabelDotView.setIconAndDotVisible;
+import static com.android.launcher3.views.FloatingIconViewCompanion.setPropertiesVisible;
import android.animation.Animator;
import android.content.Context;
@@ -516,6 +516,10 @@
// When closing an app, we want the item on the workspace to be invisible immediately
updateViewsVisibility(false /* isVisible */);
}
+ if (mFadeOutView instanceof FloatingIconViewCompanion fivc) {
+ fivc.setForceHideDot(true);
+ fivc.setForceHideRing(true);
+ }
}
@Override
@@ -653,6 +657,10 @@
if (view.mFadeOutView != null) {
view.mFadeOutView.setAlpha(1f);
}
+ if (view.mFadeOutView instanceof FloatingIconViewCompanion fivc) {
+ fivc.setForceHideDot(false);
+ fivc.setForceHideRing(false);
+ }
if (hideOriginal) {
view.updateViewsVisibility(true /* isVisible */);
@@ -674,10 +682,10 @@
private void updateViewsVisibility(boolean isVisible) {
if (mOriginalIcon != null) {
- setIconAndDotVisible(mOriginalIcon, isVisible);
+ setPropertiesVisible(mOriginalIcon, isVisible);
}
if (mMatchVisibilityView != null) {
- setIconAndDotVisible(mMatchVisibilityView, isVisible);
+ setPropertiesVisible(mMatchVisibilityView, isVisible);
}
}
diff --git a/src/com/android/launcher3/views/FloatingIconViewCompanion.java b/src/com/android/launcher3/views/FloatingIconViewCompanion.java
new file mode 100644
index 0000000..fc23903
--- /dev/null
+++ b/src/com/android/launcher3/views/FloatingIconViewCompanion.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.views;
+
+import android.view.View;
+
+/**
+ * A view that can be drawn (in some capacity) via) {@link FloatingIconView}.
+ * This interface allows us to hide certain properties of the view that the FloatingIconView
+ * cannot draw, which allows us to make a seamless handoff between the FloatingIconView and
+ * the companion view.
+ */
+public interface FloatingIconViewCompanion {
+ void setIconVisible(boolean visible);
+ void setForceHideDot(boolean hide);
+ default void setForceHideRing(boolean hide) {}
+
+ /**
+ * Sets the visibility of icon and dot of the view
+ */
+ static void setPropertiesVisible(View view, boolean visible) {
+ if (view instanceof FloatingIconViewCompanion) {
+ ((FloatingIconViewCompanion) view).setIconVisible(visible);
+ ((FloatingIconViewCompanion) view).setForceHideDot(!visible);
+ ((FloatingIconViewCompanion) view).setForceHideRing(!visible);
+ } else {
+ view.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/views/FloatingSurfaceView.java b/src/com/android/launcher3/views/FloatingSurfaceView.java
index cab7982..7fa7517 100644
--- a/src/com/android/launcher3/views/FloatingSurfaceView.java
+++ b/src/com/android/launcher3/views/FloatingSurfaceView.java
@@ -17,7 +17,7 @@
import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
import static com.android.launcher3.views.FloatingIconView.getLocationBoundsForView;
-import static com.android.launcher3.views.IconLabelDotView.setIconAndDotVisible;
+import static com.android.launcher3.views.FloatingIconViewCompanion.setPropertiesVisible;
import android.content.Context;
import android.graphics.Canvas;
@@ -237,7 +237,7 @@
private void setCurrentIconVisible(boolean isVisible) {
if (mIcon != null) {
- setIconAndDotVisible(mIcon, isVisible);
+ setPropertiesVisible(mIcon, isVisible);
}
}
}
diff --git a/src/com/android/launcher3/views/IconLabelDotView.java b/src/com/android/launcher3/views/IconLabelDotView.java
deleted file mode 100644
index e9113cf..0000000
--- a/src/com/android/launcher3/views/IconLabelDotView.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.views;
-
-import android.view.View;
-
-/**
- * A view that has an icon, label, and notification dot.
- */
-public interface IconLabelDotView {
- void setIconVisible(boolean visible);
- void setForceHideDot(boolean hide);
-
- /**
- * Sets the visibility of icon and dot of the view
- */
- static void setIconAndDotVisible(View view, boolean visible) {
- if (view instanceof IconLabelDotView) {
- ((IconLabelDotView) view).setIconVisible(visible);
- ((IconLabelDotView) view).setForceHideDot(!visible);
- } else {
- view.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
- }
- }
-}
diff --git a/src_no_quickstep/com/android/launcher3/dagger/LauncherAppComponent.java b/src_no_quickstep/com/android/launcher3/dagger/LauncherAppComponent.java
new file mode 100644
index 0000000..4d7f937
--- /dev/null
+++ b/src_no_quickstep/com/android/launcher3/dagger/LauncherAppComponent.java
@@ -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.dagger;
+
+import dagger.Component;
+
+import javax.inject.Singleton;
+
+/**
+ * Root component for Dagger injection for Launcher AOSP.
+ */
+@Singleton
+@Component
+public interface LauncherAppComponent extends LauncherBaseAppComponent {
+ /** Builder for aosp LauncherAppComponent. */
+ @Component.Builder
+ interface Builder extends LauncherBaseAppComponent.Builder {
+ LauncherAppComponent build();
+ }
+}
+
diff --git a/tests/Android.bp b/tests/Android.bp
index 1fa6e05..9f62d02 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -127,9 +127,9 @@
"com_android_launcher3_flags_lib",
],
libs: [
- "android.test.base",
- "android.test.runner",
- "android.test.mock",
+ "android.test.base.stubs.system",
+ "android.test.runner.stubs.system",
+ "android.test.mock.stubs.system",
],
// Libraries used by mockito inline extended
jni_libs: [
@@ -229,9 +229,9 @@
"android.appwidget.flags-aconfig-java",
],
libs: [
- "android.test.runner",
- "android.test.base",
- "android.test.mock",
+ "android.test.runner.stubs.system",
+ "android.test.base.stubs.system",
+ "android.test.mock.stubs.system",
"truth",
],
instrumentation_for: "Launcher3",
diff --git a/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java b/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java
index 907aa50..7c87c65 100644
--- a/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java
+++ b/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java
@@ -98,7 +98,6 @@
@Test
@PortraitLandscape
@PlatinumTest(focusArea = "launcher")
- @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/311099513
public void testUninstallFromWorkspace() throws Exception {
installDummyAppAndWaitForUIUpdate();
try {
diff --git a/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java
index 9b184ae..9c916fa 100644
--- a/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java
@@ -15,9 +15,6 @@
*/
package com.android.launcher3.ui.widget;
-import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
-import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
-
import static org.junit.Assert.assertNotNull;
import android.platform.test.annotations.PlatinumTest;
@@ -33,7 +30,6 @@
import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
import com.android.launcher3.ui.TestViewHelpers;
import com.android.launcher3.util.rule.ShellCommandRule;
-import com.android.launcher3.util.rule.TestStabilityRule.Stability;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import org.junit.Assume;
@@ -102,7 +98,6 @@
/**
* Test dragging a widget to the workspace and resize it.
*/
- @Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/316910614
@PlatinumTest(focusArea = "launcher")
@Test
public void testResizeWidget() throws Throwable {
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
index ab48a21..5433fa7 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -284,8 +284,9 @@
*
* TODO(b/326565120): remove Nullable support once the bug causing it to be null is fixed.
*/
- public boolean containsContentDescription(@Nullable String expected) {
- String actual = mTask.getContentDescription();
+ public boolean containsContentDescription(@Nullable String expected,
+ OverviewSplitTask overviewSplitTask) {
+ String actual = findObjectInTask(overviewSplitTask.snapshotRes).getContentDescription();
if (actual == null && expected == null) {
return true;
}
@@ -295,6 +296,14 @@
return actual.contains(expected);
}
+ /**
+ * Returns whether the given String is contained in this Task's contentDescription. Also returns
+ * true if both Strings are null
+ */
+ public boolean containsContentDescription(@Nullable String expected) {
+ return containsContentDescription(expected, DEFAULT);
+ }
+
private TaskViewType getType() {
String resourceName = mTask.getResourceName();
if (resourceName.endsWith("task_view_grouped")) {