diff options
284 files changed, 3232 insertions, 1873 deletions
diff --git a/ADPF_OWNERS b/ADPF_OWNERS index bcdc33825a13..c7ff4640b185 100644 --- a/ADPF_OWNERS +++ b/ADPF_OWNERS @@ -1,4 +1,5 @@ -sumir@google.com +adyabr@google.com chingtangyu@google.com -xwxw@google.com mattbuckley@google.com +sumir@google.com +xwxw@google.com
\ No newline at end of file diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 4222c7c64672..514a58244aa2 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -2586,7 +2586,7 @@ package android.os { } public class UserManager { - method @FlaggedApi("android.os.allow_private_profile") @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional=true) public boolean canAddPrivateProfile(); + method @FlaggedApi("android.os.allow_private_profile") @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean canAddPrivateProfile(); method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createProfileForUser(@Nullable String, @NonNull String, int, int, @Nullable String[]); method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createRestrictedProfile(@Nullable String); method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createUser(@Nullable String, @NonNull String, int); diff --git a/core/java/android/os/CombinedMessageQueue/MessageQueue.java b/core/java/android/os/CombinedMessageQueue/MessageQueue.java index 3c03bb5626c8..74972346bf2e 100644 --- a/core/java/android/os/CombinedMessageQueue/MessageQueue.java +++ b/core/java/android/os/CombinedMessageQueue/MessageQueue.java @@ -130,7 +130,7 @@ public final class MessageQueue { MessageQueue(boolean quitAllowed) { initIsProcessAllowedToUseConcurrent(); - mUseConcurrent = sIsProcessAllowedToUseConcurrent && !isInstrumenting(); + mUseConcurrent = sIsProcessAllowedToUseConcurrent; mQuitAllowed = quitAllowed; mPtr = nativeInit(); mThread = Thread.currentThread(); @@ -202,15 +202,6 @@ public final class MessageQueue { return; } - private static boolean isInstrumenting() { - final ActivityThread activityThread = ActivityThread.currentActivityThread(); - if (activityThread == null) { - return false; - } - final Instrumentation instrumentation = activityThread.getInstrumentation(); - return instrumentation != null && instrumentation.isInstrumenting(); - } - @Override protected void finalize() throws Throwable { try { diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 33bf4a29ecc6..767019d97758 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -3324,8 +3324,7 @@ public class UserManager { @FlaggedApi(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE) @RequiresPermission(anyOf = { Manifest.permission.MANAGE_USERS, - Manifest.permission.CREATE_USERS}, - conditional = true) + Manifest.permission.CREATE_USERS}) @UserHandleAware public boolean canAddPrivateProfile() { if (!android.multiuser.Flags.enablePrivateSpaceFeatures()) return false; diff --git a/core/java/android/view/textclassifier/intent/OWNERS b/core/java/android/view/textclassifier/intent/OWNERS index 3465fe62784f..dc18514ce949 100644 --- a/core/java/android/view/textclassifier/intent/OWNERS +++ b/core/java/android/view/textclassifier/intent/OWNERS @@ -1,6 +1,5 @@ # Bug component: 709498 -mns@google.com toki@google.com svetoslavganov@android.com svetoslavganov@google.com diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 0a5c14e3a08b..222a7b38c966 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -9216,6 +9216,7 @@ public class RemoteViews implements Parcelable, Filter { public static RemoteResponse fromFillInIntent(@NonNull Intent fillIntent) { RemoteResponse response = new RemoteResponse(); response.mFillIntent = fillIntent; + fillIntent.collectExtraIntentKeys(); return response; } @@ -9224,6 +9225,7 @@ public class RemoteViews implements Parcelable, Filter { RemoteResponse response = new RemoteResponse(); response.mPendingIntent = pendingIntent; response.mFillIntent = intent; + intent.collectExtraIntentKeys(); return response; } diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig index f77a36676b3d..2e36e9a205bf 100644 --- a/core/java/android/window/flags/lse_desktop_experience.aconfig +++ b/core/java/android/window/flags/lse_desktop_experience.aconfig @@ -206,6 +206,13 @@ flag { } flag { + name: "enable_camera_compat_for_desktop_windowing_opt_out_api" + namespace: "lse_desktop_experience" + description: "Introduces a developer API to opt-out of Camera Compat treatment for fixed-orientation apps in desktop windowing mode" + bug: "397165621" +} + +flag { name: "enable_task_stack_observer_in_shell" namespace: "lse_desktop_experience" description: "Introduces a new observer in shell to track the task stack." @@ -794,4 +801,14 @@ flag { namespace: "lse_desktop_experience" description: "Enable Desktop Mode on Projected Mode devices but constrained to the external display." bug: "384568161" +} + +flag { + name: "enable_persisting_density_scale_for_connected_displays" + namespace: "lse_desktop_experience" + description: "Enables persisting density scale on resolution change for connected displays." + bug: "392855657" + metadata { + purpose: PURPOSE_BUGFIX + } }
\ No newline at end of file diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java index a27eeb8fdd63..1281a78d4fa2 100644 --- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java +++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java @@ -97,6 +97,8 @@ public class AccessibilityShortcutController { new ComponentName("com.android.server.accessibility", "ReduceBrightColors"); public static final ComponentName FONT_SIZE_COMPONENT_NAME = new ComponentName("com.android.server.accessibility", "FontSize"); + public static final ComponentName AUTOCLICK_COMPONENT_NAME = + new ComponentName("com.android.server.accessibility", "Autoclick"); // The component name for the sub setting of Accessibility button in Accessibility settings public static final ComponentName ACCESSIBILITY_BUTTON_COMPONENT_NAME = @@ -163,7 +165,7 @@ public class AccessibilityShortcutController { getFrameworkShortcutFeaturesMap() { if (sFrameworkShortcutFeaturesMap == null) { - Map<ComponentName, FrameworkFeatureInfo> featuresMap = new ArrayMap<>(4); + Map<ComponentName, FrameworkFeatureInfo> featuresMap = new ArrayMap<>(8); featuresMap.put(COLOR_INVERSION_COMPONENT_NAME, new ToggleableFrameworkFeatureInfo( Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, @@ -174,6 +176,11 @@ public class AccessibilityShortcutController { Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, "1" /* Value to enable */, "0" /* Value to disable */, R.string.color_correction_feature_name)); + featuresMap.put(AUTOCLICK_COMPONENT_NAME, + new ToggleableFrameworkFeatureInfo( + Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED, + "1" /* Value to enable */, "0" /* Value to disable */, + R.string.autoclick_feature_name)); if (SUPPORT_ONE_HANDED_MODE) { featuresMap.put(ONE_HANDED_COMPONENT_NAME, new ToggleableFrameworkFeatureInfo( diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java index b187eb42366f..6498c53a63e2 100644 --- a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java +++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java @@ -17,6 +17,7 @@ package com.android.internal.accessibility.dialog; import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME; +import static com.android.internal.accessibility.AccessibilityShortcutController.AUTOCLICK_COMPONENT_NAME; import static com.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME; import static com.android.internal.accessibility.AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME; import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME; @@ -214,6 +215,19 @@ public final class AccessibilityTargetHelper { Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED); targets.add(colorInversion); + // TODO(b/394683600): Update Icon with the autoclick asset. + final ToggleAllowListingFeatureTarget autoclick = + new ToggleAllowListingFeatureTarget(context, + shortcutType, + isShortcutContained(context, shortcutType, + AUTOCLICK_COMPONENT_NAME.flattenToString()), + AUTOCLICK_COMPONENT_NAME.flattenToString(), + uid, + context.getString(R.string.autoclick_feature_name), + context.getDrawable(R.drawable.ic_accessibility_generic), + Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED); + targets.add(autoclick); + if (SUPPORT_ONE_HANDED_MODE) { final ToggleAllowListingFeatureTarget oneHandedMode = new ToggleAllowListingFeatureTarget(context, diff --git a/core/res/res/color-night/surface_effect_2_color.xml b/core/res/res/color-night/surface_effect_2_color.xml new file mode 100644 index 000000000000..6485017bec98 --- /dev/null +++ b/core/res/res/color-night/surface_effect_2_color.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2025 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:color="@android:color/system_accent1_100" + android:alpha="0.15"/> +</selector> diff --git a/core/res/res/color-night/surface_effect_3_color.xml b/core/res/res/color-night/surface_effect_3_color.xml new file mode 100644 index 000000000000..a7a7f8d89549 --- /dev/null +++ b/core/res/res/color-night/surface_effect_3_color.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2025 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:color="@android:color/system_accent1_100" + android:alpha="0.10"/> +</selector> diff --git a/core/res/res/color/surface_effect_2_color.xml b/core/res/res/color/surface_effect_2_color.xml new file mode 100644 index 000000000000..91c84095ec12 --- /dev/null +++ b/core/res/res/color/surface_effect_2_color.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2025 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:color="@android:color/system_accent1_0" + android:alpha="0.32"/> +</selector> diff --git a/core/res/res/color/surface_effect_3_color.xml b/core/res/res/color/surface_effect_3_color.xml new file mode 100644 index 000000000000..d766a32aff8b --- /dev/null +++ b/core/res/res/color/surface_effect_3_color.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2025 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:color="@android:color/system_accent1_600" + android:alpha="0.15"/> +</selector> diff --git a/core/res/res/layout/notification_2025_template_header.xml b/core/res/res/layout/notification_2025_template_header.xml index 5aae67802af9..72b3798e0780 100644 --- a/core/res/res/layout/notification_2025_template_header.xml +++ b/core/res/res/layout/notification_2025_template_header.xml @@ -59,7 +59,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentStart="true" - android:layout_toStartOf="@id/expand_button_container" + android:layout_toStartOf="@id/expand_button" android:layout_alignWithParentIfMissing="true" android:layout_marginVertical="@dimen/notification_2025_margin" android:clipChildren="false" @@ -81,30 +81,12 @@ android:focusable="false" /> - - <LinearLayout - android:id="@+id/expand_button_container" + <include layout="@layout/notification_2025_expand_button" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_alignParentEnd="true" - android:orientation="vertical" - > - <FrameLayout - android:id="@+id/expand_button_spacer" - android:layout_width="@dimen/notification_2025_expand_button_pill_width" - android:layout_height="@dimen/notification_2025_expand_button_pill_height" - android:layout_centerVertical="true" - android:layout_alignParentEnd="true" - android:layout_margin="@dimen/notification_2025_margin" - android:visibility="gone" /> - - <include layout="@layout/notification_2025_expand_button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="top|end" - android:layout_alignParentEnd="true" /> + android:layout_gravity="top|end" + android:layout_alignParentEnd="true" /> - </LinearLayout> <include layout="@layout/notification_close_button" android:id="@+id/close_button" android:layout_width="@dimen/notification_close_button_size" diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml index b7fd737a4e39..57959361bd48 100644 --- a/core/res/res/layout/notification_template_header.xml +++ b/core/res/res/layout/notification_template_header.xml @@ -62,7 +62,7 @@ android:layout_height="match_parent" android:layout_alignParentStart="true" android:layout_centerVertical="true" - android:layout_toStartOf="@id/expand_button_container" + android:layout_toStartOf="@id/expand_button" android:layout_alignWithParentIfMissing="true" android:clipChildren="false" android:gravity="center_vertical" @@ -83,28 +83,12 @@ android:focusable="false" /> - <LinearLayout - android:id="@+id/expand_button_container" + <include layout="@layout/notification_expand_button" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_alignParentEnd="true" - android:orientation="vertical" - > - <FrameLayout - android:id="@+id/expand_button_spacer" - android:layout_width="@dimen/notification_expand_button_pill_height" - android:layout_height="@dimen/notification_header_height" - android:layout_centerVertical="true" - android:layout_alignParentEnd="true" - android:layout_marginHorizontal="16dp" - android:visibility="gone" /> + android:layout_centerVertical="true" + android:layout_alignParentEnd="true" /> - <include layout="@layout/notification_expand_button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_centerVertical="true" - android:layout_alignParentEnd="true" /> - </LinearLayout> <include layout="@layout/notification_close_button" android:id="@+id/close_button" android:layout_width="@dimen/notification_close_button_size" diff --git a/core/res/res/values-night/colors.xml b/core/res/res/values-night/colors.xml index 7b9b2d666292..e19ee23a8bd2 100644 --- a/core/res/res/values-night/colors.xml +++ b/core/res/res/values-night/colors.xml @@ -50,6 +50,8 @@ <!-- Color for various surfaces related to system-wide blur --> <color name="surface_effect_0">@color/surface_effect_0_color</color> <color name="surface_effect_1">@color/surface_effect_1_color</color> + <color name="surface_effect_2">@color/surface_effect_2_color</color> + <color name="surface_effect_3">@color/surface_effect_3_color</color> <!-- Color for side fps toast dark theme--> <color name="side_fps_toast_background">#2E3132</color> diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index 864daf9d2072..4e93c44e44bd 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -585,6 +585,8 @@ <!-- Color for various surfaces related to system-wide blur --> <color name="surface_effect_0">@color/surface_effect_0_color</color> <color name="surface_effect_1">@color/surface_effect_1_color</color> + <color name="surface_effect_2">@color/surface_effect_2_color</color> + <color name="surface_effect_3">@color/surface_effect_3_color</color> <!-- Color for system bars --> <color name="navigation_bar_compatible">@android:color/black</color> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 7a93ca1e9ac6..6a83bae15e76 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -5009,6 +5009,9 @@ <!-- Title of hearing aids feature, shown in the warning dialog about the accessibility shortcut. [CHAR LIMIT=none] --> <string name="hearing_aids_feature_name">Hearing devices</string> + <!-- Title of autoclick feature, shown in the warning dialog about the accessibility shortcut. [CHAR LIMIT=none] --> + <string name="autoclick_feature_name">Autoclick</string> + <!-- Text of hearing device disconnected state. [CHAR LIMIT=20] --> <string name="hearing_device_status_disconnected">Disconnected</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index b013ffd41ecb..ed553c00d652 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -535,6 +535,8 @@ <java-symbol type="color" name="surface_effect_0" /> <java-symbol type="color" name="surface_effect_1" /> + <java-symbol type="color" name="surface_effect_2" /> + <java-symbol type="color" name="surface_effect_3" /> <java-symbol type="color" name="tab_indicator_text_v4" /> <java-symbol type="dimen" name="accessibility_touch_slop" /> @@ -3235,8 +3237,6 @@ <java-symbol type="id" name="header_text" /> <java-symbol type="id" name="header_text_secondary" /> <java-symbol type="id" name="expand_button" /> - <java-symbol type="id" name="expand_button_spacer" /> - <java-symbol type="id" name="expand_button_container" /> <java-symbol type="id" name="expand_button_pill" /> <java-symbol type="id" name="expand_button_pill_colorized_layer" /> <java-symbol type="id" name="expand_button_number" /> @@ -3791,6 +3791,7 @@ <java-symbol type="string" name="accessibility_shortcut_disabling_service" /> <java-symbol type="string" name="color_inversion_feature_name" /> <java-symbol type="string" name="color_correction_feature_name" /> + <java-symbol type="string" name="autoclick_feature_name"/> <java-symbol type="string" name="reduce_bright_colors_feature_name" /> <java-symbol type="string" name="config_defaultAccessibilityService" /> <java-symbol type="string" name="config_defaultSelectToSpeakService" /> diff --git a/core/tests/coretests/src/android/widget/RemoteViewsTest.java b/core/tests/coretests/src/android/widget/RemoteViewsTest.java index 8b0d3158e9e7..9110898e18c9 100644 --- a/core/tests/coretests/src/android/widget/RemoteViewsTest.java +++ b/core/tests/coretests/src/android/widget/RemoteViewsTest.java @@ -22,6 +22,7 @@ import static com.android.internal.R.id.pending_intent_tag; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; @@ -1065,6 +1066,22 @@ public class RemoteViewsTest { assertEquals(b1Memory + b2Memory, rv.estimateTotalBitmapMemoryUsage()); } + @Test + public void remoteResponse_FillInIntentNestedIntentKeysCollected() { + Intent fillInIntent = new Intent(); + fillInIntent.putExtra("extraIntent", new Intent()); + RemoteViews.RemoteResponse.fromFillInIntent(fillInIntent); + assertNotEquals(0, fillInIntent.getExtendedFlags() + & Intent.EXTENDED_FLAG_NESTED_INTENT_KEYS_COLLECTED); + + fillInIntent = new Intent(); + fillInIntent.putExtra("extraIntent", new Intent()); + RemoteViews rv = new RemoteViews(mPackage, R.layout.remote_views_test); + rv.setOnClickFillInIntent(R.id.view, fillInIntent); + assertNotEquals(0, fillInIntent.getExtendedFlags() + & Intent.EXTENDED_FLAG_NESTED_INTENT_KEYS_COLLECTED); + } + private static LayoutInflater.Factory2 createLayoutInflaterFactory(String viewTypeToReplace, View replacementView) { return new LayoutInflater.Factory2() { diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java index 0a1e3b9495a0..25b9f8ccc6ae 100644 --- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java @@ -47,20 +47,26 @@ public class GroupedTaskInfo implements Parcelable { public static final int TYPE_FULLSCREEN = 1; public static final int TYPE_SPLIT = 2; - public static final int TYPE_FREEFORM = 3; + public static final int TYPE_DESK = 3; public static final int TYPE_MIXED = 4; @IntDef(prefix = {"TYPE_"}, value = { TYPE_FULLSCREEN, TYPE_SPLIT, - TYPE_FREEFORM, + TYPE_DESK, TYPE_MIXED }) public @interface GroupType {} /** + * The ID of the desk that this `GroupedTaskInfo` represents (when the type is `TYPE_DESK`). The + * value is -1 if this is not a desk. + */ + private final int mDeskId; + + /** * The type of this particular task info, can be one of TYPE_FULLSCREEN, TYPE_SPLIT or - * TYPE_FREEFORM. + * TYPE_DESK. */ @GroupType protected final int mType; @@ -69,7 +75,7 @@ public class GroupedTaskInfo implements Parcelable { * The list of tasks associated with this single recent task info. * TYPE_FULLSCREEN: Contains the stack of tasks associated with a single "task" in overview * TYPE_SPLIT: Contains the two split roots of each side - * TYPE_FREEFORM: Contains the set of tasks currently in freeform mode + * TYPE_DESK: Contains the set of tasks currently in freeform mode contained in desk. */ @Nullable protected final List<TaskInfo> mTasks; @@ -83,7 +89,7 @@ public class GroupedTaskInfo implements Parcelable { protected final SplitBounds mSplitBounds; /** - * Only set for TYPE_FREEFORM. + * Only set for TYPE_DESK. * * TODO(b/348332802): move isMinimized inside each Task object instead once we have a * replacement for RecentTaskInfo @@ -103,8 +109,8 @@ public class GroupedTaskInfo implements Parcelable { * Create new for a stack of fullscreen tasks */ public static GroupedTaskInfo forFullscreenTasks(@NonNull TaskInfo task) { - return new GroupedTaskInfo(List.of(task), null, TYPE_FULLSCREEN, - null /* minimizedFreeformTasks */); + return new GroupedTaskInfo(/* deskId = */ -1, List.of(task), null, TYPE_FULLSCREEN, + /* minimizedFreeformTaskIds = */ null); } /** @@ -112,18 +118,19 @@ public class GroupedTaskInfo implements Parcelable { */ public static GroupedTaskInfo forSplitTasks(@NonNull TaskInfo task1, @NonNull TaskInfo task2, @NonNull SplitBounds splitBounds) { - return new GroupedTaskInfo(List.of(task1, task2), splitBounds, TYPE_SPLIT, - null /* minimizedFreeformTasks */); + return new GroupedTaskInfo(/* deskId = */ -1, List.of(task1, task2), splitBounds, + TYPE_SPLIT, /* minimizedFreeformTaskIds = */ null); } /** - * Create new for a group of freeform tasks + * Create new for a group of freeform tasks that belong to a single desk. */ - public static GroupedTaskInfo forFreeformTasks( - @NonNull List<TaskInfo> tasks, - @NonNull Set<Integer> minimizedFreeformTasks) { - return new GroupedTaskInfo(tasks, null /* splitBounds */, TYPE_FREEFORM, - minimizedFreeformTasks.stream().mapToInt(i -> i).toArray()); + public static GroupedTaskInfo forDeskTasks( + int deskId, + @NonNull List<TaskInfo> tasks, + @NonNull Set<Integer> minimizedFreeformTaskIds) { + return new GroupedTaskInfo(deskId, tasks, /* splitBounds = */ null, TYPE_DESK, + minimizedFreeformTaskIds.stream().mapToInt(i -> i).toArray()); } /** @@ -141,10 +148,12 @@ public class GroupedTaskInfo implements Parcelable { } private GroupedTaskInfo( + int deskId, @NonNull List<TaskInfo> tasks, @Nullable SplitBounds splitBounds, @GroupType int type, @Nullable int[] minimizedFreeformTaskIds) { + mDeskId = deskId; mTasks = tasks; mGroupedTasks = null; mSplitBounds = splitBounds; @@ -154,6 +163,7 @@ public class GroupedTaskInfo implements Parcelable { } private GroupedTaskInfo(@NonNull List<GroupedTaskInfo> groupedTasks) { + mDeskId = -1; mTasks = null; mGroupedTasks = groupedTasks; mSplitBounds = null; @@ -174,6 +184,7 @@ public class GroupedTaskInfo implements Parcelable { } protected GroupedTaskInfo(@NonNull Parcel parcel) { + mDeskId = parcel.readInt(); mTasks = new ArrayList(); final int numTasks = parcel.readInt(); for (int i = 0; i < numTasks; i++) { @@ -274,6 +285,16 @@ public class GroupedTaskInfo implements Parcelable { } /** + * Returns the ID of the desk represented by `this` if the type is `TYPE_DESK`, or -1 otherwise. + */ + public int getDeskId() { + if (mType == TYPE_MIXED) { + throw new IllegalStateException("No desk ID for a mixed task"); + } + return mDeskId; + } + + /** * Get type of this recents entry. One of {@link GroupType}. * Note: This is deprecated, callers should use `isBaseType()` and not make assumptions about * specific group types @@ -285,7 +306,7 @@ public class GroupedTaskInfo implements Parcelable { } /** - * Returns the set of minimized task ids, only valid for TYPE_FREEFORM. + * Returns the set of minimized task ids, only valid for TYPE_DESK. */ @Nullable public int[] getMinimizedTaskIds() { @@ -301,7 +322,8 @@ public class GroupedTaskInfo implements Parcelable { return false; } GroupedTaskInfo other = (GroupedTaskInfo) obj; - return mType == other.mType + return mDeskId == other.mDeskId + && mType == other.mType && Objects.equals(mTasks, other.mTasks) && Objects.equals(mGroupedTasks, other.mGroupedTasks) && Objects.equals(mSplitBounds, other.mSplitBounds) @@ -310,7 +332,7 @@ public class GroupedTaskInfo implements Parcelable { @Override public int hashCode() { - return Objects.hash(mType, mTasks, mGroupedTasks, mSplitBounds, + return Objects.hash(mDeskId, mType, mTasks, mGroupedTasks, mSplitBounds, Arrays.hashCode(mMinimizedTaskIds)); } @@ -322,6 +344,7 @@ public class GroupedTaskInfo implements Parcelable { .map(GroupedTaskInfo::toString) .collect(Collectors.joining(",\n\t", "[\n\t", "\n]"))); } else { + taskString.append("Desk ID= ").append(mDeskId).append(", "); taskString.append("Tasks=" + mTasks.stream() .map(taskInfo -> getTaskInfoDumpString(taskInfo)) .collect(Collectors.joining(", ", "[", "]"))); @@ -353,6 +376,7 @@ public class GroupedTaskInfo implements Parcelable { @Override public void writeToParcel(Parcel parcel, int flags) { + parcel.writeInt(mDeskId); // We don't use the parcel list methods because we want to only write the TaskInfo state // and not the subclasses (Recents/RunningTaskInfo) whose fields are all deprecated final int tasksSize = mTasks != null ? mTasks.size() : 0; @@ -375,7 +399,7 @@ public class GroupedTaskInfo implements Parcelable { return switch (type) { case TYPE_FULLSCREEN -> "FULLSCREEN"; case TYPE_SPLIT -> "SPLIT"; - case TYPE_FREEFORM -> "FREEFORM"; + case TYPE_DESK -> "DESK"; case TYPE_MIXED -> "MIXED"; default -> "UNKNOWN"; }; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt index 7c6cf4a8b37f..926e0f287869 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt @@ -217,6 +217,9 @@ class DesktopRepository( fun getDeskIds(displayId: Int): Set<Int> = desktopData.desksSequence(displayId).map { desk -> desk.deskId }.toSet() + /** Returns all the ids of all desks in all displays. */ + fun getAllDeskIds(): Set<Int> = desktopData.desksSequence().map { desk -> desk.deskId }.toSet() + /** Returns the id of the default desk in the given display. */ fun getDefaultDeskId(displayId: Int): Int? = getDefaultDesk(displayId)?.deskId diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index ca870d2b6988..87d967427d88 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -1745,6 +1745,9 @@ class DesktopTasksController( launchWindowingMode = WINDOWING_MODE_FULLSCREEN pendingIntentBackgroundActivityStartMode = ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS + if (Flags.enablePerDisplayDesktopWallpaperActivity()) { + launchDisplayId = displayId + } } val pendingIntent = PendingIntent.getActivityAsUser( diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipSurfaceTransactionHelper.java index c4d065f158a4..e0cae81920ad 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipSurfaceTransactionHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipSurfaceTransactionHelper.java @@ -134,6 +134,18 @@ public class PipSurfaceTransactionHelper { } /** + * Operates the round corner radius on a given transaction and leash, scaled by bounds + * @return same {@link PipSurfaceTransactionHelper} instance for method chaining + */ + public PipSurfaceTransactionHelper round(SurfaceControl.Transaction tx, SurfaceControl leash, + Rect fromBounds, Rect toBounds) { + final float scale = (float) (Math.hypot(fromBounds.width(), fromBounds.height()) + / Math.hypot(toBounds.width(), toBounds.height())); + tx.setCornerRadius(leash, mCornerRadius * scale); + return this; + } + + /** * Operates the shadow radius on a given transaction and leash * @return same {@link PipSurfaceTransactionHelper} instance for method chaining */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java index 06e8349259b4..eb5fe88648b3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java @@ -173,7 +173,7 @@ public class PipResizeAnimator extends ValueAnimator { transformTensor.postRotate(degrees, targetBounds.centerX(), targetBounds.centerY()); tx.setMatrix(leash, transformTensor, mMatrixTmp) - .setCornerRadius(leash, cornerRadius) + .setCornerRadius(leash, cornerRadius / scaleX) .setShadowRadius(leash, shadowRadius); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java index df7a25af8376..7805ec34e105 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java @@ -55,6 +55,7 @@ public class PipScheduler { private PipTransitionController mPipTransitionController; private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mSurfaceControlTransactionFactory; + private final PipSurfaceTransactionHelper mPipSurfaceTransactionHelper; @Nullable private Runnable mUpdateMovementBoundsRunnable; @@ -75,6 +76,7 @@ public class PipScheduler { mSurfaceControlTransactionFactory = new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory(); + mPipSurfaceTransactionHelper = new PipSurfaceTransactionHelper(mContext); mPipAlphaAnimatorSupplier = PipAlphaAnimator::new; } @@ -214,6 +216,8 @@ public class PipScheduler { transformTensor.postTranslate(toBounds.left, toBounds.top); transformTensor.postRotate(degrees, toBounds.centerX(), toBounds.centerY()); + mPipSurfaceTransactionHelper.round(tx, leash, mPipBoundsState.getBounds(), toBounds); + tx.setMatrix(leash, transformTensor, mMatrixTmp); tx.apply(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java index 97b3e5a2da87..0cfab11dbc64 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java @@ -50,6 +50,7 @@ import android.graphics.PointF; import android.graphics.Rect; import android.os.Bundle; import android.os.IBinder; +import android.os.SystemProperties; import android.view.SurfaceControl; import android.view.WindowManager; import android.window.TransitionInfo; @@ -105,7 +106,10 @@ public class PipTransition extends PipTransitionController implements * The fixed start delay in ms when fading out the content overlay from bounds animation. * The fadeout animation is guaranteed to start after the client has drawn under the new config. */ - private static final int CONTENT_OVERLAY_FADE_OUT_DELAY_MS = 500; + private static final int EXTRA_CONTENT_OVERLAY_FADE_OUT_DELAY_MS = + SystemProperties.getInt( + "persist.wm.debug.extra_content_overlay_fade_out_delay_ms", 400); + private static final int CONTENT_OVERLAY_FADE_OUT_DURATION_MS = 500; // // Dependencies @@ -551,7 +555,8 @@ public class PipTransition extends PipTransitionController implements @NonNull Runnable onAnimationEnd) { PipAlphaAnimator animator = new PipAlphaAnimator(mContext, overlayLeash, null /* startTx */, null /* finishTx */, PipAlphaAnimator.FADE_OUT); - animator.setDuration(CONTENT_OVERLAY_FADE_OUT_DELAY_MS); + animator.setDuration(CONTENT_OVERLAY_FADE_OUT_DURATION_MS); + animator.setStartDelay(EXTRA_CONTENT_OVERLAY_FADE_OUT_DELAY_MS); animator.setAnimationEndCallback(onAnimationEnd); animator.start(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java index 2fa09664b73f..bb5b5cec1b4a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java @@ -44,6 +44,7 @@ import android.os.Bundle; import android.os.RemoteException; import android.util.Slog; import android.util.SparseIntArray; +import android.window.DesktopExperienceFlags; import android.window.DesktopModeFlags; import android.window.WindowContainerToken; @@ -92,6 +93,11 @@ public class RecentTasksController implements TaskStackListenerCallback, TaskStackTransitionObserver.TaskStackTransitionObserverListener, UserChangeListener { private static final String TAG = RecentTasksController.class.getSimpleName(); + // When the multiple desktops feature is disabled, all freeform tasks are lumped together into + // a single `GroupedTaskInfo` whose type is `TYPE_DESK`, and its `mDeskId` doesn't matter, so + // we pick the below arbitrary value. + private static final int INVALID_DESK_ID = -1; + private final Context mContext; private final ShellController mShellController; private final ShellCommandHandler mShellCommandHandler; @@ -128,6 +134,7 @@ public class RecentTasksController implements TaskStackListenerCallback, // Temporary vars used in `generateList()` private final Map<Integer, TaskInfo> mTmpRemaining = new HashMap<>(); + private final Map<Integer, Desk> mTmpDesks = new HashMap<>(); /** * Creates {@link RecentTasksController}, returns {@code null} if the feature is not @@ -529,6 +536,78 @@ public class RecentTasksController implements TaskStackListenerCallback, } /** + * Represents a desk whose ID is `mDeskId` and contains the tasks in `mDeskTasks`. Some of these + * tasks are minimized and their IDs are contained in the `mMinimizedDeskTasks` set. + */ + private static class Desk { + final int mDeskId; + boolean mHasVisibleTasks = false; + final ArrayList<TaskInfo> mDeskTasks = new ArrayList<>(); + final Set<Integer> mMinimizedDeskTasks = new HashSet<>(); + + Desk(int deskId) { + mDeskId = deskId; + } + + void addTask(TaskInfo taskInfo, boolean isMinimized, boolean isVisible) { + mDeskTasks.add(taskInfo); + if (isMinimized) { + mMinimizedDeskTasks.add(taskInfo.taskId); + } + mHasVisibleTasks |= isVisible; + } + + boolean hasTasks() { + return !mDeskTasks.isEmpty(); + } + + GroupedTaskInfo createDeskTaskInfo() { + return GroupedTaskInfo.forDeskTasks(mDeskId, mDeskTasks, mMinimizedDeskTasks); + } + } + + /** + * Clears the `mTmpDesks` map, and re-initializes it with the current state of desks from all + * displays, without adding any desk tasks. This is a preparation step so that tasks can be + * added to these desks in `generateList()`. + * + * This is needed since with the `ENABLE_MULTIPLE_DESKTOPS_BACKEND` flag, we want to include + * desk even if they're empty (i.e. have no tasks). + * + * @param multipleDesktopsEnabled whether the multiple desktops backend feature is enabled. + */ + private void initializeDesksMap(boolean multipleDesktopsEnabled) { + mTmpDesks.clear(); + + if (DesktopModeStatus.canEnterDesktopMode(mContext) + && mDesktopUserRepositories.isPresent()) { + if (multipleDesktopsEnabled) { + for (var deskId : mDesktopUserRepositories.get().getCurrent().getAllDeskIds()) { + getOrCreateDesk(deskId); + } + } else { + // When the multiple desks feature is disabled, we lump all freeform windows in a + // single `GroupedTaskInfo` regardless of their display. The `deskId` in this case + // doesn't matter and can be any arbitrary value. + getOrCreateDesk(/* deskId = */ INVALID_DESK_ID); + } + } + } + + /** + * Returns the `Desk` whose ID is `deskId` from the `mTmpDesks` map if it exists, or it creates + * one and adds it to the map and then returns it. + */ + private Desk getOrCreateDesk(int deskId) { + var desk = mTmpDesks.get(deskId); + if (desk == null) { + desk = new Desk(deskId); + mTmpDesks.put(deskId, desk); + } + return desk; + } + + /** * Generates a list of GroupedTaskInfos for the given raw list of tasks (either recents or * running tasks). * @@ -555,6 +634,14 @@ public class RecentTasksController implements TaskStackListenerCallback, ProtoLog.v(WM_SHELL_TASK_OBSERVER, "RecentTasksController.generateList(%s)", reason); } + final boolean multipleDesktopsEnabled = + DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue(); + initializeDesksMap(multipleDesktopsEnabled); + + // When the multiple desktops feature is enabled, we include all desks even if they're + // empty. + final boolean shouldIncludeEmptyDesktops = multipleDesktopsEnabled; + // Make a mapping of task id -> task info for the remaining tasks to be processed, this // mapping is used to keep track of split tasks that may exist later in the task list that // should be ignored because they've already been grouped @@ -566,11 +653,7 @@ public class RecentTasksController implements TaskStackListenerCallback, ArrayList<GroupedTaskInfo> groupedTasks = new ArrayList<>(tasks.size()); ArrayList<GroupedTaskInfo> visibleGroupedTasks = new ArrayList<>(); - // Phase 1: Extract the desktop and visible fullscreen/split tasks. We make new collections - // here as the GroupedTaskInfo can store them without copying - ArrayList<TaskInfo> desktopTasks = new ArrayList<>(); - Set<Integer> minimizedDesktopTasks = new HashSet<>(); - boolean desktopTasksVisible = false; + // Phase 1: Extract the desktops and visible fullscreen/split tasks. for (int i = 0; i < tasks.size(); i++) { final TaskInfo taskInfo = tasks.get(i); final int taskId = taskInfo.taskId; @@ -600,11 +683,15 @@ public class RecentTasksController implements TaskStackListenerCallback, taskInfo.positionInParent = new Point(taskInfo.lastNonFullscreenBounds.left, taskInfo.lastNonFullscreenBounds.top); } - desktopTasks.add(taskInfo); - if (mDesktopUserRepositories.get().getCurrent().isMinimizedTask(taskId)) { - minimizedDesktopTasks.add(taskId); - } - desktopTasksVisible |= mVisibleTasksMap.containsKey(taskId); + // Lump all freeform tasks together as if they were all in a single desk whose ID is + // `INVALID_DESK_ID` when the multiple desktops feature is disabled. + final int deskId = multipleDesktopsEnabled + ? mDesktopUserRepositories.get().getCurrent().getDeskIdForTask(taskId) + : INVALID_DESK_ID; + final Desk desk = getOrCreateDesk(deskId); + desk.addTask(taskInfo, + mDesktopUserRepositories.get().getCurrent().isMinimizedTask(taskId), + mVisibleTasksMap.containsKey(taskId)); mTmpRemaining.remove(taskId); continue; } @@ -636,9 +723,10 @@ public class RecentTasksController implements TaskStackListenerCallback, if (enableShellTopTaskTracking()) { // Phase 2: If there were desktop tasks and they are visible, add them to the visible // list as well (the actual order doesn't matter for Overview) - if (!desktopTasks.isEmpty() && desktopTasksVisible) { - visibleGroupedTasks.add( - GroupedTaskInfo.forFreeformTasks(desktopTasks, minimizedDesktopTasks)); + for (var desk : mTmpDesks.values()) { + if (desk.mHasVisibleTasks) { + visibleGroupedTasks.add(desk.createDeskTaskInfo()); + } } if (!visibleGroupedTasks.isEmpty()) { @@ -674,16 +762,18 @@ public class RecentTasksController implements TaskStackListenerCallback, // Phase 5: If there were desktop tasks and they are not visible (ie. weren't added // above), add them to the end of the final list (the actual order doesn't // matter for Overview) - if (!desktopTasks.isEmpty() && !desktopTasksVisible) { - groupedTasks.add( - GroupedTaskInfo.forFreeformTasks(desktopTasks, minimizedDesktopTasks)); + for (var desk : mTmpDesks.values()) { + if (!desk.mHasVisibleTasks && (desk.hasTasks() || shouldIncludeEmptyDesktops)) { + groupedTasks.add(desk.createDeskTaskInfo()); + } } dumpGroupedTasks(groupedTasks, "Phase 5"); } else { // Add the desktop tasks at the end of the list - if (!desktopTasks.isEmpty()) { - groupedTasks.add( - GroupedTaskInfo.forFreeformTasks(desktopTasks, minimizedDesktopTasks)); + for (var desk : mTmpDesks.values()) { + if (desk.hasTasks() || shouldIncludeEmptyDesktops) { + groupedTasks.add(desk.createDeskTaskInfo()); + } } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt index 0b160c6844ae..9a73b022ebc3 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt @@ -2809,6 +2809,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase() // Should launch home wct.assertPendingIntentAt(0, launchHomeIntent(DEFAULT_DISPLAY)) + wct.assertPendingIntentActivityOptionsLaunchDisplayIdAt(0, DEFAULT_DISPLAY) } @Test @@ -3931,6 +3932,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase() // Should launch home assertNotNull(result, "Should handle request") .assertPendingIntentAt(0, launchHomeIntent(DEFAULT_DISPLAY)) + result!!.assertPendingIntentActivityOptionsLaunchDisplayIdAt(0, DEFAULT_DISPLAY) } @Test @@ -3957,6 +3959,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase() // Should launch home assertNotNull(result, "Should handle request") .assertPendingIntentAt(0, launchHomeIntent(DEFAULT_DISPLAY)) + result!!.assertPendingIntentActivityOptionsLaunchDisplayIdAt(0, DEFAULT_DISPLAY) } @Test @@ -4089,6 +4092,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase() // Should launch home assertNotNull(result, "Should handle request") .assertPendingIntentAt(0, launchHomeIntent(DEFAULT_DISPLAY)) + result!!.assertPendingIntentActivityOptionsLaunchDisplayIdAt(0, DEFAULT_DISPLAY) } @Test @@ -4105,6 +4109,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase() // Should launch home assertNotNull(result, "Should handle request") .assertPendingIntentAt(0, launchHomeIntent(SECOND_DISPLAY)) + result!!.assertPendingIntentActivityOptionsLaunchDisplayIdAt(0, SECOND_DISPLAY) } @Test @@ -6898,6 +6903,18 @@ private fun WindowContainerTransaction.assertPendingIntentAt(index: Int, intent: assertThat(op.pendingIntent?.intent?.categories).isEqualTo(intent.categories) } +private fun WindowContainerTransaction.assertPendingIntentActivityOptionsLaunchDisplayIdAt( + index: Int, + displayId: Int, +) { + assertIndexInBounds(index) + val op = hierarchyOps[index] + if (op.launchOptions != null) { + val options = ActivityOptions(op.launchOptions) + assertThat(options.launchDisplayId).isEqualTo(displayId) + } +} + private fun WindowContainerTransaction.assertLaunchTask(taskId: Int, windowingMode: Int) { val keyLaunchWindowingMode = "android.activity.windowingMode" diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedTaskInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedTaskInfoTest.kt index 32096645aea7..6ecebd76a951 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedTaskInfoTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedTaskInfoTest.kt @@ -26,7 +26,7 @@ import android.window.WindowContainerToken import androidx.test.filters.SmallTest import com.android.wm.shell.ShellTestCase import com.android.wm.shell.shared.GroupedTaskInfo -import com.android.wm.shell.shared.GroupedTaskInfo.TYPE_FREEFORM +import com.android.wm.shell.shared.GroupedTaskInfo.TYPE_DESK import com.android.wm.shell.shared.GroupedTaskInfo.TYPE_FULLSCREEN import com.android.wm.shell.shared.GroupedTaskInfo.TYPE_SPLIT import com.android.wm.shell.shared.split.SplitBounds @@ -87,14 +87,14 @@ class GroupedTaskInfoTest : ShellTestCase() { } @Test - fun testFreeformTasks_hasCorrectType() { - assertThat(freeformTasksGroupInfo(freeformTaskIds = arrayOf(1)).isBaseType(TYPE_FREEFORM)) + fun testDeskTasks_hasCorrectType() { + assertThat(deskTasksGroupInfo(deskId = 0, freeformTaskIds = arrayOf(1)).isBaseType(TYPE_DESK)) .isTrue() } @Test - fun testCreateFreeformTasks_hasCorrectNumberOfTasks() { - val list = freeformTasksGroupInfo(freeformTaskIds = arrayOf(1, 2, 3)).taskInfoList + fun testCreateDeskTasks_hasCorrectNumberOfTasks() { + val list = deskTasksGroupInfo(deskId = 0, freeformTaskIds = arrayOf(1, 2, 3)).taskInfoList assertThat(list).hasSize(3) assertThat(list[0].taskId).isEqualTo(1) assertThat(list[1].taskId).isEqualTo(2) @@ -102,9 +102,9 @@ class GroupedTaskInfoTest : ShellTestCase() { } @Test - fun testCreateFreeformTasks_nonExistentMinimizedTaskId_throwsException() { + fun testCreateDeskTasks_nonExistentMinimizedTaskId_throwsException() { assertThrows(IllegalArgumentException::class.java) { - freeformTasksGroupInfo( + deskTasksGroupInfo(deskId = 0, freeformTaskIds = arrayOf(1, 2, 3), minimizedTaskIds = arrayOf(1, 4) ) @@ -122,8 +122,8 @@ class GroupedTaskInfoTest : ShellTestCase() { } @Test - fun testMixedWithFreeformBase_hasCorrectType() { - assertThat(mixedTaskGroupInfoWithFreeformBase().isBaseType(TYPE_FREEFORM)).isTrue() + fun testMixedWithDeskBase_hasCorrectType() { + assertThat(mixedTaskGroupInfoWithDeskBase().isBaseType(TYPE_DESK)).isTrue() } @Test @@ -190,15 +190,16 @@ class GroupedTaskInfoTest : ShellTestCase() { } @Test - fun testParcelling_freeformTasks() { - val taskInfo = freeformTasksGroupInfo(freeformTaskIds = arrayOf(1, 2, 3)) + fun testParcelling_DeskTasks() { + val taskInfo = deskTasksGroupInfo(deskId = 50, freeformTaskIds = arrayOf(1, 2, 3)) val parcel = Parcel.obtain() taskInfo.writeToParcel(parcel, 0) parcel.setDataPosition(0) // Read the object back from the parcel val taskInfoFromParcel: GroupedTaskInfo = GroupedTaskInfo.CREATOR.createFromParcel(parcel) - assertThat(taskInfoFromParcel.isBaseType(TYPE_FREEFORM)).isTrue() + assertThat(taskInfoFromParcel.deskId).isEqualTo(taskInfo.deskId) + assertThat(taskInfoFromParcel.isBaseType(TYPE_DESK)).isTrue() assertThat(taskInfoFromParcel.taskInfoList).hasSize(3) // Only compare task ids val taskIdComparator = Correspondence.transforming<TaskInfo, Int>( @@ -209,8 +210,8 @@ class GroupedTaskInfoTest : ShellTestCase() { } @Test - fun testParcelling_freeformTasks_minimizedTasks() { - val taskInfo = freeformTasksGroupInfo( + fun testParcelling_DeskTasks_minimizedTasks() { + val taskInfo = deskTasksGroupInfo(deskId = 0, freeformTaskIds = arrayOf(1, 2, 3), minimizedTaskIds = arrayOf(2)) val parcel = Parcel.obtain() @@ -220,14 +221,14 @@ class GroupedTaskInfoTest : ShellTestCase() { // Read the object back from the parcel val taskInfoFromParcel: GroupedTaskInfo = GroupedTaskInfo.CREATOR.createFromParcel(parcel) - assertThat(taskInfoFromParcel.isBaseType(TYPE_FREEFORM)).isTrue() + assertThat(taskInfoFromParcel.isBaseType(TYPE_DESK)).isTrue() assertThat(taskInfoFromParcel.minimizedTaskIds).isEqualTo(arrayOf(2).toIntArray()) } @Test fun testParcelling_mixedTasks() { val taskInfo = GroupedTaskInfo.forMixed(listOf( - freeformTasksGroupInfo(freeformTaskIds = arrayOf(4, 5, 6), + deskTasksGroupInfo(deskId = 0, freeformTaskIds = arrayOf(4, 5, 6), minimizedTaskIds = arrayOf(5)), splitTasksGroupInfo(firstId = 2, secondId = 3), singleTaskGroupInfo(id = 1))) @@ -239,7 +240,7 @@ class GroupedTaskInfoTest : ShellTestCase() { // Read the object back from the parcel val taskInfoFromParcel: GroupedTaskInfo = GroupedTaskInfo.CREATOR.createFromParcel(parcel) - assertThat(taskInfoFromParcel.isBaseType(TYPE_FREEFORM)).isTrue() + assertThat(taskInfoFromParcel.isBaseType(TYPE_DESK)).isTrue() assertThat(taskInfoFromParcel.baseGroupedTask.minimizedTaskIds).isEqualTo( arrayOf(5).toIntArray()) for (i in 1..6) { @@ -275,12 +276,14 @@ class GroupedTaskInfoTest : ShellTestCase() { } @Test - fun testTaskProperties_freeformTasks() { + fun testTaskProperties_DeskTasks() { val task1 = createTaskInfo(id = 1) val task2 = createTaskInfo(id = 2) - val taskInfo = GroupedTaskInfo.forFreeformTasks(listOf(task1, task2), setOf()) + val taskInfo = GroupedTaskInfo.forDeskTasks( + /* deskId = */ 500, listOf(task1, task2), setOf()) + assertThat(taskInfo.deskId).isEqualTo(500) assertThat(taskInfo.getTaskById(1)).isEqualTo(task1) assertThat(taskInfo.getTaskById(2)).isEqualTo(task2) assertThat(taskInfo.containsTask(1)).isTrue() @@ -325,11 +328,13 @@ class GroupedTaskInfoTest : ShellTestCase() { return GroupedTaskInfo.forSplitTasks(task1, task2, splitBounds) } - private fun freeformTasksGroupInfo( + private fun deskTasksGroupInfo( + deskId: Int, freeformTaskIds: Array<Int>, minimizedTaskIds: Array<Int> = emptyArray() ): GroupedTaskInfo { - return GroupedTaskInfo.forFreeformTasks( + return GroupedTaskInfo.forDeskTasks( + deskId, freeformTaskIds.map { createTaskInfo(it) }.toList(), minimizedTaskIds.toSet()) } @@ -346,9 +351,9 @@ class GroupedTaskInfoTest : ShellTestCase() { singleTaskGroupInfo(id = 1))) } - private fun mixedTaskGroupInfoWithFreeformBase(): GroupedTaskInfo { + private fun mixedTaskGroupInfoWithDeskBase(): GroupedTaskInfo { return GroupedTaskInfo.forMixed(listOf( - freeformTasksGroupInfo(freeformTaskIds = arrayOf(2, 3, 4)), + deskTasksGroupInfo(deskId = 0, freeformTaskIds = arrayOf(2, 3, 4)), singleTaskGroupInfo(id = 1))) } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java index a546b3ea7d8f..bf65caec88c7 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java @@ -24,7 +24,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE; -import static com.android.wm.shell.shared.GroupedTaskInfo.TYPE_FREEFORM; +import static com.android.wm.shell.shared.GroupedTaskInfo.TYPE_DESK; import static com.android.wm.shell.shared.GroupedTaskInfo.TYPE_FULLSCREEN; import static com.android.wm.shell.shared.GroupedTaskInfo.TYPE_SPLIT; import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50; @@ -349,7 +349,7 @@ public class RecentTasksControllerTest extends ShellTestCase { GroupedTaskInfo freeformGroup = recentTasks.get(2); // Check that groups have expected types - assertTrue(freeformGroup.isBaseType(TYPE_FREEFORM)); + assertTrue(freeformGroup.isBaseType(TYPE_DESK)); assertTrue(singleGroup1.isBaseType(TYPE_FULLSCREEN)); assertTrue(singleGroup2.isBaseType(TYPE_FULLSCREEN)); @@ -389,7 +389,7 @@ public class RecentTasksControllerTest extends ShellTestCase { // Check that groups have expected types assertTrue(splitGroup.isBaseType(TYPE_SPLIT)); - assertTrue(freeformGroup.isBaseType(TYPE_FREEFORM)); + assertTrue(freeformGroup.isBaseType(TYPE_DESK)); assertTrue(singleGroup.isBaseType(TYPE_FULLSCREEN)); // Check freeform group entries @@ -460,7 +460,7 @@ public class RecentTasksControllerTest extends ShellTestCase { GroupedTaskInfo freeformGroup = recentTasks.get(2); // Check that groups have expected types - assertTrue(freeformGroup.isBaseType(TYPE_FREEFORM)); + assertTrue(freeformGroup.isBaseType(TYPE_DESK)); assertTrue(singleGroup1.isBaseType(TYPE_FULLSCREEN)); assertTrue(singleGroup2.isBaseType(TYPE_FULLSCREEN)); diff --git a/libs/hostgraphics/Fence.cpp b/libs/hostgraphics/Fence.cpp index 4383bf02a00e..938a9384768d 100644 --- a/libs/hostgraphics/Fence.cpp +++ b/libs/hostgraphics/Fence.cpp @@ -18,6 +18,6 @@ namespace android { -const sp<Fence> Fence::NO_FENCE = sp<Fence>(new Fence); +const sp<Fence> Fence::NO_FENCE = sp<Fence>::make(); } // namespace android diff --git a/packages/SettingsLib/ActionButtonsPreference/res/layout-v35/settingslib_expressive_action_buttons.xml b/packages/SettingsLib/ActionButtonsPreference/res/layout-v36/settingslib_expressive_action_buttons.xml index 3e73ebd4880e..3e73ebd4880e 100644 --- a/packages/SettingsLib/ActionButtonsPreference/res/layout-v35/settingslib_expressive_action_buttons.xml +++ b/packages/SettingsLib/ActionButtonsPreference/res/layout-v36/settingslib_expressive_action_buttons.xml diff --git a/packages/SettingsLib/ActionButtonsPreference/res/values-v35/styles_expressive.xml b/packages/SettingsLib/ActionButtonsPreference/res/values-v36/styles_expressive.xml index 267c9f65e104..267c9f65e104 100644 --- a/packages/SettingsLib/ActionButtonsPreference/res/values-v35/styles_expressive.xml +++ b/packages/SettingsLib/ActionButtonsPreference/res/values-v36/styles_expressive.xml diff --git a/packages/SettingsLib/BannerMessagePreference/res/drawable-v35/settingslib_expressive_card_background.xml b/packages/SettingsLib/BannerMessagePreference/res/drawable-v36/settingslib_expressive_card_background.xml index a677a66f86e7..a677a66f86e7 100644 --- a/packages/SettingsLib/BannerMessagePreference/res/drawable-v35/settingslib_expressive_card_background.xml +++ b/packages/SettingsLib/BannerMessagePreference/res/drawable-v36/settingslib_expressive_card_background.xml diff --git a/packages/SettingsLib/BannerMessagePreference/res/drawable-v35/settingslib_resolved_banner_avd.xml b/packages/SettingsLib/BannerMessagePreference/res/drawable-v36/settingslib_resolved_banner_avd.xml index c999de7d99ea..c999de7d99ea 100644 --- a/packages/SettingsLib/BannerMessagePreference/res/drawable-v35/settingslib_resolved_banner_avd.xml +++ b/packages/SettingsLib/BannerMessagePreference/res/drawable-v36/settingslib_resolved_banner_avd.xml diff --git a/packages/SettingsLib/BannerMessagePreference/res/layout-v35/settingslib_expressive_banner_message.xml b/packages/SettingsLib/BannerMessagePreference/res/layout-v36/settingslib_expressive_banner_message.xml index c448a2d434f8..c448a2d434f8 100644 --- a/packages/SettingsLib/BannerMessagePreference/res/layout-v35/settingslib_expressive_banner_message.xml +++ b/packages/SettingsLib/BannerMessagePreference/res/layout-v36/settingslib_expressive_banner_message.xml diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-v35/styles_expressive.xml b/packages/SettingsLib/BannerMessagePreference/res/values-v36/styles_expressive.xml index 09e07ccef683..09e07ccef683 100644 --- a/packages/SettingsLib/BannerMessagePreference/res/values-v35/styles_expressive.xml +++ b/packages/SettingsLib/BannerMessagePreference/res/values-v36/styles_expressive.xml diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled.xml b/packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_filled.xml index ff22b2e7f86f..ff22b2e7f86f 100644 --- a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled.xml +++ b/packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_filled.xml diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled_extra.xml b/packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_filled_extra.xml index d878ba0d310b..d878ba0d310b 100644 --- a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled_extra.xml +++ b/packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_filled_extra.xml diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled_large.xml b/packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_filled_large.xml index 8f0a158c55eb..8f0a158c55eb 100644 --- a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled_large.xml +++ b/packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_filled_large.xml diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline.xml b/packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_outline.xml index 0c8996063e9f..0c8996063e9f 100644 --- a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline.xml +++ b/packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_outline.xml diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline_extra.xml b/packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_outline_extra.xml index 41d8490feeb3..41d8490feeb3 100644 --- a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline_extra.xml +++ b/packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_outline_extra.xml diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline_large.xml b/packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_outline_large.xml index 958552064c98..958552064c98 100644 --- a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline_large.xml +++ b/packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_outline_large.xml diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal.xml b/packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_tonal.xml index 03ca1f0a1033..03ca1f0a1033 100644 --- a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal.xml +++ b/packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_tonal.xml diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal_extra.xml b/packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_tonal_extra.xml index 030ee66fef3f..030ee66fef3f 100644 --- a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal_extra.xml +++ b/packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_tonal_extra.xml diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal_large.xml b/packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_tonal_large.xml index 5c16723f7a63..5c16723f7a63 100644 --- a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal_large.xml +++ b/packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_tonal_large.xml diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/drawable-v35/settingslib_expressive_icon_back.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/drawable-v36/settingslib_expressive_icon_back.xml index 9986a60250fe..9986a60250fe 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/drawable-v35/settingslib_expressive_icon_back.xml +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/drawable-v36/settingslib_expressive_icon_back.xml diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v35/settingslib_expressive_collapsing_toolbar_base_layout.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v36/settingslib_expressive_collapsing_toolbar_base_layout.xml index b881c57e2f53..b881c57e2f53 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v35/settingslib_expressive_collapsing_toolbar_base_layout.xml +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v36/settingslib_expressive_collapsing_toolbar_base_layout.xml diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v35/settingslib_expressive_collapsing_toolbar_content_layout.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v36/settingslib_expressive_collapsing_toolbar_content_layout.xml index 6221659388d1..6221659388d1 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v35/settingslib_expressive_collapsing_toolbar_content_layout.xml +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v36/settingslib_expressive_collapsing_toolbar_content_layout.xml diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night-v35/themes.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night-v36/themes.xml index fadcf7ba8699..fadcf7ba8699 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night-v35/themes.xml +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night-v36/themes.xml diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/styles.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v36/styles.xml index 0f71a788e8c6..0f71a788e8c6 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/styles.xml +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v36/styles.xml diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/styles_expressive.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v36/styles_expressive.xml index 37a78101cc4e..37a78101cc4e 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/styles_expressive.xml +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v36/styles_expressive.xml diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/themes.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v36/themes.xml index 7c9d1a47b7ef..7c9d1a47b7ef 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/themes.xml +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v36/themes.xml diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/themes_expressive.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v36/themes_expressive.xml index ca1904a15b2f..ca1904a15b2f 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/themes_expressive.xml +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v36/themes_expressive.xml diff --git a/packages/SettingsLib/MainSwitchPreference/res/color-night-v35/settingslib_main_switch_text_color.xml b/packages/SettingsLib/MainSwitchPreference/res/color-night-v36/settingslib_main_switch_text_color.xml index ea15a67e93cd..ea15a67e93cd 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/color-night-v35/settingslib_main_switch_text_color.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/color-night-v36/settingslib_main_switch_text_color.xml diff --git a/packages/SettingsLib/MainSwitchPreference/res/color-v35/settingslib_main_switch_text_color.xml b/packages/SettingsLib/MainSwitchPreference/res/color-v36/settingslib_main_switch_text_color.xml index ea15a67e93cd..ea15a67e93cd 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/color-v35/settingslib_main_switch_text_color.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/color-v36/settingslib_main_switch_text_color.xml diff --git a/packages/SettingsLib/MainSwitchPreference/res/drawable-v35/settingslib_expressive_switch_bar_bg.xml b/packages/SettingsLib/MainSwitchPreference/res/drawable-v36/settingslib_expressive_switch_bar_bg.xml index 3999e762ea9e..3999e762ea9e 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/drawable-v35/settingslib_expressive_switch_bar_bg.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/drawable-v36/settingslib_expressive_switch_bar_bg.xml diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v36/settingslib_expressive_main_switch_bar.xml index f75d9b23c49b..f75d9b23c49b 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_bar.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v36/settingslib_expressive_main_switch_bar.xml diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_layout.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v36/settingslib_expressive_main_switch_layout.xml index 94c6924a02f2..94c6924a02f2 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_layout.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v36/settingslib_expressive_main_switch_layout.xml diff --git a/packages/SettingsLib/ProfileSelector/res/color-night-v35/settingslib_tabs_indicator_color.xml b/packages/SettingsLib/ProfileSelector/res/color-night-v36/settingslib_tabs_indicator_color.xml index 5192a9a53572..5192a9a53572 100644 --- a/packages/SettingsLib/ProfileSelector/res/color-night-v35/settingslib_tabs_indicator_color.xml +++ b/packages/SettingsLib/ProfileSelector/res/color-night-v36/settingslib_tabs_indicator_color.xml diff --git a/packages/SettingsLib/SettingsTheme/res/color-night-v35/settingslib_switch_track_outline_color.xml b/packages/SettingsLib/SettingsTheme/res/color-night-v36/settingslib_switch_track_outline_color.xml index eedc364ff54b..eedc364ff54b 100644 --- a/packages/SettingsLib/SettingsTheme/res/color-night-v35/settingslib_switch_track_outline_color.xml +++ b/packages/SettingsLib/SettingsTheme/res/color-night-v36/settingslib_switch_track_outline_color.xml diff --git a/packages/SettingsLib/SettingsTheme/res/color-v35/settingslib_expressive_color_main_switch_track.xml b/packages/SettingsLib/SettingsTheme/res/color-v36/settingslib_expressive_color_main_switch_track.xml index a6885a4ac358..a6885a4ac358 100644 --- a/packages/SettingsLib/SettingsTheme/res/color-v35/settingslib_expressive_color_main_switch_track.xml +++ b/packages/SettingsLib/SettingsTheme/res/color-v36/settingslib_expressive_color_main_switch_track.xml diff --git a/packages/SettingsLib/SettingsTheme/res/color-v35/settingslib_preference_bg_color.xml b/packages/SettingsLib/SettingsTheme/res/color-v36/settingslib_preference_bg_color.xml index cece9665b729..cece9665b729 100644 --- a/packages/SettingsLib/SettingsTheme/res/color-v35/settingslib_preference_bg_color.xml +++ b/packages/SettingsLib/SettingsTheme/res/color-v36/settingslib_preference_bg_color.xml diff --git a/packages/SettingsLib/SettingsTheme/res/color-v35/settingslib_switch_track_outline_color.xml b/packages/SettingsLib/SettingsTheme/res/color-v36/settingslib_switch_track_outline_color.xml index eedc364ff54b..eedc364ff54b 100644 --- a/packages/SettingsLib/SettingsTheme/res/color-v35/settingslib_switch_track_outline_color.xml +++ b/packages/SettingsLib/SettingsTheme/res/color-v36/settingslib_switch_track_outline_color.xml diff --git a/packages/SettingsLib/SettingsTheme/res/color-v35/settingslib_text_color_primary.xml b/packages/SettingsLib/SettingsTheme/res/color-v36/settingslib_text_color_primary.xml index 230eb7d30aea..230eb7d30aea 100644 --- a/packages/SettingsLib/SettingsTheme/res/color-v35/settingslib_text_color_primary.xml +++ b/packages/SettingsLib/SettingsTheme/res/color-v36/settingslib_text_color_primary.xml diff --git a/packages/SettingsLib/SettingsTheme/res/color-v35/settingslib_text_color_secondary.xml b/packages/SettingsLib/SettingsTheme/res/color-v36/settingslib_text_color_secondary.xml index 5bd2a29ccf9a..5bd2a29ccf9a 100644 --- a/packages/SettingsLib/SettingsTheme/res/color-v35/settingslib_text_color_secondary.xml +++ b/packages/SettingsLib/SettingsTheme/res/color-v36/settingslib_text_color_secondary.xml diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_arrow_drop_down.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_arrow_drop_down.xml index 875524775a3c..875524775a3c 100644 --- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_arrow_drop_down.xml +++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_arrow_drop_down.xml diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_list_divider.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_list_divider.xml index c17f7ee8c61e..c17f7ee8c61e 100644 --- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_list_divider.xml +++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_list_divider.xml diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_progress_horizontal.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_progress_horizontal.xml index 3cb34354de17..3cb34354de17 100644 --- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_progress_horizontal.xml +++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_progress_horizontal.xml diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background.xml index 9aa0bc39c5d8..9aa0bc39c5d8 100644 --- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background.xml +++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background.xml diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_bottom.xml index 554cba565383..554cba565383 100644 --- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom.xml +++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_bottom.xml diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom_highlighted.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_bottom_highlighted.xml index c0c08699cc2a..c0c08699cc2a 100644 --- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom_highlighted.xml +++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_bottom_highlighted.xml diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom_selected.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_bottom_selected.xml index 543b237373fb..543b237373fb 100644 --- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom_selected.xml +++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_bottom_selected.xml diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_center.xml index b89a0ddcdec5..b89a0ddcdec5 100644 --- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center.xml +++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_center.xml diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center_highlighted.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_center_highlighted.xml index 8099d9b3d7f7..8099d9b3d7f7 100644 --- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center_highlighted.xml +++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_center_highlighted.xml diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center_selected.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_center_selected.xml index 6d2cd1a51620..6d2cd1a51620 100644 --- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center_selected.xml +++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_center_selected.xml diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_highlighted.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_highlighted.xml index a119a4ae083f..a119a4ae083f 100644 --- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_highlighted.xml +++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_highlighted.xml diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_selected.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_selected.xml index bcdbf1d19545..bcdbf1d19545 100644 --- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_selected.xml +++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_selected.xml diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_top.xml index 7955e4418ae9..7955e4418ae9 100644 --- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top.xml +++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_top.xml diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top_highlighted.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_top_highlighted.xml index 052eb01cab8d..052eb01cab8d 100644 --- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top_highlighted.xml +++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_top_highlighted.xml diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top_selected.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_top_selected.xml index d4b658c384e6..d4b658c384e6 100644 --- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top_selected.xml +++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_top_selected.xml diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_spinner_background.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_spinner_background.xml index 20ee38190b01..20ee38190b01 100644 --- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_spinner_background.xml +++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_spinner_background.xml diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_spinner_dropdown_background.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_spinner_dropdown_background.xml index b287c3bb546f..b287c3bb546f 100644 --- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_spinner_dropdown_background.xml +++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_spinner_dropdown_background.xml diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference.xml b/packages/SettingsLib/SettingsTheme/res/layout-v36/settingslib_expressive_preference.xml index 4ef747a99b49..4ef747a99b49 100644 --- a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference.xml +++ b/packages/SettingsLib/SettingsTheme/res/layout-v36/settingslib_expressive_preference.xml diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_icon_frame.xml b/packages/SettingsLib/SettingsTheme/res/layout-v36/settingslib_expressive_preference_icon_frame.xml index 19818e0b06da..19818e0b06da 100644 --- a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_icon_frame.xml +++ b/packages/SettingsLib/SettingsTheme/res/layout-v36/settingslib_expressive_preference_icon_frame.xml diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_switch.xml b/packages/SettingsLib/SettingsTheme/res/layout-v36/settingslib_expressive_preference_switch.xml index 4abcd22d67cc..4abcd22d67cc 100644 --- a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_switch.xml +++ b/packages/SettingsLib/SettingsTheme/res/layout-v36/settingslib_expressive_preference_switch.xml diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_two_target_divider.xml b/packages/SettingsLib/SettingsTheme/res/layout-v36/settingslib_expressive_two_target_divider.xml index 9be9ec331984..9be9ec331984 100644 --- a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_two_target_divider.xml +++ b/packages/SettingsLib/SettingsTheme/res/layout-v36/settingslib_expressive_two_target_divider.xml diff --git a/packages/SettingsLib/SettingsTheme/res/values-night-v35/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-night-v36/colors.xml index e31e80176625..e31e80176625 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-night-v35/colors.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-night-v36/colors.xml diff --git a/packages/SettingsLib/SettingsTheme/res/values-v35/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-v36/colors.xml index b1b37b12c572..b1b37b12c572 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-v35/colors.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v36/colors.xml diff --git a/packages/SettingsLib/SettingsTheme/res/values-v35/dimens.xml b/packages/SettingsLib/SettingsTheme/res/values-v36/dimens.xml index 193ae618e803..193ae618e803 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-v35/dimens.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v36/dimens.xml diff --git a/packages/SettingsLib/SettingsTheme/res/values-v35/styles_expressive.xml b/packages/SettingsLib/SettingsTheme/res/values-v36/styles_expressive.xml index 3af88c48e8ca..3af88c48e8ca 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-v35/styles_expressive.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v36/styles_expressive.xml diff --git a/packages/SettingsLib/SettingsTheme/res/values-v35/styles_preference_expressive.xml b/packages/SettingsLib/SettingsTheme/res/values-v36/styles_preference_expressive.xml index cec8e45e2bfb..cec8e45e2bfb 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-v35/styles_preference_expressive.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v36/styles_preference_expressive.xml diff --git a/packages/SettingsLib/SettingsTheme/res/values-v35/themes.xml b/packages/SettingsLib/SettingsTheme/res/values-v36/themes.xml index b6e80c784f10..1c45ff6ca6cf 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-v35/themes.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v36/themes.xml @@ -16,7 +16,7 @@ --> <resources> - <style name="Theme.SettingsBase_v35" parent="Theme.SettingsBase_v33" > + <style name="Theme.SettingsBase_v36" parent="Theme.SettingsBase_v33" > <item name="android:colorAccent">@color/settingslib_materialColorPrimary</item> <item name="android:colorBackground">@color/settingslib_materialColorSurfaceContainer</item> <item name="android:textColorPrimary">@color/settingslib_materialColorOnSurface</item> @@ -27,5 +27,5 @@ <item name="android:clipChildren">false</item> </style> - <style name="Theme.SettingsBase" parent="Theme.SettingsBase_v35" /> + <style name="Theme.SettingsBase" parent="Theme.SettingsBase_v36" /> </resources>
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsTheme/res/values-v35/themes_expressive.xml b/packages/SettingsLib/SettingsTheme/res/values-v36/themes_expressive.xml index 14f214a96435..14f214a96435 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-v35/themes_expressive.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v36/themes_expressive.xml diff --git a/packages/SettingsLib/SettingsTheme/res/values-v35/themes_preference_expressive.xml b/packages/SettingsLib/SettingsTheme/res/values-v36/themes_preference_expressive.xml index 41fe2250f0ad..41fe2250f0ad 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-v35/themes_preference_expressive.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v36/themes_preference_expressive.xml diff --git a/packages/SettingsLib/TwoTargetPreference/res/layout-v35/settingslib_expressive_preference_two_target.xml b/packages/SettingsLib/TwoTargetPreference/res/layout-v36/settingslib_expressive_preference_two_target.xml index 4347ef29037d..4347ef29037d 100644 --- a/packages/SettingsLib/TwoTargetPreference/res/layout-v35/settingslib_expressive_preference_two_target.xml +++ b/packages/SettingsLib/TwoTargetPreference/res/layout-v36/settingslib_expressive_preference_two_target.xml diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index eb5b22f6c82c..f2c76ba25079 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -1939,6 +1939,16 @@ flag { } flag { + name: "notification_appear_nonlinear" + namespace: "systemui" + description: "Fix linear usage of notification appear" + bug: "397658189" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "disable_shade_trackpad_two_finger_swipe" namespace: "systemui" description: "Disables expansion of the shade via two finger swipe on a trackpad" diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/BouncerSceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/BouncerOverlayModule.kt index 5b368df9d0ef..426942439391 100644 --- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/BouncerSceneModule.kt +++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/BouncerOverlayModule.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Android Open Source Project + * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,14 +16,13 @@ package com.android.systemui.scene -import com.android.systemui.bouncer.ui.composable.BouncerScene -import com.android.systemui.scene.ui.composable.Scene +import com.android.systemui.bouncer.ui.composable.BouncerOverlay +import com.android.systemui.scene.ui.composable.Overlay import dagger.Binds import dagger.Module import dagger.multibindings.IntoSet @Module -interface BouncerSceneModule { - - @Binds @IntoSet fun bouncerScene(scene: BouncerScene): Scene +interface BouncerOverlayModule { + @Binds @IntoSet fun bouncer(overlay: BouncerOverlay): Overlay } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt index 456edaf1012e..48e52d6c5fb2 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt @@ -98,7 +98,7 @@ import com.android.systemui.bouncer.shared.model.BouncerActionButtonModel import com.android.systemui.bouncer.ui.BouncerDialogFactory import com.android.systemui.bouncer.ui.viewmodel.AuthMethodBouncerViewModel import com.android.systemui.bouncer.ui.viewmodel.BouncerMessageViewModel -import com.android.systemui.bouncer.ui.viewmodel.BouncerSceneContentViewModel +import com.android.systemui.bouncer.ui.viewmodel.BouncerOverlayContentViewModel import com.android.systemui.bouncer.ui.viewmodel.MessageViewModel import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel import com.android.systemui.bouncer.ui.viewmodel.PatternBouncerViewModel @@ -118,7 +118,7 @@ import platform.test.motion.compose.values.motionTestValues @Composable fun BouncerContent( - viewModel: BouncerSceneContentViewModel, + viewModel: BouncerOverlayContentViewModel, dialogFactory: BouncerDialogFactory, modifier: Modifier = Modifier, ) { @@ -131,8 +131,8 @@ fun BouncerContent( @Composable @VisibleForTesting fun BouncerContent( - layout: BouncerSceneLayout, - viewModel: BouncerSceneContentViewModel, + layout: BouncerOverlayLayout, + viewModel: BouncerOverlayContentViewModel, dialogFactory: BouncerDialogFactory, modifier: Modifier, ) { @@ -147,11 +147,12 @@ fun BouncerContent( modifier = modifier.imePadding().onKeyEvent(viewModel::onKeyEvent).scale(scale) ) { when (layout) { - BouncerSceneLayout.STANDARD_BOUNCER -> StandardLayout(viewModel = viewModel) - BouncerSceneLayout.BESIDE_USER_SWITCHER -> + BouncerOverlayLayout.STANDARD_BOUNCER -> StandardLayout(viewModel = viewModel) + BouncerOverlayLayout.BESIDE_USER_SWITCHER -> BesideUserSwitcherLayout(viewModel = viewModel) - BouncerSceneLayout.BELOW_USER_SWITCHER -> BelowUserSwitcherLayout(viewModel = viewModel) - BouncerSceneLayout.SPLIT_BOUNCER -> SplitLayout(viewModel = viewModel) + BouncerOverlayLayout.BELOW_USER_SWITCHER -> + BelowUserSwitcherLayout(viewModel = viewModel) + BouncerOverlayLayout.SPLIT_BOUNCER -> SplitLayout(viewModel = viewModel) } Dialog(bouncerViewModel = viewModel, dialogFactory = dialogFactory) @@ -163,7 +164,10 @@ fun BouncerContent( * authentication attempt, including all messaging UI (directives, reasoning, errors, etc.). */ @Composable -private fun StandardLayout(viewModel: BouncerSceneContentViewModel, modifier: Modifier = Modifier) { +private fun StandardLayout( + viewModel: BouncerOverlayContentViewModel, + modifier: Modifier = Modifier, +) { val isHeightExpanded = LocalWindowSizeClass.current.heightSizeClass == WindowHeightSizeClass.Expanded @@ -208,7 +212,7 @@ private fun StandardLayout(viewModel: BouncerSceneContentViewModel, modifier: Mo * by double-tapping on the side. */ @Composable -private fun SplitLayout(viewModel: BouncerSceneContentViewModel, modifier: Modifier = Modifier) { +private fun SplitLayout(viewModel: BouncerOverlayContentViewModel, modifier: Modifier = Modifier) { val authMethod by viewModel.authMethodViewModel.collectAsStateWithLifecycle() Row( @@ -297,7 +301,7 @@ private fun SplitLayout(viewModel: BouncerSceneContentViewModel, modifier: Modif */ @Composable private fun BesideUserSwitcherLayout( - viewModel: BouncerSceneContentViewModel, + viewModel: BouncerOverlayContentViewModel, modifier: Modifier = Modifier, ) { val isLeftToRight = LocalLayoutDirection.current == LayoutDirection.Ltr @@ -452,7 +456,7 @@ private fun BesideUserSwitcherLayout( /** Arranges the bouncer contents and user switcher contents one on top of the other, vertically. */ @Composable private fun BelowUserSwitcherLayout( - viewModel: BouncerSceneContentViewModel, + viewModel: BouncerOverlayContentViewModel, modifier: Modifier = Modifier, ) { Column(modifier = modifier.padding(vertical = 128.dp)) { @@ -483,7 +487,7 @@ private fun BelowUserSwitcherLayout( @Composable private fun FoldAware( - viewModel: BouncerSceneContentViewModel, + viewModel: BouncerOverlayContentViewModel, aboveFold: @Composable BoxScope.() -> Unit, belowFold: @Composable BoxScope.() -> Unit, modifier: Modifier = Modifier, @@ -606,7 +610,7 @@ private fun StatusMessage(viewModel: BouncerMessageViewModel, modifier: Modifier * For example, this can be the PIN shapes or password text field. */ @Composable -private fun OutputArea(viewModel: BouncerSceneContentViewModel, modifier: Modifier = Modifier) { +private fun OutputArea(viewModel: BouncerOverlayContentViewModel, modifier: Modifier = Modifier) { val authMethodViewModel: AuthMethodBouncerViewModel? by viewModel.authMethodViewModel.collectAsStateWithLifecycle() when (val nonNullViewModel = authMethodViewModel) { @@ -631,7 +635,7 @@ private fun OutputArea(viewModel: BouncerSceneContentViewModel, modifier: Modifi */ @Composable private fun InputArea( - viewModel: BouncerSceneContentViewModel, + viewModel: BouncerOverlayContentViewModel, pinButtonRowVerticalSpacing: Dp, centerPatternDotsVertically: Boolean, modifier: Modifier = Modifier, @@ -659,7 +663,7 @@ private fun InputArea( } @Composable -private fun ActionArea(viewModel: BouncerSceneContentViewModel, modifier: Modifier = Modifier) { +private fun ActionArea(viewModel: BouncerOverlayContentViewModel, modifier: Modifier = Modifier) { val actionButton: BouncerActionButtonModel? by viewModel.actionButton.collectAsStateWithLifecycle() val appearFadeInAnimatable = remember { Animatable(0f) } @@ -723,7 +727,7 @@ private fun ActionArea(viewModel: BouncerSceneContentViewModel, modifier: Modifi @Composable private fun Dialog( - bouncerViewModel: BouncerSceneContentViewModel, + bouncerViewModel: BouncerOverlayContentViewModel, dialogFactory: BouncerDialogFactory, ) { val dialogViewModel by bouncerViewModel.dialogViewModel.collectAsStateWithLifecycle() @@ -751,7 +755,7 @@ private fun Dialog( /** Renders the UI of the user switcher that's displayed on large screens next to the bouncer UI. */ @Composable -private fun UserSwitcher(viewModel: BouncerSceneContentViewModel, modifier: Modifier = Modifier) { +private fun UserSwitcher(viewModel: BouncerOverlayContentViewModel, modifier: Modifier = Modifier) { val isUserSwitcherVisible by viewModel.isUserSwitcherVisible.collectAsStateWithLifecycle() if (!isUserSwitcherVisible) { // Take up the same space as the user switcher normally would, but with nothing inside it. @@ -829,7 +833,7 @@ private fun UserSwitcher(viewModel: BouncerSceneContentViewModel, modifier: Modi @Composable private fun UserSwitcherDropdownMenu( isExpanded: Boolean, - items: List<BouncerSceneContentViewModel.UserSwitcherDropdownItemViewModel>, + items: List<BouncerOverlayContentViewModel.UserSwitcherDropdownItemViewModel>, onDismissed: () -> Unit, ) { val context = LocalContext.current diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerOverlay.kt index fad8ae7e3ba2..48dee240a1df 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerOverlay.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 The Android Open Source Project + * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,19 +29,19 @@ import com.android.compose.animation.scene.ElementKey import com.android.compose.animation.scene.UserAction import com.android.compose.animation.scene.UserActionResult import com.android.systemui.bouncer.ui.BouncerDialogFactory -import com.android.systemui.bouncer.ui.viewmodel.BouncerSceneContentViewModel +import com.android.systemui.bouncer.ui.viewmodel.BouncerOverlayContentViewModel import com.android.systemui.bouncer.ui.viewmodel.BouncerUserActionsViewModel import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.lifecycle.ExclusiveActivatable import com.android.systemui.lifecycle.rememberViewModel -import com.android.systemui.scene.shared.model.Scenes -import com.android.systemui.scene.ui.composable.Scene +import com.android.systemui.scene.shared.model.Overlays +import com.android.systemui.scene.ui.composable.Overlay import javax.inject.Inject import kotlinx.coroutines.flow.Flow object Bouncer { object Elements { + val Root = ElementKey("BouncerRoot") val Background = ElementKey("BouncerBackground") val Content = ElementKey("BouncerContent") } @@ -51,16 +51,16 @@ object Bouncer { } } -/** The bouncer scene displays authentication challenges like PIN, password, or pattern. */ +/** The bouncer overlay displays authentication challenges like PIN, password, or pattern. */ @SysUISingleton -class BouncerScene +class BouncerOverlay @Inject constructor( private val actionsViewModelFactory: BouncerUserActionsViewModel.Factory, - private val contentViewModelFactory: BouncerSceneContentViewModel.Factory, + private val contentViewModelFactory: BouncerOverlayContentViewModel.Factory, private val dialogFactory: BouncerDialogFactory, -) : ExclusiveActivatable(), Scene { - override val key = Scenes.Bouncer +) : Overlay { + override val key = Overlays.Bouncer private val actionsViewModel: BouncerUserActionsViewModel by lazy { actionsViewModelFactory.create() @@ -68,22 +68,22 @@ constructor( override val userActions: Flow<Map<UserAction, UserActionResult>> = actionsViewModel.actions - override suspend fun onActivated(): Nothing { + override suspend fun activate(): Nothing { actionsViewModel.activate() } @Composable override fun ContentScope.Content(modifier: Modifier) = - BouncerScene( - viewModel = rememberViewModel("BouncerScene") { contentViewModelFactory.create() }, + BouncerOverlay( + viewModel = rememberViewModel("BouncerOverlay") { contentViewModelFactory.create() }, dialogFactory = dialogFactory, - modifier = modifier, + modifier = modifier.element(Bouncer.Elements.Root), ) } @Composable -private fun ContentScope.BouncerScene( - viewModel: BouncerSceneContentViewModel, +private fun ContentScope.BouncerOverlay( + viewModel: BouncerOverlayContentViewModel, dialogFactory: BouncerDialogFactory, modifier: Modifier = Modifier, ) { diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerSceneLayout.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerOverlayLayout.kt index 328fec591b41..8651859dd556 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerSceneLayout.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerOverlayLayout.kt @@ -23,12 +23,12 @@ import androidx.compose.runtime.Composable import com.android.compose.windowsizeclass.LocalWindowSizeClass /** - * Returns the [BouncerSceneLayout] that should be used by the bouncer scene. If - * [isOneHandedModeSupported] is `false`, then [BouncerSceneLayout.BESIDE_USER_SWITCHER] is replaced - * by [BouncerSceneLayout.STANDARD_BOUNCER]. + * Returns the [BouncerOverlayLayout] that should be used by the bouncer scene. If + * [isOneHandedModeSupported] is `false`, then [BouncerOverlayLayout.BESIDE_USER_SWITCHER] is + * replaced by [BouncerOverlayLayout.STANDARD_BOUNCER]. */ @Composable -fun calculateLayout(isOneHandedModeSupported: Boolean): BouncerSceneLayout { +fun calculateLayout(isOneHandedModeSupported: Boolean): BouncerOverlayLayout { val windowSizeClass = LocalWindowSizeClass.current return calculateLayoutInternal( @@ -57,7 +57,7 @@ private fun WindowHeightSizeClass.toEnum(): SizeClass { } /** Enumerates all known adaptive layout configurations. */ -enum class BouncerSceneLayout { +enum class BouncerOverlayLayout { /** The default UI with the bouncer laid out normally. */ STANDARD_BOUNCER, /** The bouncer is displayed vertically stacked with the user switcher. */ @@ -84,21 +84,21 @@ fun calculateLayoutInternal( width: SizeClass, height: SizeClass, isOneHandedModeSupported: Boolean, -): BouncerSceneLayout { +): BouncerOverlayLayout { return when (height) { - SizeClass.COMPACT -> BouncerSceneLayout.SPLIT_BOUNCER + SizeClass.COMPACT -> BouncerOverlayLayout.SPLIT_BOUNCER SizeClass.MEDIUM -> when (width) { - SizeClass.COMPACT -> BouncerSceneLayout.STANDARD_BOUNCER - SizeClass.MEDIUM -> BouncerSceneLayout.STANDARD_BOUNCER - SizeClass.EXPANDED -> BouncerSceneLayout.BESIDE_USER_SWITCHER + SizeClass.COMPACT -> BouncerOverlayLayout.STANDARD_BOUNCER + SizeClass.MEDIUM -> BouncerOverlayLayout.STANDARD_BOUNCER + SizeClass.EXPANDED -> BouncerOverlayLayout.BESIDE_USER_SWITCHER } SizeClass.EXPANDED -> when (width) { - SizeClass.COMPACT -> BouncerSceneLayout.STANDARD_BOUNCER - SizeClass.MEDIUM -> BouncerSceneLayout.BELOW_USER_SWITCHER - SizeClass.EXPANDED -> BouncerSceneLayout.BESIDE_USER_SWITCHER + SizeClass.COMPACT -> BouncerOverlayLayout.STANDARD_BOUNCER + SizeClass.MEDIUM -> BouncerOverlayLayout.BELOW_USER_SWITCHER + SizeClass.EXPANDED -> BouncerOverlayLayout.BESIDE_USER_SWITCHER } - }.takeIf { it != BouncerSceneLayout.BESIDE_USER_SWITCHER || isOneHandedModeSupported } - ?: BouncerSceneLayout.STANDARD_BOUNCER + }.takeIf { it != BouncerOverlayLayout.BESIDE_USER_SWITCHER || isOneHandedModeSupported } + ?: BouncerOverlayLayout.STANDARD_BOUNCER } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationLockscreenScrim.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationLockscreenScrim.kt index ef8911dae566..624c56099e25 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationLockscreenScrim.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationLockscreenScrim.kt @@ -31,6 +31,7 @@ import androidx.compose.ui.graphics.graphicsLayer import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.animation.scene.ContentScope import com.android.compose.animation.scene.content.state.TransitionState +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationLockscreenScrimViewModel @@ -100,7 +101,7 @@ fun ContentScope.NotificationLockscreenScrim( val isBouncerToLockscreen = layoutState.currentTransition?.isTransitioning( - from = Scenes.Bouncer, + from = Overlays.Bouncer, to = Scenes.Lockscreen, ) ?: false @@ -120,5 +121,5 @@ private fun shouldShowScrimFadeOut( return shadeMode != ShadeMode.Dual && currentTransition.isInitiatedByUserInput && (currentTransition.isTransitioning(from = Scenes.Shade, to = Scenes.Lockscreen) || - currentTransition.isTransitioning(from = Scenes.Bouncer, to = Scenes.Lockscreen)) + currentTransition.isTransitioning(from = Overlays.Bouncer, to = Scenes.Lockscreen)) } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt index b826187578e0..fe5c72f930fa 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt @@ -135,7 +135,8 @@ private fun ContentScope.stateForQuickSettingsContent( } } is TransitionState.Transition.OverlayTransition -> - error("Bad transition for QuickSettings scene: overlays not supported") + // Currently, no overlays use this QS impl, so we should make sure it's closed. + QSSceneAdapter.State.CLOSED } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerInterruptionHandler.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerInterruptionHandler.kt index 7e99847a7200..4ff7f4815e7c 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerInterruptionHandler.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerInterruptionHandler.kt @@ -20,9 +20,11 @@ import com.android.compose.animation.scene.InterruptionHandler import com.android.compose.animation.scene.InterruptionResult import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.content.state.TransitionState +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.ui.composable.transitions.TO_BOUNCER_FADE_FRACTION +// TODO(b/394632609) update this class to handle Bouncer as an Overlay object SceneContainerInterruptionHandler : InterruptionHandler { override fun onInterruption( interrupted: TransitionState.Transition.ChangeScene, @@ -39,7 +41,7 @@ object SceneContainerInterruptionHandler : InterruptionHandler { transition: TransitionState.Transition.ChangeScene, targetScene: SceneKey, ): InterruptionResult? { - if (targetScene != Scenes.Gone || !transition.isTransitioningFromOrTo(Scenes.Bouncer)) { + if (targetScene != Scenes.Gone || !transition.isTransitioningFromOrTo(Overlays.Bouncer)) { return null } @@ -48,7 +50,7 @@ object SceneContainerInterruptionHandler : InterruptionHandler { // usually the Lockscreen scene). val otherScene: SceneKey val animatesFromBouncer = - if (transition.isTransitioning(to = Scenes.Bouncer)) { + if (transition.isTransitioning(to = Overlays.Bouncer)) { otherScene = transition.fromScene transition.progress >= TO_BOUNCER_FADE_FRACTION } else { @@ -58,7 +60,7 @@ object SceneContainerInterruptionHandler : InterruptionHandler { return if (animatesFromBouncer) { InterruptionResult( - animateFrom = Scenes.Bouncer, + animateFrom = Scenes.Lockscreen, // We don't want the content of the lockscreen to be shown during the Bouncer => // Launcher transition. We disable chaining of the transitions so that only the diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt index 2ad9b7a10ad1..7fe19fe70a25 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt @@ -1,5 +1,6 @@ package com.android.systemui.scene.ui.composable +import com.android.compose.animation.scene.DefaultInterruptionHandler import com.android.compose.animation.scene.SceneTransitions import com.android.compose.animation.scene.TransitionKey import com.android.compose.animation.scene.reveal.ContainerRevealHaptics @@ -29,6 +30,7 @@ import com.android.systemui.scene.ui.composable.transitions.lockscreenToQuickSet import com.android.systemui.scene.ui.composable.transitions.lockscreenToShadeTransition import com.android.systemui.scene.ui.composable.transitions.lockscreenToSplitShadeTransition import com.android.systemui.scene.ui.composable.transitions.shadeToQuickSettingsTransition +import com.android.systemui.scene.ui.composable.transitions.toBouncerTransition import com.android.systemui.scene.ui.composable.transitions.toNotificationsShadeTransition import com.android.systemui.scene.ui.composable.transitions.toQuickSettingsShadeTransition import com.android.systemui.shade.ui.composable.Shade @@ -48,12 +50,10 @@ import com.android.systemui.shade.ui.composable.Shade class SceneContainerTransitions : SceneContainerTransitionsBuilder { override fun build(revealHaptics: ContainerRevealHaptics): SceneTransitions { return transitions { - interruptionHandler = SceneContainerInterruptionHandler + interruptionHandler = DefaultInterruptionHandler // Scene transitions - from(Scenes.Bouncer, to = Scenes.Gone) { bouncerToGoneTransition() } - from(Scenes.Dream, to = Scenes.Bouncer) { dreamToBouncerTransition() } from(Scenes.Dream, to = Scenes.Communal) { dreamToCommunalTransition() } from(Scenes.Dream, to = Scenes.Gone) { dreamToGoneTransition() } from( @@ -102,15 +102,6 @@ class SceneContainerTransitions : SceneContainerTransitionsBuilder { goneToQuickSettingsTransition(durationScale = 0.9) } - from(Scenes.Lockscreen, to = Scenes.Bouncer) { lockscreenToBouncerTransition() } - from( - Scenes.Lockscreen, - to = Scenes.Bouncer, - key = TransitionKey.PredictiveBack, - reversePreview = { bouncerToLockscreenPreview() }, - ) { - lockscreenToBouncerTransition() - } from(Scenes.Lockscreen, to = Scenes.Communal) { lockscreenToCommunalTransition() } from(Scenes.Lockscreen, to = Scenes.Dream) { lockscreenToDreamTransition() } from( @@ -190,10 +181,22 @@ class SceneContainerTransitions : SceneContainerTransitionsBuilder { ) { communalToShadeTransition() } - from(Scenes.Communal, to = Scenes.Bouncer) { communalToBouncerTransition() } // Overlay transitions + to(Overlays.Bouncer) { toBouncerTransition() } + from(Overlays.Bouncer, to = Scenes.Gone) { bouncerToGoneTransition() } + from(Scenes.Dream, to = Overlays.Bouncer) { dreamToBouncerTransition() } + from(Scenes.Lockscreen, to = Overlays.Bouncer) { lockscreenToBouncerTransition() } + from( + Scenes.Lockscreen, + to = Overlays.Bouncer, + key = TransitionKey.PredictiveBack, + reversePreview = { bouncerToLockscreenPreview() }, + ) { + lockscreenToBouncerTransition() + } + from(Scenes.Communal, to = Overlays.Bouncer) { communalToBouncerTransition() } to( Overlays.NotificationsShade, cuj = Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, // NOTYPO diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromBouncerToGoneTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromBouncerToGoneTransition.kt index 5eefe490ab5b..27cdf21f0697 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromBouncerToGoneTransition.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromBouncerToGoneTransition.kt @@ -2,10 +2,10 @@ package com.android.systemui.scene.ui.composable.transitions import androidx.compose.animation.core.tween import com.android.compose.animation.scene.TransitionBuilder -import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.bouncer.ui.composable.Bouncer fun TransitionBuilder.bouncerToGoneTransition() { spec = tween(durationMillis = 500) - fade(Scenes.Bouncer.rootElementKey) + fade(Bouncer.Elements.Root) } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt index 5bf77ae9b23c..f7f0bf19dda6 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt @@ -220,13 +220,70 @@ sealed interface ObservableTransitionState { isInPreviewStage, ) } + + fun showOverlay( + overlay: OverlayKey, + fromScene: SceneKey, + currentOverlays: Flow<Set<OverlayKey>>, + progress: Flow<Float>, + isInitiatedByUserInput: Boolean, + isUserInputOngoing: Flow<Boolean>, + previewProgress: Flow<Float> = flowOf(0f), + isInPreviewStage: Flow<Boolean> = flowOf(false), + ): ShowOrHideOverlay { + return ShowOrHideOverlay( + overlay = overlay, + fromContent = fromScene, + toContent = overlay, + currentScene = fromScene, + currentOverlays = currentOverlays, + progress = progress, + isInitiatedByUserInput = isInitiatedByUserInput, + isUserInputOngoing = isUserInputOngoing, + previewProgress = previewProgress, + isInPreviewStage = isInPreviewStage, + ) + } + + fun hideOverlay( + overlay: OverlayKey, + toScene: SceneKey, + currentOverlays: Flow<Set<OverlayKey>>, + progress: Flow<Float>, + isInitiatedByUserInput: Boolean, + isUserInputOngoing: Flow<Boolean>, + previewProgress: Flow<Float> = flowOf(0f), + isInPreviewStage: Flow<Boolean> = flowOf(false), + ): ShowOrHideOverlay { + return ShowOrHideOverlay( + overlay = overlay, + fromContent = overlay, + toContent = toScene, + currentScene = toScene, + currentOverlays = currentOverlays, + progress = progress, + isInitiatedByUserInput = isInitiatedByUserInput, + isUserInputOngoing = isUserInputOngoing, + previewProgress = previewProgress, + isInPreviewStage = isInPreviewStage, + ) + } } } - fun isIdle(scene: SceneKey? = null): Boolean { - return this is Idle && (scene == null || this.currentScene == scene) + fun isIdle(scene: SceneKey? = null, overlay: OverlayKey? = null): Boolean { + return this is Idle && + (scene == null || this.currentScene == scene) && + (overlay == null || overlay in this.currentOverlays) } + fun isIdle(content: ContentKey?): Boolean = + when (content) { + is SceneKey -> isIdle(scene = content) + is OverlayKey -> isIdle(overlay = content) + null -> this is Idle + } + fun isTransitioning(from: ContentKey? = null, to: ContentKey? = null): Boolean { return this is Transition && (from == null || this.fromContent == from) && diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt index 72bb82bd41bb..f423bab5381f 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt @@ -447,7 +447,7 @@ sealed class UserAction { } infix fun to(overlay: OverlayKey): Pair<UserAction, UserActionResult> { - return this to UserActionResult(toOverlay = overlay) + return this to UserActionResult.ShowOverlay(overlay) } /** Resolve this into a [Resolved] user action given [layoutDirection]. */ @@ -677,22 +677,6 @@ sealed class UserActionResult( */ requiresFullDistanceSwipe: Boolean = false, ): UserActionResult = ChangeScene(toScene, transitionKey, requiresFullDistanceSwipe) - - /** A [UserActionResult] that shows [toOverlay]. */ - operator fun invoke( - /** The overlay we should be transitioning to during the [UserAction]. */ - toOverlay: OverlayKey, - - /** The key of the transition that should be used. */ - transitionKey: TransitionKey? = null, - - /** - * If `true`, the swipe will be committed if only if the user swiped at least the swipe - * distance, i.e. the transition progress was already equal to or bigger than 100% when - * the user released their finger. - */ - requiresFullDistanceSwipe: Boolean = false, - ): UserActionResult = ShowOverlay(toOverlay, transitionKey, requiresFullDistanceSwipe) } } diff --git a/packages/SystemUI/lint.xml b/packages/SystemUI/lint.xml index 59a71099bb3f..bef2edd436e2 100644 --- a/packages/SystemUI/lint.xml +++ b/packages/SystemUI/lint.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<lint> +<!-- TODO(397519800): Remove hard coded once ASfP is capable of configuring this automatically --> +<lint lintJars="../../../../out/soong/.intermediates/frameworks/base/packages/SystemUI/checks/SystemUILintChecker/linux_glibc_common/combined/SystemUILintChecker.jar"> <issue id="PrivateResource" severity="ignore" /> -</lint>
\ No newline at end of file +</lint> diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt index 24b9e847d621..189d554415d0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt @@ -43,6 +43,7 @@ import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants import com.android.systemui.classifier.FalsingA11yDelegate import com.android.systemui.classifier.FalsingCollector +import com.android.systemui.coroutines.collectLastValue import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor @@ -65,6 +66,7 @@ import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.scene.shared.model.FakeSceneDataSource +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.fakeSceneDataSource import com.android.systemui.statusbar.policy.ConfigurationController @@ -797,6 +799,9 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { @EnableSceneContainer fun dismissesKeyguard_whenSceneChangesToGone() = kosmos.testScope.runTest { + // Collect sceneInteractor.currentOverlays so that show/hideOverlay receive updated + // overlay state during validation + val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) // Upon init, we have never dismisses the keyguard. underTest.onInit() runCurrent() @@ -807,19 +812,24 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { // is not enough to trigger a dismissal of the keyguard. underTest.onViewAttached() fakeSceneDataSource.pause() - sceneInteractor.changeScene(Scenes.Bouncer, "reason") + sceneInteractor.changeScene(Scenes.Lockscreen, "reason") + sceneInteractor.showOverlay(Overlays.Bouncer, "reason") sceneTransitionStateFlow.value = - ObservableTransitionState.Transition( - Scenes.Lockscreen, - Scenes.Bouncer, - flowOf(Scenes.Bouncer), - flowOf(.5f), - false, + ObservableTransitionState.Transition.showOverlay( + overlay = Overlays.Bouncer, + fromScene = Scenes.Lockscreen, + currentOverlays = flowOf(setOf(Overlays.Bouncer)), + progress = flowOf(.5f), + isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), ) runCurrent() - fakeSceneDataSource.unpause(expectedScene = Scenes.Bouncer) - sceneTransitionStateFlow.value = ObservableTransitionState.Idle(Scenes.Bouncer) + fakeSceneDataSource.unpause(expectedOverlays = setOf(Overlays.Bouncer)) + sceneTransitionStateFlow.value = + ObservableTransitionState.Idle( + currentScene = Scenes.Lockscreen, + currentOverlays = setOf(Overlays.Bouncer), + ) runCurrent() verify(primaryBouncerInteractor, never()) .notifyKeyguardAuthenticatedPrimaryAuth(anyInt()) @@ -832,17 +842,18 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { runCurrent() fakeSceneDataSource.pause() sceneInteractor.changeScene(Scenes.Gone, "reason") + sceneInteractor.hideOverlay(Overlays.Bouncer, "reason") sceneTransitionStateFlow.value = - ObservableTransitionState.Transition( - Scenes.Bouncer, - Scenes.Gone, - flowOf(Scenes.Gone), - flowOf(.5f), - false, + ObservableTransitionState.Transition.hideOverlay( + overlay = Overlays.Bouncer, + toScene = Scenes.Gone, + currentOverlays = flowOf(emptySet()), + progress = flowOf(.5f), + isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), ) runCurrent() - fakeSceneDataSource.unpause(expectedScene = Scenes.Gone) + fakeSceneDataSource.unpause(expectedScene = Scenes.Gone, expectedOverlays = emptySet()) sceneTransitionStateFlow.value = ObservableTransitionState.Idle(Scenes.Gone) runCurrent() verify(primaryBouncerInteractor).notifyKeyguardAuthenticatedPrimaryAuth(anyInt()) @@ -850,20 +861,30 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { // While listening, moving back to the bouncer scene does not dismiss the keyguard // again. clearInvocations(primaryBouncerInteractor) + + // switch to a different non-keyguard scene since showing overlay over Gone is + // prohibited + sceneInteractor.snapToScene(Scenes.Shade, "reason") + runCurrent() + fakeSceneDataSource.pause() - sceneInteractor.changeScene(Scenes.Bouncer, "reason") + sceneInteractor.showOverlay(Overlays.Bouncer, "reason") sceneTransitionStateFlow.value = - ObservableTransitionState.Transition( - Scenes.Gone, - Scenes.Bouncer, - flowOf(Scenes.Bouncer), - flowOf(.5f), - false, + ObservableTransitionState.Transition.showOverlay( + overlay = Overlays.Bouncer, + fromScene = Scenes.Shade, + currentOverlays = flowOf(setOf(Overlays.Bouncer)), + progress = flowOf(.5f), + isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), ) runCurrent() - fakeSceneDataSource.unpause(expectedScene = Scenes.Bouncer) - sceneTransitionStateFlow.value = ObservableTransitionState.Idle(Scenes.Bouncer) + fakeSceneDataSource.unpause(expectedOverlays = setOf(Overlays.Bouncer)) + sceneTransitionStateFlow.value = + ObservableTransitionState.Idle( + currentScene = Scenes.Lockscreen, + currentOverlays = setOf(Overlays.Bouncer), + ) runCurrent() verify(primaryBouncerInteractor, never()) .notifyKeyguardAuthenticatedPrimaryAuth(anyInt()) @@ -874,17 +895,18 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { underTest.onViewDetached() fakeSceneDataSource.pause() sceneInteractor.changeScene(Scenes.Gone, "reason") + sceneInteractor.hideOverlay(Overlays.Bouncer, "reason") sceneTransitionStateFlow.value = - ObservableTransitionState.Transition( - Scenes.Bouncer, - Scenes.Gone, - flowOf(Scenes.Gone), - flowOf(.5f), - false, + ObservableTransitionState.Transition.hideOverlay( + overlay = Overlays.Bouncer, + toScene = Scenes.Gone, + currentOverlays = flowOf(emptySet()), + progress = flowOf(.5f), + isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), ) runCurrent() - fakeSceneDataSource.unpause(expectedScene = Scenes.Gone) + fakeSceneDataSource.unpause(expectedScene = Scenes.Gone, expectedOverlays = emptySet()) sceneTransitionStateFlow.value = ObservableTransitionState.Idle(Scenes.Gone) runCurrent() verify(primaryBouncerInteractor, never()) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt index f376e932e70b..40295a242b2a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt @@ -33,6 +33,7 @@ import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.kosmos.testScope import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository import com.android.systemui.statusbar.pipeline.mobile.data.repository.fake @@ -94,7 +95,8 @@ class BouncerActionButtonInteractorTest : SysuiTestCase() { kosmos.telecomManager = telecomManager - kosmos.sceneInteractor.changeScene(Scenes.Bouncer, "") + kosmos.sceneInteractor.changeScene(Scenes.Lockscreen, "") + kosmos.sceneInteractor.showOverlay(Overlays.Bouncer, "") } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/composable/BouncerSceneLayoutTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/composable/BouncerOverlayLayoutTest.kt index b4b41787d833..2d0dd49242af 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/composable/BouncerSceneLayoutTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/composable/BouncerOverlayLayoutTest.kt @@ -18,10 +18,10 @@ package com.android.systemui.bouncer.ui.composable import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.bouncer.ui.composable.BouncerSceneLayout.BELOW_USER_SWITCHER -import com.android.systemui.bouncer.ui.composable.BouncerSceneLayout.BESIDE_USER_SWITCHER -import com.android.systemui.bouncer.ui.composable.BouncerSceneLayout.SPLIT_BOUNCER -import com.android.systemui.bouncer.ui.composable.BouncerSceneLayout.STANDARD_BOUNCER +import com.android.systemui.bouncer.ui.composable.BouncerOverlayLayout.BELOW_USER_SWITCHER +import com.android.systemui.bouncer.ui.composable.BouncerOverlayLayout.BESIDE_USER_SWITCHER +import com.android.systemui.bouncer.ui.composable.BouncerOverlayLayout.SPLIT_BOUNCER +import com.android.systemui.bouncer.ui.composable.BouncerOverlayLayout.STANDARD_BOUNCER import com.google.common.truth.Truth.assertThat import java.util.Locale import org.junit.Test @@ -32,7 +32,7 @@ import platform.test.runner.parameterized.Parameters @SmallTest @RunWith(ParameterizedAndroidJunit4::class) -class BouncerSceneLayoutTest : SysuiTestCase() { +class BouncerOverlayLayoutTest : SysuiTestCase() { data object Phone : Device( @@ -186,7 +186,7 @@ class BouncerSceneLayoutTest : SysuiTestCase() { data class TestCase( val device: Device, val held: Held, - val expected: BouncerSceneLayout, + val expected: BouncerOverlayLayout, val isOneHandedModeSupported: Boolean = true, ) { override fun toString(): String { @@ -203,10 +203,10 @@ class BouncerSceneLayoutTest : SysuiTestCase() { } data class Expected( - val whenNaturallyHeld: BouncerSceneLayout, - val whenUnnaturallyHeld: BouncerSceneLayout, + val whenNaturallyHeld: BouncerOverlayLayout, + val whenUnnaturallyHeld: BouncerOverlayLayout, ) { - fun layout(heldNaturally: Boolean): BouncerSceneLayout { + fun layout(heldNaturally: Boolean): BouncerOverlayLayout { return if (heldNaturally) { whenNaturallyHeld } else { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/composable/BouncerPredictiveBackTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/composable/BouncerPredictiveBackTest.kt index dda460a6198f..0fe649c867f2 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/composable/BouncerPredictiveBackTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/composable/BouncerPredictiveBackTest.kt @@ -43,11 +43,10 @@ import com.android.compose.animation.scene.UserActionResult import com.android.compose.animation.scene.isElement import com.android.compose.theme.PlatformTheme import com.android.systemui.SysuiTestCase -import com.android.systemui.bouncer.domain.interactor.bouncerInteractor import com.android.systemui.bouncer.ui.BouncerDialogFactory -import com.android.systemui.bouncer.ui.viewmodel.BouncerSceneContentViewModel +import com.android.systemui.bouncer.ui.viewmodel.BouncerOverlayContentViewModel import com.android.systemui.bouncer.ui.viewmodel.BouncerUserActionsViewModel -import com.android.systemui.bouncer.ui.viewmodel.bouncerSceneContentViewModel +import com.android.systemui.bouncer.ui.viewmodel.bouncerOverlayContentViewModel import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -59,6 +58,7 @@ import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.domain.startable.sceneContainerStartable import com.android.systemui.scene.sceneContainerViewModelFactory import com.android.systemui.scene.sceneTransitionsBuilder +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.SceneContainerConfig import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.sceneDataSourceDelegator @@ -103,10 +103,10 @@ class BouncerPredictiveBackTest : SysuiTestCase() { motionTestRule.toolkit.composeContentTestRule as AndroidComposeTestRule<*, *> private val sceneInteractor by lazy { kosmos.sceneInteractor } - private val Kosmos.sceneKeys by Fixture { listOf(Scenes.Lockscreen, Scenes.Bouncer) } - private val Kosmos.initialSceneKey by Fixture { Scenes.Bouncer } + private val Kosmos.sceneKeys by Fixture { listOf(Scenes.Lockscreen) } + private val Kosmos.initialSceneKey by Fixture { Scenes.Lockscreen } private val Kosmos.sceneContainerConfig by Fixture { - val navigationDistances = mapOf(Scenes.Lockscreen to 1, Scenes.Bouncer to 0) + val navigationDistances = mapOf(Scenes.Lockscreen to 1) SceneContainerConfig( sceneKeys, initialSceneKey, @@ -137,17 +137,17 @@ class BouncerPredictiveBackTest : SysuiTestCase() { } private val bouncerSceneActionsViewModelFactory = object : BouncerUserActionsViewModel.Factory { - override fun create() = BouncerUserActionsViewModel(kosmos.bouncerInteractor) + override fun create() = BouncerUserActionsViewModel() } - private lateinit var bouncerSceneContentViewModel: BouncerSceneContentViewModel - private val bouncerSceneContentViewModelFactory = - object : BouncerSceneContentViewModel.Factory { - override fun create() = bouncerSceneContentViewModel + private lateinit var mBouncerOverlayContentViewModel: BouncerOverlayContentViewModel + private val mBouncerOverlayContentViewModelFactory = + object : BouncerOverlayContentViewModel.Factory { + override fun create() = mBouncerOverlayContentViewModel } private val bouncerScene = - BouncerScene( + BouncerOverlay( bouncerSceneActionsViewModelFactory, - bouncerSceneContentViewModelFactory, + mBouncerOverlayContentViewModelFactory, bouncerDialogFactory, ) @@ -155,7 +155,7 @@ class BouncerPredictiveBackTest : SysuiTestCase() { fun setUp() { MockitoAnnotations.initMocks(this) - bouncerSceneContentViewModel = kosmos.bouncerSceneContentViewModel + mBouncerOverlayContentViewModel = kosmos.bouncerOverlayContentViewModel val startable = kosmos.sceneContainerStartable startable.start() @@ -175,14 +175,10 @@ class BouncerPredictiveBackTest : SysuiTestCase() { rememberViewModel("BouncerPredictiveBackTest") { sceneContainerViewModel }, - sceneByKey = - mapOf( - Scenes.Lockscreen to FakeLockscreen(), - Scenes.Bouncer to bouncerScene, - ), - initialSceneKey = Scenes.Bouncer, + sceneByKey = mapOf(Scenes.Lockscreen to FakeLockscreen()), + initialSceneKey = Scenes.Lockscreen, transitionsBuilder = kosmos.sceneTransitionsBuilder, - overlayByKey = emptyMap(), + overlayByKey = mapOf(Overlays.Bouncer to bouncerScene), dataSourceDelegator = kosmos.sceneDataSourceDelegator, qsSceneAdapter = { kosmos.fakeQsSceneAdapter }, sceneJankMonitorFactory = kosmos.sceneJankMonitorFactory, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerOverlayContentViewModelTest.kt index eef8d9f40458..9cc09bd406e7 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerOverlayContentViewModelTest.kt @@ -61,17 +61,17 @@ import org.junit.runner.RunWith @SmallTest @RunWith(AndroidJUnit4::class) @EnableSceneContainer -class BouncerSceneContentViewModelTest : SysuiTestCase() { +class BouncerOverlayContentViewModelTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope - private lateinit var underTest: BouncerSceneContentViewModel + private lateinit var underTest: BouncerOverlayContentViewModel @Before fun setUp() { kosmos.sceneContainerStartable.start() - underTest = kosmos.bouncerSceneContentViewModel + underTest = kosmos.bouncerOverlayContentViewModel underTest.activateIn(testScope) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModelTest.kt index e12fabfd199d..fe2f51fd52cd 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModelTest.kt @@ -27,6 +27,7 @@ import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.kosmos.testScope import com.android.systemui.lifecycle.activateIn import com.android.systemui.scene.domain.startable.sceneContainerStartable +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.fakeSceneDataSource import com.android.systemui.testKosmos @@ -62,13 +63,13 @@ class BouncerUserActionsViewModelTest : SysuiTestCase() { kosmos.fakeSceneDataSource.changeScene(Scenes.QuickSettings) runCurrent() - kosmos.fakeSceneDataSource.changeScene(Scenes.Bouncer) + kosmos.fakeSceneDataSource.showOverlay(Overlays.Bouncer) runCurrent() assertThat(actions) .containsEntriesExactly( - Back to UserActionResult(Scenes.QuickSettings), - Swipe.Down to UserActionResult(Scenes.QuickSettings), + Back to UserActionResult.HideOverlay(Overlays.Bouncer), + Swipe.Down to UserActionResult.HideOverlay(Overlays.Bouncer), ) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt index b2d245858196..7cfbf5efce68 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt @@ -22,7 +22,6 @@ import android.view.KeyEvent import androidx.compose.ui.input.key.KeyEventType import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.compose.animation.scene.SceneKey import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository import com.android.systemui.authentication.domain.interactor.authenticationInteractor @@ -38,7 +37,7 @@ import com.android.systemui.kosmos.testScope import com.android.systemui.lifecycle.activateIn import com.android.systemui.res.R import com.android.systemui.scene.domain.interactor.sceneInteractor -import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.testKosmos import com.android.systemui.user.data.model.SelectedUserModel import com.android.systemui.user.data.model.SelectionStatus @@ -86,12 +85,12 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { @Test fun onShown() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene) + val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) val password by collectLastValue(underTest.password) lockDeviceAndOpenPasswordBouncer() assertThat(password).isEmpty() - assertThat(currentScene).isEqualTo(Scenes.Bouncer) + assertThat(currentOverlays).contains(Overlays.Bouncer) assertThat(underTest.authenticationMethod).isEqualTo(AuthenticationMethodModel.Password) } @@ -111,14 +110,14 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { @Test fun onPasswordInputChanged() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene) + val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) val password by collectLastValue(underTest.password) lockDeviceAndOpenPasswordBouncer() underTest.onPasswordInputChanged("password") assertThat(password).isEqualTo("password") - assertThat(currentScene).isEqualTo(Scenes.Bouncer) + assertThat(currentOverlays).contains(Overlays.Bouncer) } @Test @@ -152,7 +151,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { kosmos.fakeAuthenticationRepository.setAuthenticationMethod( AuthenticationMethodModel.Password ) - switchToScene(Scenes.Bouncer) + showBouncer() // No input entered. @@ -185,7 +184,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { @Test fun onShown_againAfterSceneChange_resetsPassword() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene) + val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) val password by collectLastValue(underTest.password) lockDeviceAndOpenPasswordBouncer() @@ -194,14 +193,14 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { assertThat(password).isEqualTo("password") // The user doesn't confirm the password, but navigates back to the lockscreen instead. - switchToScene(Scenes.Lockscreen) + hideBouncer() // The user navigates to the bouncer again. - switchToScene(Scenes.Bouncer) + showBouncer() // Ensure the previously-entered password is not shown. assertThat(password).isEmpty() - assertThat(currentScene).isEqualTo(Scenes.Bouncer) + assertThat(currentOverlays).contains(Overlays.Bouncer) } @Test @@ -379,21 +378,28 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { assertThat(underTest.onKeyEvent(KeyEventType.KeyDown, KeyEvent.KEYCODE_SPACE)).isFalse() } - private fun TestScope.switchToScene(toScene: SceneKey) { - val currentScene by collectLastValue(sceneInteractor.currentScene) - val bouncerHidden = currentScene == Scenes.Bouncer && toScene != Scenes.Bouncer - sceneInteractor.changeScene(toScene, "reason") - if (bouncerHidden) underTest.onHidden() + private fun TestScope.showBouncer() { + val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) + sceneInteractor.showOverlay(Overlays.Bouncer, "reason") runCurrent() - assertThat(currentScene).isEqualTo(toScene) + assertThat(currentOverlays).contains(Overlays.Bouncer) + } + + private fun TestScope.hideBouncer() { + val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) + sceneInteractor.hideOverlay(Overlays.Bouncer, "reason") + underTest.onHidden() + runCurrent() + + assertThat(currentOverlays).doesNotContain(Overlays.Bouncer) } private fun TestScope.lockDeviceAndOpenPasswordBouncer() { kosmos.fakeAuthenticationRepository.setAuthenticationMethod( AuthenticationMethodModel.Password ) - switchToScene(Scenes.Bouncer) + showBouncer() } private suspend fun TestScope.setLockout(isLockedOut: Boolean, failedAttemptCount: Int = 5) { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt index ec7d1c3bea3e..4075d1158154 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt @@ -19,7 +19,6 @@ package com.android.systemui.bouncer.ui.viewmodel import android.platform.test.annotations.EnableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.compose.animation.scene.SceneKey import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository @@ -36,7 +35,7 @@ import com.android.systemui.kosmos.testScope import com.android.systemui.lifecycle.activateIn import com.android.systemui.res.R import com.android.systemui.scene.domain.interactor.sceneInteractor -import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.testKosmos import com.google.android.msdl.data.model.MSDLToken import com.google.common.truth.Truth.assertThat @@ -58,7 +57,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() { private val testScope = kosmos.testScope private val authenticationInteractor by lazy { kosmos.authenticationInteractor } private val sceneInteractor by lazy { kosmos.sceneInteractor } - private val bouncerViewModel by lazy { kosmos.bouncerSceneContentViewModel } + private val bouncerViewModel by lazy { kosmos.bouncerOverlayContentViewModel } private val msdlPlayer: FakeMSDLPlayer = kosmos.fakeMSDLPlayer private val bouncerHapticHelper = kosmos.bouncerHapticPlayer private val underTest = @@ -81,21 +80,21 @@ class PatternBouncerViewModelTest : SysuiTestCase() { @Test fun onShown() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene) + val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) val selectedDots by collectLastValue(underTest.selectedDots) val currentDot by collectLastValue(underTest.currentDot) lockDeviceAndOpenPatternBouncer() assertThat(selectedDots).isEmpty() assertThat(currentDot).isNull() - assertThat(currentScene).isEqualTo(Scenes.Bouncer) + assertThat(currentOverlays).contains(Overlays.Bouncer) assertThat(underTest.authenticationMethod).isEqualTo(AuthenticationMethodModel.Pattern) } @Test fun onDragStart() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene) + val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) val selectedDots by collectLastValue(underTest.selectedDots) val currentDot by collectLastValue(underTest.currentDot) lockDeviceAndOpenPatternBouncer() @@ -104,7 +103,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() { assertThat(selectedDots).isEmpty() assertThat(currentDot).isNull() - assertThat(currentScene).isEqualTo(Scenes.Bouncer) + assertThat(currentOverlays).contains(Overlays.Bouncer) } @Test @@ -143,7 +142,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() { @Test fun onDragEnd_whenWrong() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene) + val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) val selectedDots by collectLastValue(underTest.selectedDots) val currentDot by collectLastValue(underTest.currentDot) lockDeviceAndOpenPatternBouncer() @@ -154,7 +153,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() { assertThat(selectedDots).isEmpty() assertThat(currentDot).isNull() - assertThat(currentScene).isEqualTo(Scenes.Bouncer) + assertThat(currentOverlays).contains(Overlays.Bouncer) } @Test @@ -365,21 +364,16 @@ class PatternBouncerViewModelTest : SysuiTestCase() { ) } - private fun TestScope.switchToScene(toScene: SceneKey) { - val currentScene by collectLastValue(sceneInteractor.currentScene) - val bouncerHidden = currentScene == Scenes.Bouncer && toScene != Scenes.Bouncer - sceneInteractor.changeScene(toScene, "reason") - if (bouncerHidden) underTest.onHidden() - runCurrent() - - assertThat(currentScene).isEqualTo(toScene) - } - private fun TestScope.lockDeviceAndOpenPatternBouncer() { + val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) kosmos.fakeAuthenticationRepository.setAuthenticationMethod( AuthenticationMethodModel.Pattern ) - switchToScene(Scenes.Bouncer) + + sceneInteractor.showOverlay(Overlays.Bouncer, "reason") + runCurrent() + + assertThat(currentOverlays).contains(Overlays.Bouncer) } companion object { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt index 705e8341ff80..2518d2fdd2e5 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt @@ -26,7 +26,6 @@ import android.view.KeyEvent.KEYCODE_NUMPAD_0 import androidx.compose.ui.input.key.KeyEventType import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.compose.animation.scene.SceneKey import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository @@ -42,7 +41,7 @@ import com.android.systemui.kosmos.testScope import com.android.systemui.lifecycle.activateIn import com.android.systemui.res.R import com.android.systemui.scene.domain.interactor.sceneInteractor -import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.testKosmos import com.google.android.msdl.data.model.MSDLToken import com.google.common.truth.Truth.assertThat @@ -181,7 +180,7 @@ class PinBouncerViewModelTest : SysuiTestCase() { @Test fun onBackspaceButtonLongPressed() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene) + val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) val pin by collectLastValue(underTest.pinInput.map { it.getPin() }) lockDeviceAndOpenPinBouncer() @@ -194,7 +193,7 @@ class PinBouncerViewModelTest : SysuiTestCase() { underTest.onBackspaceButtonLongPressed() assertThat(pin).isEmpty() - assertThat(currentScene).isEqualTo(Scenes.Bouncer) + assertThat(currentOverlays).contains(Overlays.Bouncer) } @Test @@ -213,7 +212,7 @@ class PinBouncerViewModelTest : SysuiTestCase() { @Test fun onAuthenticateButtonClicked_whenWrong() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene) + val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) val pin by collectLastValue(underTest.pinInput.map { it.getPin() }) lockDeviceAndOpenPinBouncer() @@ -226,7 +225,7 @@ class PinBouncerViewModelTest : SysuiTestCase() { underTest.onAuthenticateButtonClicked() assertThat(pin).isEmpty() - assertThat(currentScene).isEqualTo(Scenes.Bouncer) + assertThat(currentOverlays).contains(Overlays.Bouncer) } @Test @@ -281,7 +280,7 @@ class PinBouncerViewModelTest : SysuiTestCase() { val autoConfirmEnabled by collectLastValue(authenticationInteractor.isAutoConfirmEnabled) - val currentScene by collectLastValue(sceneInteractor.currentScene) + val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) val pin by collectLastValue(underTest.pinInput.map { it.getPin() }) kosmos.fakeAuthenticationRepository.setAutoConfirmFeatureEnabled(true) @@ -296,7 +295,7 @@ class PinBouncerViewModelTest : SysuiTestCase() { ) // PIN is now wrong! assertThat(pin).isEmpty() - assertThat(currentScene).isEqualTo(Scenes.Bouncer) + assertThat(currentOverlays).contains(Overlays.Bouncer) } @Test @@ -310,10 +309,10 @@ class PinBouncerViewModelTest : SysuiTestCase() { assertThat(pin).isNotEmpty() // The user doesn't confirm the PIN, but navigates back to the lockscreen instead. - switchToScene(Scenes.Lockscreen) + hideBouncer() // The user navigates to the bouncer again. - switchToScene(Scenes.Bouncer) + showBouncer() // Ensure the previously-entered PIN is not shown. assertThat(pin).isEmpty() @@ -527,19 +526,26 @@ class PinBouncerViewModelTest : SysuiTestCase() { assertThat(msdlPlayer.latestPropertiesPlayed).isNull() } - private fun TestScope.switchToScene(toScene: SceneKey) { - val currentScene by collectLastValue(sceneInteractor.currentScene) - val bouncerHidden = currentScene == Scenes.Bouncer && toScene != Scenes.Bouncer - sceneInteractor.changeScene(toScene, "reason") - if (bouncerHidden) underTest.onHidden() + private fun TestScope.showBouncer() { + val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) + sceneInteractor.showOverlay(Overlays.Bouncer, "reason") runCurrent() - assertThat(currentScene).isEqualTo(toScene) + assertThat(currentOverlays).contains(Overlays.Bouncer) + } + + private fun TestScope.hideBouncer() { + val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) + sceneInteractor.hideOverlay(Overlays.Bouncer, "reason") + underTest.onHidden() + runCurrent() + + assertThat(currentOverlays).doesNotContain(Overlays.Bouncer) } private fun TestScope.lockDeviceAndOpenPinBouncer() { kosmos.fakeAuthenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) - switchToScene(Scenes.Bouncer) + showBouncer() } companion object { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalUserActionsViewModelTest.kt index 619995478ecc..59fda984b3d5 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalUserActionsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalUserActionsViewModelTest.kt @@ -74,7 +74,8 @@ class CommunalUserActionsViewModelTest : SysuiTestCase() { setUpState(isShadeTouchable = true, isDeviceUnlocked = false) assertThat(actions).isNotEmpty() assertThat(actions?.get(Swipe.End)).isEqualTo(UserActionResult(Scenes.Lockscreen)) - assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer)) + assertThat(actions?.get(Swipe.Up)) + .isEqualTo(UserActionResult.ShowOverlay(Overlays.Bouncer)) assertThat(actions?.get(Swipe.Down)).isEqualTo(UserActionResult(Scenes.Shade)) setUpState(isShadeTouchable = false, isDeviceUnlocked = false) @@ -96,7 +97,8 @@ class CommunalUserActionsViewModelTest : SysuiTestCase() { setUpState(isShadeTouchable = true, isDeviceUnlocked = false) assertThat(actions).isNotEmpty() assertThat(actions?.get(Swipe.End)).isEqualTo(UserActionResult(Scenes.Lockscreen)) - assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer)) + assertThat(actions?.get(Swipe.Up)) + .isEqualTo(UserActionResult.ShowOverlay(Overlays.Bouncer)) assertThat(actions?.get(Swipe.Down)) .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade)) @@ -120,7 +122,8 @@ class CommunalUserActionsViewModelTest : SysuiTestCase() { setUpState(isShadeTouchable = true, isDeviceUnlocked = false) assertThat(actions).isNotEmpty() assertThat(actions?.get(Swipe.End)).isEqualTo(UserActionResult(Scenes.Lockscreen)) - assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer)) + assertThat(actions?.get(Swipe.Up)) + .isEqualTo(UserActionResult.ShowOverlay(Overlays.Bouncer)) assertThat(actions?.get(Swipe.Down)) .isEqualTo(UserActionResult.ShowOverlay(Overlays.NotificationsShade)) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt index 799054a92bee..f47aa6b3dc03 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt @@ -85,6 +85,7 @@ import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.scene.data.repository.Idle import com.android.systemui.scene.data.repository.Transition import com.android.systemui.scene.data.repository.setTransition +import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.settings.fakeUserTracker @@ -602,6 +603,7 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() { flowOf(ObservableTransitionState.Idle(CommunalScenes.Communal)) ) // Transitioned to Glanceable hub. + kosmos.sceneInteractor.changeScene(Scenes.Communal, "") kosmos.setTransition( sceneTransition = Idle(Scenes.Communal), stateTransition = @@ -656,6 +658,7 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() { keyguardRepository.setKeyguardOccluded(true) // And on hub + kosmos.sceneInteractor.changeScene(Scenes.Communal, "") kosmos.setTransition( sceneTransition = Idle(Scenes.Communal), stateTransition = @@ -673,6 +676,7 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() { assertThat(isCommunalContentFlowFrozen).isEqualTo(true) // 3. When transitioned to OCCLUDED and activity shows + kosmos.sceneInteractor.changeScene(Scenes.Lockscreen, "") kosmos.setTransition( sceneTransition = Idle(Scenes.Lockscreen), stateTransition = @@ -759,6 +763,7 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() { keyguardRepository.setKeyguardOccluded(true) // And transitioned to hub + kosmos.sceneInteractor.changeScene(Scenes.Communal, "") kosmos.setTransition( sceneTransition = Idle(Scenes.Communal), stateTransition = diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt index f6016c6479ac..651d474d78b9 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt @@ -81,6 +81,7 @@ import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.se import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.testKosmos @@ -186,15 +187,13 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { underTest = createDeviceEntryFaceAuthRepositoryImpl(faceManager, bypassController) if (!SceneContainerFlag.isEnabled) { - mSetFlagsRule.disableFlags( - AConfigFlags.FLAG_KEYGUARD_WM_STATE_REFACTOR, - ) + mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_WM_STATE_REFACTOR) } } private fun createDeviceEntryFaceAuthRepositoryImpl( fmOverride: FaceManager? = faceManager, - bypassControllerOverride: KeyguardBypassController? = bypassController + bypassControllerOverride: KeyguardBypassController? = bypassController, ): DeviceEntryFaceAuthRepositoryImpl { val faceAuthBuffer = logcatTableLogBuffer(kosmos, "face auth") val faceDetectBuffer = logcatTableLogBuffer(kosmos, "face detect") @@ -256,7 +255,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { 0, null, keyguardSessionId, - faceAuthUiEvent.extraInfo + faceAuthUiEvent.extraInfo, ) } @@ -289,7 +288,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { authenticationCallback.value.onAuthenticationError( FACE_ERROR_LOCKOUT_PERMANENT, - "face locked out" + "face locked out", ) assertThat(lockedOut()).isTrue() @@ -317,7 +316,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { .thenReturn( listOf( createFaceSensorProperties(supportsFaceDetection = false), - createFaceSensorProperties(supportsFaceDetection = true) + createFaceSensorProperties(supportsFaceDetection = true), ) ) assertThat(createDeviceEntryFaceAuthRepositoryImpl().isDetectionSupported).isFalse() @@ -326,7 +325,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { .thenReturn( listOf( createFaceSensorProperties(supportsFaceDetection = true), - createFaceSensorProperties(supportsFaceDetection = false) + createFaceSensorProperties(supportsFaceDetection = false), ) ) assertThat(createDeviceEntryFaceAuthRepositoryImpl().isDetectionSupported).isTrue() @@ -409,7 +408,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { // Auth is done cancelling. authenticationCallback.value.onAuthenticationError( FACE_ERROR_CANCELED, - "First auth attempt cancellation completed" + "First auth attempt cancellation completed", ) val value = authStatus() as ErrorFaceAuthenticationStatus assertThat(value.msgId).isEqualTo(FACE_ERROR_CANCELED) @@ -472,7 +471,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { underTest = createDeviceEntryFaceAuthRepositoryImpl( fmOverride = null, - bypassControllerOverride = null + bypassControllerOverride = null, ) fakeUserRepository.setSelectedUserInfo(primaryUser) @@ -499,7 +498,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { testGatingCheckForFaceAuth { fakeUserRepository.setSelectedUserInfo( primaryUser, - SelectionStatus.SELECTION_IN_PROGRESS + SelectionStatus.SELECTION_IN_PROGRESS, ) } } @@ -510,7 +509,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { testGatingCheckForDetect { fakeUserRepository.setSelectedUserInfo( userInfo = primaryUser, - selectionStatus = SelectionStatus.SELECTION_IN_PROGRESS + selectionStatus = SelectionStatus.SELECTION_IN_PROGRESS, ) } } @@ -544,11 +543,11 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { testGatingCheckForFaceAuth(sceneContainerEnabled = true) { kosmos.sceneInteractor.setTransitionState( MutableStateFlow( - ObservableTransitionState.Transition( - fromScene = Scenes.Bouncer, + ObservableTransitionState.Transition.hideOverlay( + overlay = Overlays.Bouncer, toScene = Scenes.Gone, - currentScene = flowOf(Scenes.Bouncer), - progress = MutableStateFlow(0.2f), + currentOverlays = flowOf(setOf(Overlays.Bouncer)), + progress = flowOf(.2f), isInitiatedByUserInput = true, isUserInputOngoing = flowOf(false), ) @@ -634,9 +633,11 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { assertThat(canFaceAuthRun()).isFalse() // but bouncer is shown after that. - kosmos.sceneInteractor.changeScene(Scenes.Bouncer, "for-test") + kosmos.sceneInteractor.showOverlay(Overlays.Bouncer, "for-test") kosmos.sceneInteractor.setTransitionState( - MutableStateFlow(ObservableTransitionState.Idle(Scenes.Bouncer)) + MutableStateFlow( + ObservableTransitionState.Idle(Scenes.Lockscreen, setOf(Overlays.Bouncer)) + ) ) runCurrent() @@ -666,7 +667,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { assertThat(canFaceAuthRun()).isFalse() underTest.requestAuthenticate( FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, - fallbackToDetection = true + fallbackToDetection = true, ) faceAuthenticateIsNotCalled() @@ -687,7 +688,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { assertThat(canFaceAuthRun()).isFalse() underTest.requestAuthenticate( FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, - fallbackToDetection = true + fallbackToDetection = true, ) faceAuthenticateIsNotCalled() @@ -704,7 +705,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { keyguardTransitionRepository.sendTransitionSteps( from = KeyguardState.LOCKSCREEN, to = KeyguardState.OFF, - testScope + testScope, ) runCurrent() keyguardTransitionRepository.sendTransitionStep( @@ -730,7 +731,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { keyguardTransitionRepository.sendTransitionSteps( from = KeyguardState.OFF, to = KeyguardState.LOCKSCREEN, - testScope + testScope, ) runCurrent() @@ -834,7 +835,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { fakeUserRepository.setSelectedUserInfo( primaryUser, - SelectionStatus.SELECTION_IN_PROGRESS + SelectionStatus.SELECTION_IN_PROGRESS, ) assertThat(authenticated()).isFalse() @@ -896,11 +897,11 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { testGatingCheckForDetect(sceneContainerEnabled = true) { kosmos.sceneInteractor.setTransitionState( MutableStateFlow( - ObservableTransitionState.Transition( - fromScene = Scenes.Bouncer, + ObservableTransitionState.Transition.hideOverlay( + overlay = Overlays.Bouncer, toScene = Scenes.Gone, - currentScene = flowOf(Scenes.Bouncer), - progress = MutableStateFlow(0.2f), + currentOverlays = flowOf(setOf(Overlays.Bouncer)), + progress = flowOf(.2f), isInitiatedByUserInput = true, isUserInputOngoing = flowOf(false), ) @@ -1032,7 +1033,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { keyguardTransitionRepository.sendTransitionSteps( from = KeyguardState.DOZING, to = KeyguardState.GONE, - testScope + testScope, ) runCurrent() verify(faceManager).scheduleWatchdog() @@ -1044,7 +1045,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { keyguardTransitionRepository.sendTransitionSteps( from = KeyguardState.AOD, to = KeyguardState.GONE, - testScope + testScope, ) runCurrent() verify(faceManager).scheduleWatchdog() @@ -1056,7 +1057,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { keyguardTransitionRepository.sendTransitionSteps( from = KeyguardState.LOCKSCREEN, to = KeyguardState.GONE, - testScope + testScope, ) runCurrent() verify(faceManager).scheduleWatchdog() @@ -1068,7 +1069,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { keyguardTransitionRepository.sendTransitionSteps( from = KeyguardState.PRIMARY_BOUNCER, to = KeyguardState.GONE, - testScope + testScope, ) runCurrent() verify(faceManager).scheduleWatchdog() @@ -1085,7 +1086,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { authenticationCallback.value.onAuthenticationError( FACE_ERROR_HW_UNAVAILABLE, - "HW unavailable" + "HW unavailable", ) advanceTimeBy(DeviceEntryFaceAuthRepositoryImpl.HAL_ERROR_RETRY_TIMEOUT) @@ -1137,7 +1138,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { private suspend fun TestScope.testGatingCheckForFaceAuth( sceneContainerEnabled: Boolean = false, - gatingCheckModifier: suspend () -> Unit + gatingCheckModifier: suspend () -> Unit, ) { initCollectors() allPreconditionsToRunFaceAuthAreTrue(sceneContainerEnabled) @@ -1180,7 +1181,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { private suspend fun TestScope.testGatingCheckForDetect( sceneContainerEnabled: Boolean = false, - gatingCheckModifier: suspend () -> Unit + gatingCheckModifier: suspend () -> Unit, ) { initCollectors() allPreconditionsToRunFaceAuthAreTrue(sceneContainerEnabled) @@ -1194,7 +1195,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { // Trigger authenticate with detection fallback underTest.requestAuthenticate( FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, - fallbackToDetection = true + fallbackToDetection = true, ) runCurrent() @@ -1213,7 +1214,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { // Try to run detect again underTest.requestAuthenticate( FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, - fallbackToDetection = true + fallbackToDetection = true, ) // Detect won't run because preconditions are not true anymore. @@ -1242,7 +1243,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { // Keyguard is not going away kosmos.fakeKeyguardTransitionRepository.sendTransitionStep( TransitionStep(KeyguardState.OFF, KeyguardState.LOCKSCREEN, value = 1.0f), - validateStep = false + validateStep = false, ) kosmos.sceneInteractor.setTransitionState( MutableStateFlow(ObservableTransitionState.Idle(Scenes.Lockscreen)) @@ -1263,7 +1264,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { keyguardTransitionRepository.sendTransitionSteps( from = KeyguardState.AOD, to = KeyguardState.LOCKSCREEN, - testScope + testScope, ) runCurrent() } @@ -1315,7 +1316,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { any(), any(), isNull(), - any(FaceAuthenticateOptions::class.java) + any(FaceAuthenticateOptions::class.java), ) } @@ -1334,7 +1335,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */, "00000001" /* serialNumber */, - "" /* softwareVersion */ + "", /* softwareVersion */ ) ) return FaceSensorPropertiesInternal( @@ -1345,7 +1346,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { FaceSensorProperties.TYPE_UNKNOWN, supportsFaceDetection /* supportsFaceDetection */, true /* supportsSelfIllumination */, - false /* resetLockoutRequiresChallenge */ + false, /* resetLockoutRequiresChallenge */ ) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt index e36d2455d316..d2d8ab9d5cb7 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt @@ -55,6 +55,7 @@ import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.se import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.power.shared.model.WakeSleepReason import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.testKosmos import com.android.systemui.user.data.model.SelectionStatus @@ -298,9 +299,12 @@ class DeviceEntryFaceAuthInteractorTest : SysuiTestCase() { testScope.runTest { underTest.start() - kosmos.sceneInteractor.changeScene(Scenes.Bouncer, "for-test") + kosmos.sceneInteractor.snapToScene(Scenes.Lockscreen, "for-test") + kosmos.sceneInteractor.showOverlay(Overlays.Bouncer, "for-test") kosmos.sceneInteractor.setTransitionState( - MutableStateFlow(ObservableTransitionState.Idle(Scenes.Bouncer)) + MutableStateFlow( + ObservableTransitionState.Idle(Scenes.Lockscreen, setOf(Overlays.Bouncer)) + ) ) runCurrent() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt index 84f08f14a86a..d247137868f4 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt @@ -45,6 +45,7 @@ import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticati import com.android.systemui.kosmos.testScope import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.domain.startable.sceneContainerStartable +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.statusbar.sysuiStatusBarStateController import com.android.systemui.testKosmos @@ -179,7 +180,7 @@ class DeviceEntryInteractorTest : SysuiTestCase() { kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true) switchToScene(Scenes.Lockscreen) runCurrent() - switchToScene(Scenes.Bouncer) + showBouncer() val isDeviceEntered by collectLastValue(underTest.isDeviceEntered) assertThat(isDeviceEntered).isFalse() @@ -375,6 +376,7 @@ class DeviceEntryInteractorTest : SysuiTestCase() { fun showOrUnlockDevice_noAlternateBouncer_switchesToBouncerScene() = testScope.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene) + val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) switchToScene(Scenes.Lockscreen) assertThat(currentScene).isEqualTo(Scenes.Lockscreen) @@ -386,7 +388,7 @@ class DeviceEntryInteractorTest : SysuiTestCase() { underTest.attemptDeviceEntry() - assertThat(currentScene).isEqualTo(Scenes.Bouncer) + assertThat(currentOverlays).contains(Overlays.Bouncer) } @Test @@ -437,6 +439,7 @@ class DeviceEntryInteractorTest : SysuiTestCase() { assertThat(isDeviceEntered).isFalse() assertThat(isDeviceEnteredDirectly).isFalse() val currentScene by collectLastValue(sceneInteractor.currentScene) + val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) assertThat(currentScene).isEqualTo(Scenes.Lockscreen) // Navigate to shade and bouncer: @@ -446,8 +449,8 @@ class DeviceEntryInteractorTest : SysuiTestCase() { // be shown and successful authentication should take the user back to where they are, // the shade scene. sysuiStatusBarStateController.setLeaveOpenOnKeyguardHide(true) - switchToScene(Scenes.Bouncer) - assertThat(currentScene).isEqualTo(Scenes.Bouncer) + showBouncer() + assertThat(currentOverlays).contains(Overlays.Bouncer) assertThat(isDeviceEntered).isFalse() assertThat(isDeviceEnteredDirectly).isFalse() @@ -465,6 +468,19 @@ class DeviceEntryInteractorTest : SysuiTestCase() { runCurrent() } + private fun TestScope.showBouncer() { + sceneInteractor.showOverlay(Overlays.Bouncer, "reason") + sceneInteractor.setTransitionState( + flowOf( + ObservableTransitionState.Idle( + sceneInteractor.currentScene.value, + setOf(Overlays.Bouncer), + ) + ) + ) + runCurrent() + } + private suspend fun givenCanShowAlternateBouncer() { val canShowAlternateBouncer by testScope.collectLastValue(kosmos.alternateBouncerInteractor.canShowAlternateBouncer) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntrySourceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntrySourceInteractorTest.kt index a5c0da52d86d..f15fd28e34f0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntrySourceInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntrySourceInteractorTest.kt @@ -45,6 +45,7 @@ import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticati import com.android.systemui.kosmos.testScope import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.statusbar.phone.dozeScrimController import com.android.systemui.statusbar.phone.screenOffAnimationController @@ -145,6 +146,7 @@ class DeviceEntrySourceInteractorTest : SysuiTestCase() { whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true) configureDeviceEntryBiometricAuthSuccessState(isFingerprintAuth = true) configureBiometricUnlockState( + primaryBouncerVisible = false, alternateBouncerVisible = false, sceneKey = Scenes.Lockscreen, ) @@ -167,7 +169,11 @@ class DeviceEntrySourceInteractorTest : SysuiTestCase() { whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(false) configureDeviceEntryBiometricAuthSuccessState(isFingerprintAuth = true) - configureBiometricUnlockState(alternateBouncerVisible = false, sceneKey = Scenes.Dream) + configureBiometricUnlockState( + primaryBouncerVisible = false, + alternateBouncerVisible = false, + sceneKey = Scenes.Dream, + ) runCurrent() assertThat(deviceEntryFromBiometricSource) @@ -185,8 +191,9 @@ class DeviceEntrySourceInteractorTest : SysuiTestCase() { whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true) configureDeviceEntryBiometricAuthSuccessState(isFingerprintAuth = true) configureBiometricUnlockState( + primaryBouncerVisible = true, alternateBouncerVisible = false, - sceneKey = Scenes.Bouncer, + sceneKey = Scenes.Lockscreen, ) runCurrent() @@ -205,6 +212,7 @@ class DeviceEntrySourceInteractorTest : SysuiTestCase() { whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true) configureDeviceEntryBiometricAuthSuccessState(isFingerprintAuth = true) configureBiometricUnlockState( + primaryBouncerVisible = false, alternateBouncerVisible = false, sceneKey = Scenes.Lockscreen, ) @@ -225,6 +233,7 @@ class DeviceEntrySourceInteractorTest : SysuiTestCase() { whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true) configureDeviceEntryBiometricAuthSuccessState(isFingerprintAuth = true) configureBiometricUnlockState( + primaryBouncerVisible = false, alternateBouncerVisible = true, sceneKey = Scenes.Lockscreen, ) @@ -247,6 +256,7 @@ class DeviceEntrySourceInteractorTest : SysuiTestCase() { whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true) configureDeviceEntryBiometricAuthSuccessState(isFaceAuth = true) configureBiometricUnlockState( + primaryBouncerVisible = false, alternateBouncerVisible = false, sceneKey = Scenes.Lockscreen, ) @@ -274,6 +284,7 @@ class DeviceEntrySourceInteractorTest : SysuiTestCase() { whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true) configureDeviceEntryBiometricAuthSuccessState(isFaceAuth = true) configureBiometricUnlockState( + primaryBouncerVisible = false, alternateBouncerVisible = false, sceneKey = Scenes.Lockscreen, ) @@ -295,8 +306,9 @@ class DeviceEntrySourceInteractorTest : SysuiTestCase() { whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true) configureDeviceEntryBiometricAuthSuccessState(isFaceAuth = true) configureBiometricUnlockState( + primaryBouncerVisible = true, alternateBouncerVisible = false, - sceneKey = Scenes.Bouncer, + sceneKey = Scenes.Lockscreen, ) runCurrent() @@ -314,7 +326,11 @@ class DeviceEntrySourceInteractorTest : SysuiTestCase() { whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true) configureDeviceEntryBiometricAuthSuccessState(isFaceAuth = true) - configureBiometricUnlockState(alternateBouncerVisible = false, sceneKey = Scenes.Shade) + configureBiometricUnlockState( + primaryBouncerVisible = false, + alternateBouncerVisible = false, + sceneKey = Scenes.Shade, + ) runCurrent() // MODE_NONE does not dismiss keyguard @@ -333,6 +349,7 @@ class DeviceEntrySourceInteractorTest : SysuiTestCase() { whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true) configureDeviceEntryBiometricAuthSuccessState(isFaceAuth = true) configureBiometricUnlockState( + primaryBouncerVisible = false, alternateBouncerVisible = false, sceneKey = Scenes.Lockscreen, ) @@ -354,6 +371,7 @@ class DeviceEntrySourceInteractorTest : SysuiTestCase() { kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(onTop = true) configureDeviceEntryBiometricAuthSuccessState(isFaceAuth = true) configureBiometricUnlockState( + primaryBouncerVisible = false, alternateBouncerVisible = true, sceneKey = Scenes.Lockscreen, ) @@ -381,13 +399,20 @@ class DeviceEntrySourceInteractorTest : SysuiTestCase() { } private fun configureBiometricUnlockState( + primaryBouncerVisible: Boolean, alternateBouncerVisible: Boolean, sceneKey: SceneKey, ) { kosmos.keyguardBouncerRepository.setAlternateVisible(alternateBouncerVisible) kosmos.sceneInteractor.changeScene(sceneKey, "reason") + if (primaryBouncerVisible) kosmos.sceneInteractor.showOverlay(Overlays.Bouncer, "reason") kosmos.sceneInteractor.setTransitionState( - MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(sceneKey)) + MutableStateFlow<ObservableTransitionState>( + ObservableTransitionState.Idle( + sceneKey, + if (primaryBouncerVisible) setOf(Overlays.Bouncer) else emptySet(), + ) + ) ) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt index 526510a98e37..b42eddd12e4e 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt @@ -77,6 +77,7 @@ import com.android.systemui.navigationbar.gestural.domain.TaskInfo import com.android.systemui.navigationbar.gestural.domain.TaskMatcher import com.android.systemui.scene.data.repository.sceneContainerRepository import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.testKosmos import com.android.systemui.touch.TouchInsetManager @@ -1073,7 +1074,8 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { assertThat(lifecycleRegistry.currentState).isEqualTo(Lifecycle.State.RESUMED) // Bouncer shows. - kosmos.sceneInteractor.changeScene(Scenes.Bouncer, "test") + kosmos.sceneInteractor.snapToScene(Scenes.Lockscreen, "test") + kosmos.sceneInteractor.showOverlay(Overlays.Bouncer, "test") testScope.runCurrent() mMainExecutor.runAllReady() @@ -1082,6 +1084,7 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { // Bouncer closes. kosmos.sceneInteractor.changeScene(Scenes.Dream, "test") + kosmos.sceneInteractor.hideOverlay(Overlays.Bouncer, "test") testScope.runCurrent() mMainExecutor.runAllReady() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModelTest.kt index 102ce0b51c94..69b6e6c84e7e 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModelTest.kt @@ -74,7 +74,8 @@ class DreamUserActionsViewModelTest : SysuiTestCase() { setUpState(isShadeTouchable = true, isDeviceUnlocked = false) assertThat(actions).isNotEmpty() - assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer)) + assertThat(actions?.get(Swipe.Up)) + .isEqualTo(UserActionResult.ShowOverlay(Overlays.Bouncer)) assertThat(actions?.get(Swipe.Down)).isEqualTo(UserActionResult(Scenes.Shade)) assertThat(actions?.get(Swipe.Start)).isNull() assertThat(actions?.get(Swipe.End)).isNull() @@ -99,7 +100,8 @@ class DreamUserActionsViewModelTest : SysuiTestCase() { setUpState(isShadeTouchable = true, isDeviceUnlocked = false) assertThat(actions).isNotEmpty() - assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer)) + assertThat(actions?.get(Swipe.Up)) + .isEqualTo(UserActionResult.ShowOverlay(Overlays.Bouncer)) assertThat(actions?.get(Swipe.Down)) .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade)) assertThat(actions?.get(Swipe.Start)).isNull() @@ -126,7 +128,8 @@ class DreamUserActionsViewModelTest : SysuiTestCase() { setUpState(isShadeTouchable = true, isDeviceUnlocked = false) assertThat(actions).isNotEmpty() - assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer)) + assertThat(actions?.get(Swipe.Up)) + .isEqualTo(UserActionResult.ShowOverlay(Overlays.Bouncer)) assertThat(actions?.get(Swipe.Down)) .isEqualTo(UserActionResult.ShowOverlay(Overlays.NotificationsShade)) assertThat(actions?.get(Swipe.Start)).isNull() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModelTest.kt index 57bcc1407e24..63be132dacaa 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModelTest.kt @@ -248,6 +248,23 @@ class KeyboardTouchpadTutorialViewModelTest : SysuiTestCase() { } @Test + fun screensOrderUntilFinish_whenAutoProceed() = + testScope.runTest { + val screens by collectValues(viewModel.screen) + val closeActivity by collectLastValue(viewModel.closeActivity) + + peripheralsState(keyboardConnected = true, touchpadConnected = true) + + autoProceed() + autoProceed() + // No autoproceeding at the last screen + goToNextScreen() + + assertThat(screens).containsExactly(BACK_GESTURE, HOME_GESTURE, ACTION_KEY).inOrder() + assertThat(closeActivity).isTrue() + } + + @Test fun activityFinishes_ifTouchpadModuleIsNotPresent() = testScope.runTest { val viewModel = @@ -299,6 +316,11 @@ class KeyboardTouchpadTutorialViewModelTest : SysuiTestCase() { runCurrent() } + private suspend fun TestScope.autoProceed() { + viewModel.onAutoProceed() + runCurrent() + } + private fun TestScope.goBack() { viewModel.onBack() runCurrent() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt index 32a631b191bf..043daf02f279 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt @@ -8,16 +8,19 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.flags.DisableSceneContainer import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.flags.fakeFeatureFlagsClassic +import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.scene.data.repository.Idle import com.android.systemui.scene.data.repository.setSceneTransition +import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.testKosmos import com.android.systemui.utils.GlobalWindowManager @@ -130,7 +133,7 @@ class ResourceTrimmerTest : SysuiTestCase() { keyguardTransitionRepository.sendTransitionSteps( from = KeyguardState.LOCKSCREEN, to = KeyguardState.GONE, - testScope + testScope, ) verify(globalWindowManager, times(1)) .trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) @@ -141,6 +144,13 @@ class ResourceTrimmerTest : SysuiTestCase() { @EnableSceneContainer fun keyguardTransitionsToGone_trimsFontCache_scene_container() = testScope.runTest { + kosmos.sceneInteractor.snapToScene(Scenes.Lockscreen, "reason") + kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( + SuccessFingerprintAuthenticationStatus(0, true) + ) + runCurrent() + + kosmos.sceneInteractor.changeScene(Scenes.Gone, "") kosmos.setSceneTransition(Idle(Scenes.Gone)) verify(globalWindowManager, times(1)) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt index 425079d7b5d7..5bcf73b4719a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt @@ -41,7 +41,7 @@ import com.android.systemui.keyguard.data.repository.FakeTrustRepository import com.android.systemui.kosmos.testScope import com.android.systemui.res.R import com.android.systemui.scene.domain.interactor.sceneInteractor -import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.testKosmos import com.android.systemui.user.domain.interactor.SelectedUserInteractor @@ -173,7 +173,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { collectLastValue(underTest.showIndicatorForDeviceEntry) runCurrent() - updateBouncerScene( + updateBouncer( isActive = true, fpsDetectionRunning = true, isUnlockingWithFpAllowed = true, @@ -199,7 +199,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { collectLastValue(underTest.showIndicatorForDeviceEntry) runCurrent() - updateBouncerScene( + updateBouncer( isActive = false, fpsDetectionRunning = true, isUnlockingWithFpAllowed = true, @@ -248,7 +248,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { collectLastValue(underTest.showIndicatorForDeviceEntry) runCurrent() - updateBouncerScene( + updateBouncer( isActive = true, fpsDetectionRunning = false, isUnlockingWithFpAllowed = true, @@ -264,7 +264,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { collectLastValue(underTest.showIndicatorForDeviceEntry) runCurrent() - updateBouncerScene( + updateBouncer( isActive = true, fpsDetectionRunning = true, isUnlockingWithFpAllowed = false, @@ -349,15 +349,16 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { ) } - private fun TestScope.updateBouncerScene( + private fun TestScope.updateBouncer( isActive: Boolean, fpsDetectionRunning: Boolean, isUnlockingWithFpAllowed: Boolean, ) { - kosmos.sceneInteractor.changeScene( - if (isActive) Scenes.Bouncer else Scenes.Lockscreen, - "reason", - ) + if (isActive) { + kosmos.sceneInteractor.showOverlay(Overlays.Bouncer, "reason") + } else { + kosmos.sceneInteractor.hideOverlay(Overlays.Bouncer, "reason") + } whenever(keyguardUpdateMonitor.isFingerprintDetectionRunning) .thenReturn(fpsDetectionRunning) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt index c63f6f65c631..0a5f079dd077 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt @@ -38,10 +38,12 @@ import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticati import com.android.systemui.kosmos.collectLastValue import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.testScope +import com.android.systemui.scene.data.repository.HideOverlay import com.android.systemui.scene.data.repository.Idle import com.android.systemui.scene.data.repository.Transition import com.android.systemui.scene.data.repository.setSceneTransition import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat @@ -221,7 +223,7 @@ class KeyguardDismissActionInteractorTest : SysuiTestCase() { @Test fun doNotResetDismissActionOnUnlockedShade() = testScope.runTest { - kosmos.setSceneTransition(Idle(Scenes.Bouncer)) + kosmos.setSceneTransition(Idle(Scenes.Lockscreen, setOf(Overlays.Bouncer))) kosmos.fakeAuthenticationRepository.setAuthenticationMethod( AuthenticationMethodModel.None ) @@ -238,7 +240,12 @@ class KeyguardDismissActionInteractorTest : SysuiTestCase() { assertThat(wasOnCancelInvoked).isFalse() kosmos.setSceneTransition( - Transition(from = Scenes.Bouncer, to = Scenes.Shade, progress = flowOf(1f)) + HideOverlay( + overlay = Overlays.Bouncer, + toScene = Scenes.Shade, + currentOverlays = flowOf(emptySet()), + progress = flowOf(1f), + ) ) runCurrent() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt index 6d53e6c138c7..3fe30b7fb5c0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt @@ -24,9 +24,11 @@ import com.android.systemui.coroutines.collectValues import com.android.systemui.flags.DisableSceneContainer import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository +import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.KeyguardSurfaceBehindModel +import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.keyguard.util.mockTopActivityClassName @@ -34,6 +36,7 @@ import com.android.systemui.kosmos.testScope import com.android.systemui.scene.data.repository.Idle import com.android.systemui.scene.data.repository.Transition import com.android.systemui.scene.data.repository.setSceneTransition +import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shared.system.ActivityManagerWrapper import com.android.systemui.shared.system.activityManagerWrapper @@ -84,7 +87,7 @@ class KeyguardSurfaceBehindInteractorTest : SysuiTestCase() { assertThat(values) .containsExactly( // We're initialized in LOCKSCREEN. - KeyguardSurfaceBehindModel(alpha = 0f), + KeyguardSurfaceBehindModel(alpha = 0f) ) transitionRepository.sendTransitionStep( @@ -105,7 +108,7 @@ class KeyguardSurfaceBehindInteractorTest : SysuiTestCase() { it.alpha == 1f && it.animateFromTranslationY != 0f && it.translationY == 0f - } + }, ) transitionRepository.sendTransitionStep( @@ -125,7 +128,7 @@ class KeyguardSurfaceBehindInteractorTest : SysuiTestCase() { it.alpha == 1f && it.animateFromTranslationY != 0f && it.translationY == 0f - } + }, ) transitionRepository.sendTransitionStep( @@ -146,7 +149,7 @@ class KeyguardSurfaceBehindInteractorTest : SysuiTestCase() { it.translationY == 0f }, // Once the current state is GONE, we should default to alpha = 1f. - { it == KeyguardSurfaceBehindModel(alpha = 1f) } + { it == KeyguardSurfaceBehindModel(alpha = 1f) }, ) } @@ -161,7 +164,7 @@ class KeyguardSurfaceBehindInteractorTest : SysuiTestCase() { assertThat(values) .containsExactly( // We're initialized in LOCKSCREEN. - KeyguardSurfaceBehindModel(alpha = 0f), + KeyguardSurfaceBehindModel(alpha = 0f) ) .inOrder() @@ -242,8 +245,7 @@ class KeyguardSurfaceBehindInteractorTest : SysuiTestCase() { values.assertValuesMatch( // We should be at alpha = 0f during the animation. - { it == KeyguardSurfaceBehindModel(alpha = 0f) }, - ) + { it == KeyguardSurfaceBehindModel(alpha = 0f) }) } @Test @@ -251,14 +253,19 @@ class KeyguardSurfaceBehindInteractorTest : SysuiTestCase() { fun testSurfaceBehindModel_toAppSurface_scene_container() = testScope.runTest { val values by collectValues(underTest.viewParams) + kosmos.sceneInteractor.snapToScene(Scenes.Lockscreen, "") + kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( + SuccessFingerprintAuthenticationStatus(0, true) + ) runCurrent() assertThat(values) .containsExactly( // We're initialized in LOCKSCREEN. - KeyguardSurfaceBehindModel(alpha = 0f), + KeyguardSurfaceBehindModel(alpha = 0f) ) + kosmos.sceneInteractor.changeScene(Scenes.Gone, "") kosmos.setSceneTransition(Transition(Scenes.Lockscreen, Scenes.Gone)) values.assertValuesMatch( @@ -270,7 +277,7 @@ class KeyguardSurfaceBehindInteractorTest : SysuiTestCase() { it.alpha == 1f && it.animateFromTranslationY != 0f && it.translationY == 0f - } + }, ) kosmos.setSceneTransition(Idle(Scenes.Gone)) @@ -284,7 +291,7 @@ class KeyguardSurfaceBehindInteractorTest : SysuiTestCase() { it.translationY == 0f }, // Once the current state is GONE, we should default to alpha = 1f. - { it == KeyguardSurfaceBehindModel(alpha = 1f) } + { it == KeyguardSurfaceBehindModel(alpha = 1f) }, ) } @@ -294,15 +301,20 @@ class KeyguardSurfaceBehindInteractorTest : SysuiTestCase() { testScope.runTest { val values by collectValues(underTest.viewParams) activityManagerWrapper.mockTopActivityClassName(LAUNCHER_ACTIVITY_NAME) + kosmos.sceneInteractor.snapToScene(Scenes.Lockscreen, "") + kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( + SuccessFingerprintAuthenticationStatus(0, true) + ) runCurrent() assertThat(values) .containsExactly( // We're initialized in LOCKSCREEN. - KeyguardSurfaceBehindModel(alpha = 0f), + KeyguardSurfaceBehindModel(alpha = 0f) ) .inOrder() + kosmos.sceneInteractor.changeScene(Scenes.Gone, "") kosmos.setSceneTransition(Transition(Scenes.Lockscreen, Scenes.Gone)) assertThat(values) @@ -340,8 +352,7 @@ class KeyguardSurfaceBehindInteractorTest : SysuiTestCase() { values.assertValuesMatch( // We should be at alpha = 0f during the animation. - { it == KeyguardSurfaceBehindModel(alpha = 0f) }, - ) + { it == KeyguardSurfaceBehindModel(alpha = 0f) }) } @Test @@ -435,6 +446,13 @@ class KeyguardSurfaceBehindInteractorTest : SysuiTestCase() { testScope.runTest { val isAnimatingSurface by collectLastValue(underTest.isAnimatingSurface) + kosmos.sceneInteractor.snapToScene(Scenes.Lockscreen, "") + kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( + SuccessFingerprintAuthenticationStatus(0, true) + ) + runCurrent() + + kosmos.sceneInteractor.changeScene(Scenes.Gone, "") kosmos.setSceneTransition(Idle(Scenes.Gone)) kosmos.notificationLaunchAnimationInteractor.setIsLaunchAnimationRunning(true) runCurrent() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt index 0b42898d82ae..2d1c905b6db7 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt @@ -24,6 +24,7 @@ import com.android.systemui.coroutines.collectValues import com.android.systemui.flags.DisableSceneContainer import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository +import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState @@ -34,15 +35,20 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN import com.android.systemui.keyguard.shared.model.KeyguardState.OFF import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER import com.android.systemui.keyguard.shared.model.KeyguardState.UNDEFINED +import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus import com.android.systemui.keyguard.shared.model.TransitionState.CANCELED import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING import com.android.systemui.keyguard.shared.model.TransitionState.STARTED import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.kosmos.testScope +import com.android.systemui.scene.data.repository.HideOverlay import com.android.systemui.scene.data.repository.Idle +import com.android.systemui.scene.data.repository.ShowOverlay import com.android.systemui.scene.data.repository.Transition import com.android.systemui.scene.data.repository.setSceneTransition +import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat @@ -295,11 +301,13 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() { inTransition, ) - kosmos.setSceneTransition(Transition(Scenes.Gone, Scenes.Bouncer)) + kosmos.setSceneTransition( + ShowOverlay(overlay = Overlays.Bouncer, fromScene = Scenes.Gone) + ) assertEquals(listOf(false, true, false, true), inTransition) - kosmos.setSceneTransition(Idle(Scenes.Bouncer)) + kosmos.setSceneTransition(Idle(Scenes.Lockscreen, setOf(Overlays.Bouncer))) assertEquals(listOf(false, true, false, true, false), inTransition) } @@ -713,6 +721,11 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() { TransitionStep(AOD, DOZING, 1f, FINISHED), ) + kosmos.sceneInteractor.snapToScene(Scenes.Lockscreen, "reason") + kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( + SuccessFingerprintAuthenticationStatus(0, true) + ) + assertThat(results) .isEqualTo( listOf( @@ -724,6 +737,7 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() { assertThat(results).isEqualTo(listOf(false)) + kosmos.sceneInteractor.changeScene(Scenes.Gone, "reason") kosmos.setSceneTransition(Idle(Scenes.Gone)) assertThat(results).isEqualTo(listOf(false, true)) @@ -732,6 +746,7 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() { assertThat(results).isEqualTo(listOf(false, true)) + kosmos.sceneInteractor.changeScene(Scenes.Lockscreen, "reason") kosmos.setSceneTransition(Idle(Scenes.Lockscreen)) assertThat(results).isEqualTo(listOf(false, true, false)) @@ -740,6 +755,7 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() { assertThat(results).isEqualTo(listOf(false, true, false)) + kosmos.sceneInteractor.changeScene(Scenes.Gone, "reason") kosmos.setSceneTransition(Idle(Scenes.Gone)) assertThat(results).isEqualTo(listOf(false, true, false, true)) @@ -1042,13 +1058,23 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() { progress.emit(0.6f) runCurrent() - kosmos.setSceneTransition(Transition(Scenes.Gone, Scenes.Bouncer, progress = progress)) + kosmos.setSceneTransition( + ShowOverlay( + overlay = Overlays.Bouncer, + fromScene = Scenes.Gone, + progress = progress, + ) + ) progress.emit(0.1f) runCurrent() kosmos.setSceneTransition( - Transition(Scenes.Bouncer, Scenes.Lockscreen, progress = progress) + HideOverlay( + overlay = Overlays.Bouncer, + toScene = Scenes.Lockscreen, + progress = progress, + ) ) progress.emit(0.3f) @@ -1087,12 +1113,20 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() { kosmos.setSceneTransition(Idle(Scenes.Gone)) - kosmos.setSceneTransition(Transition(Scenes.Gone, Scenes.Bouncer, progress = progress)) + kosmos.setSceneTransition( + ShowOverlay( + overlay = Overlays.Bouncer, + fromScene = Scenes.Gone, + progress = progress, + ) + ) progress.emit(0.1f) runCurrent() - kosmos.setSceneTransition(Transition(Scenes.Bouncer, Scenes.Gone, progress = progress)) + kosmos.setSceneTransition( + HideOverlay(overlay = Overlays.Bouncer, toScene = Scenes.Gone, progress = progress) + ) progress.emit(0.3f) runCurrent() @@ -1125,7 +1159,13 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() { progress1.emit(0.1f) runCurrent() - kosmos.setSceneTransition(Transition(Scenes.Gone, Scenes.Bouncer, progress = progress2)) + kosmos.setSceneTransition( + ShowOverlay( + overlay = Overlays.Bouncer, + fromScene = Scenes.Gone, + progress = progress2, + ) + ) progress2.emit(0.3f) runCurrent() @@ -1152,8 +1192,14 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() { val currentStatesMapped by collectValues(underTest.transition(Edge.create(LOCKSCREEN, Scenes.Gone))) + kosmos.sceneInteractor.snapToScene(Scenes.Lockscreen, "reason") + kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( + SuccessFingerprintAuthenticationStatus(0, true) + ) + kosmos.setSceneTransition(Transition(Scenes.Gone, Scenes.Lockscreen)) val sendStep1 = TransitionStep(UNDEFINED, LOCKSCREEN, 0f, STARTED) + kosmos.sceneInteractor.changeScene(Scenes.Gone, "reason") kosmos.setSceneTransition(Idle(Scenes.Gone)) val sendStep2 = TransitionStep(UNDEFINED, LOCKSCREEN, 0.6f, CANCELED) sendSteps(sendStep1, sendStep2) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt index b7c162b4d6eb..f0eedee48e57 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt @@ -37,11 +37,13 @@ import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.kosmos.testScope import com.android.systemui.scene.data.model.asIterable import com.android.systemui.scene.data.model.sceneStackOf +import com.android.systemui.scene.data.repository.HideOverlay import com.android.systemui.scene.data.repository.Idle import com.android.systemui.scene.data.repository.Transition import com.android.systemui.scene.data.repository.setSceneTransition import com.android.systemui.scene.domain.interactor.sceneBackInteractor import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.testKosmos import com.android.systemui.util.mockito.mock @@ -250,12 +252,14 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { fun surfaceBehindVisibility_fromBouncerToGone_becomesTrue() = testScope.runTest { val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility) - val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene) + val currentOverlays by collectLastValue(kosmos.sceneInteractor.currentOverlays) // Before the transition, we start on Bouncer so the surface should start invisible. - kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Bouncer)) - kosmos.sceneInteractor.changeScene(Scenes.Bouncer, "") - assertThat(currentScene).isEqualTo(Scenes.Bouncer) + kosmos.setSceneTransition( + ObservableTransitionState.Idle(Scenes.Lockscreen, setOf(Overlays.Bouncer)) + ) + kosmos.sceneInteractor.showOverlay(Overlays.Bouncer, "") + assertThat(currentOverlays).contains(Overlays.Bouncer) assertThat(isSurfaceBehindVisible).isFalse() // Unlocked with fingerprint. @@ -267,44 +271,45 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { // the // threshold. kosmos.setSceneTransition( - ObservableTransitionState.Transition( - fromScene = Scenes.Bouncer, + ObservableTransitionState.Transition.hideOverlay( + overlay = Overlays.Bouncer, toScene = Scenes.Gone, - isInitiatedByUserInput = false, - isUserInputOngoing = flowOf(false), + currentOverlays = flowOf(setOf(Overlays.Bouncer)), progress = flowOf( FromPrimaryBouncerTransitionInteractor .TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD ), - currentScene = flowOf(Scenes.Bouncer), + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) ) - assertThat(currentScene).isEqualTo(Scenes.Bouncer) + assertThat(currentOverlays).contains(Overlays.Bouncer) assertThat(isSurfaceBehindVisible).isFalse() // Once the transition passes the threshold, the surface should become visible. kosmos.setSceneTransition( - ObservableTransitionState.Transition( - fromScene = Scenes.Bouncer, + ObservableTransitionState.Transition.hideOverlay( + overlay = Overlays.Bouncer, toScene = Scenes.Gone, - isInitiatedByUserInput = false, - isUserInputOngoing = flowOf(false), + currentOverlays = flowOf(setOf(Overlays.Bouncer)), progress = flowOf( FromPrimaryBouncerTransitionInteractor .TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD + 0.01f ), - currentScene = flowOf(Scenes.Gone), + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) ) - assertThat(currentScene).isEqualTo(Scenes.Bouncer) + assertThat(currentOverlays).contains(Overlays.Bouncer) assertThat(isSurfaceBehindVisible).isTrue() // After the transition, settles on Gone. Surface behind should stay visible now. kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Gone)) kosmos.sceneInteractor.changeScene(Scenes.Gone, "") - assertThat(currentScene).isEqualTo(Scenes.Gone) + kosmos.sceneInteractor.hideOverlay(Overlays.Bouncer, "") + assertThat(currentOverlays).doesNotContain(Overlays.Bouncer) assertThat(isSurfaceBehindVisible).isTrue() } @@ -1012,6 +1017,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { kosmos.setSceneTransition(Idle(Scenes.Lockscreen)) val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene) + val currentOverlays by collectLastValue(kosmos.sceneInteractor.currentOverlays) assertThat(currentScene).isEqualTo(Scenes.Lockscreen) val lockscreenVisibility by collectLastValue(underTest.value.lockscreenVisibility) @@ -1038,12 +1044,16 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { assertThat(currentScene).isEqualTo(Scenes.Shade) assertThat(lockscreenVisibility).isTrue() - kosmos.setSceneTransition(Idle(Scenes.Bouncer)) - kosmos.sceneInteractor.changeScene(Scenes.Bouncer, "") - assertThat(currentScene).isEqualTo(Scenes.Bouncer) + kosmos.setSceneTransition(Idle(Scenes.Lockscreen, setOf(Overlays.Bouncer))) + kosmos.sceneInteractor.snapToScene(Scenes.Lockscreen, "") + kosmos.sceneInteractor.showOverlay(Overlays.Bouncer, "") + assertThat(currentScene).isEqualTo(Scenes.Lockscreen) + assertThat(currentOverlays).contains(Overlays.Bouncer) assertThat(lockscreenVisibility).isTrue() - kosmos.setSceneTransition(Transition(from = Scenes.Bouncer, to = Scenes.Gone)) + kosmos.setSceneTransition( + HideOverlay(overlay = Overlays.Bouncer, toScene = Scenes.Gone) + ) assertThat(lockscreenVisibility).isTrue() kosmos.setSceneTransition(Idle(Scenes.Gone)) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt index 3cff0fc96af4..41ec065dc560 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt @@ -1220,13 +1220,13 @@ class LockscreenSceneTransitionInteractorTest : SysuiTestCase() { progress.value = 0.4f sceneTransitions.value = - ObservableTransitionState.Transition( - Scenes.Lockscreen, - Scenes.Bouncer, - flowOf(Scenes.Lockscreen), - progress, - false, - flowOf(false), + ObservableTransitionState.Transition.showOverlay( + overlay = Overlays.Bouncer, + fromScene = Scenes.Lockscreen, + currentOverlays = flowOf(setOf(Overlays.Bouncer)), + progress = progress, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) assertTransition( @@ -1342,7 +1342,7 @@ class LockscreenSceneTransitionInteractorTest : SysuiTestCase() { sceneTransitions.value = ObservableTransitionState.Transition( Scenes.Gone, - Scenes.Bouncer, + Scenes.Dream, flowOf(Scenes.Lockscreen), progress, false, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt index a31728ce5e18..91cb1ff266c9 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt @@ -39,6 +39,7 @@ import com.android.systemui.keyguard.ui.transitions.blurConfig import com.android.systemui.kosmos.testScope import com.android.systemui.scene.data.repository.sceneContainerRepository import com.android.systemui.scene.shared.flag.SceneContainerFlag +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.shadeTestUtil import com.android.systemui.testKosmos @@ -125,14 +126,15 @@ class LockscreenToPrimaryBouncerTransitionViewModelTest(flags: FlagsParameteriza kosmos.sceneContainerRepository.setTransitionState(transitionState) transitionState.value = - ObservableTransitionState.Transition( + ObservableTransitionState.Transition.showOverlay( + overlay = Overlays.Bouncer, fromScene = Scenes.Lockscreen, - toScene = Scenes.Bouncer, - emptyFlow(), - emptyFlow(), - false, - emptyFlow(), + currentOverlays = emptyFlow(), + progress = emptyFlow(), + isInitiatedByUserInput = false, + isUserInputOngoing = emptyFlow(), ) + runCurrent() // fade out repository.sendTransitionStep(step(0f, TransitionState.STARTED)) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt index f357d0c80822..f98ddd240fd0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt @@ -19,6 +19,7 @@ package com.android.systemui.keyguard.ui.viewmodel import android.platform.test.annotations.EnableFlags import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.ContentKey import com.android.compose.animation.scene.Edge import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.Swipe @@ -135,11 +136,11 @@ class LockscreenUserActionsViewModelTest : SysuiTestCase() { private fun expectedUpDestination( canSwipeToEnter: Boolean, isShadeTouchable: Boolean, - ): SceneKey? { + ): ContentKey? { return when { !isShadeTouchable -> null canSwipeToEnter -> Scenes.Gone - else -> Scenes.Bouncer + else -> Overlays.Bouncer } } } @@ -215,15 +216,17 @@ class LockscreenUserActionsViewModelTest : SysuiTestCase() { ) ) - val upScene by - collectLastValue( - (userActions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene?.let { - scene -> - kosmos.sceneInteractor.resolveSceneFamily(scene) - } ?: flowOf(null) - ) + val upContent = + userActions?.get(Swipe.Up)?.let { result -> + when (result) { + is UserActionResult.ChangeScene -> result.toScene + is UserActionResult.ShowOverlay -> result.overlay + is UserActionResult.HideOverlay -> result.overlay + is UserActionResult.ReplaceByOverlay -> result.overlay + } + } - assertThat(upScene) + assertThat(upContent) .isEqualTo( expectedUpDestination( canSwipeToEnter = canSwipeToEnter, @@ -292,15 +295,17 @@ class LockscreenUserActionsViewModelTest : SysuiTestCase() { } } - val upScene by - collectLastValue( - (userActions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene?.let { - scene -> - kosmos.sceneInteractor.resolveSceneFamily(scene) - } ?: flowOf(null) - ) + val upContent = + userActions?.get(Swipe.Up)?.let { result -> + when (result) { + is UserActionResult.ChangeScene -> result.toScene + is UserActionResult.ShowOverlay -> result.overlay + is UserActionResult.HideOverlay -> result.overlay + is UserActionResult.ReplaceByOverlay -> result.overlay + } + } - assertThat(upScene) + assertThat(upContent) .isEqualTo( expectedUpDestination( canSwipeToEnter = canSwipeToEnter, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/dialog/MediaOutputAdapterLegacyTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/dialog/MediaOutputAdapterLegacyTest.java index f293614954e9..2db2199602b8 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/dialog/MediaOutputAdapterLegacyTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/dialog/MediaOutputAdapterLegacyTest.java @@ -268,6 +268,10 @@ public class MediaOutputAdapterLegacyTest extends SysuiTestCase { assertThat(mViewHolder.mSeekBar.getContentDescription()).isNotNull(); assertThat(mViewHolder.mContainerLayout.isFocusable()).isFalse(); + assertThat(mViewHolder.mContainerLayout.getImportantForAccessibility()).isEqualTo( + View.IMPORTANT_FOR_ACCESSIBILITY_NO); + assertThat(mViewHolder.mTextContent.getImportantForAccessibility()).isEqualTo( + View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); } @Test @@ -511,6 +515,11 @@ public class MediaOutputAdapterLegacyTest extends SysuiTestCase { assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE); assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_2); assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mContainerLayout.isFocusable()).isTrue(); + assertThat(mViewHolder.mContainerLayout.getImportantForAccessibility()).isEqualTo( + View.IMPORTANT_FOR_ACCESSIBILITY_YES); + assertThat(mViewHolder.mTextContent.getImportantForAccessibility()).isEqualTo( + View.IMPORTANT_FOR_ACCESSIBILITY_YES); assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE); assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE); assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.VISIBLE); @@ -750,7 +759,7 @@ public class MediaOutputAdapterLegacyTest extends SysuiTestCase { .onCreateViewHolder(new LinearLayout(mContext), 0); mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1); - mViewHolder.mEndTouchArea.performClick(); + mViewHolder.mCheckBox.performClick(); verify(mMediaSwitchingController).addDeviceToPlayMedia(mMediaDevice2); } @@ -894,7 +903,7 @@ public class MediaOutputAdapterLegacyTest extends SysuiTestCase { .onCreateViewHolder(new LinearLayout(mContext), 0); mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0); - mViewHolder.mEndTouchArea.performClick(); + mViewHolder.mCheckBox.performClick(); verify(mMediaSwitchingController).removeDeviceFromPlayMedia(mMediaDevice1); } @@ -1050,7 +1059,7 @@ public class MediaOutputAdapterLegacyTest extends SysuiTestCase { new LinearLayout(mContext), MediaItem.MediaItemType.TYPE_DEVICE); mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0); - mViewHolder.mEndTouchArea.performClick(); + mViewHolder.mEndClickIcon.performClick(); mViewHolder = (MediaOutputAdapterLegacy.MediaDeviceViewHolderLegacy) mMediaOutputAdapter .onCreateViewHolder( new LinearLayout(mContext), MediaItem.MediaItemType.TYPE_DEVICE); @@ -1073,7 +1082,7 @@ public class MediaOutputAdapterLegacyTest extends SysuiTestCase { new LinearLayout(mContext), MediaItem.MediaItemType.TYPE_DEVICE); mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0); - mViewHolder.mEndTouchArea.performClick(); + mViewHolder.mEndClickIcon.performClick(); mViewHolder = (MediaOutputAdapterLegacy.MediaDeviceViewHolderLegacy) mMediaOutputAdapter .onCreateViewHolder( new LinearLayout(mContext), MediaItem.MediaItemType.TYPE_DEVICE); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt index b23cd5e5547f..917f3564a1bd 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt @@ -103,20 +103,6 @@ class NotificationsShadeOverlayContentViewModelTest : SysuiTestCase() { } @Test - fun bouncerShown_hidesShade() = - testScope.runTest { - val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) - lockDevice() - sceneInteractor.showOverlay(Overlays.NotificationsShade, "test") - assertThat(currentOverlays).contains(Overlays.NotificationsShade) - - sceneInteractor.changeScene(Scenes.Bouncer, "test") - runCurrent() - - assertThat(currentOverlays).doesNotContain(Overlays.NotificationsShade) - } - - @Test fun shadeNotTouchable_hidesShade() = testScope.runTest { val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CastTileTest.java index 31a627fe0667..765c5749cd4b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CastTileTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CastTileTest.java @@ -50,6 +50,8 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.shade.domain.interactor.FakeShadeDialogContextInteractor; +import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor; import com.android.systemui.statusbar.connectivity.IconState; import com.android.systemui.statusbar.connectivity.NetworkController; import com.android.systemui.statusbar.connectivity.SignalCallback; @@ -107,6 +109,8 @@ public class CastTileTest extends SysuiTestCase { private final FakeConnectivityRepository mConnectivityRepository = new FakeConnectivityRepository(); private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags(); + private final ShadeDialogContextInteractor mShadeDialogContextInteractor = + new FakeShadeDialogContextInteractor(mContext); private TestableLooper mTestableLooper; private CastTile mCastTile; @@ -535,7 +539,8 @@ public class CastTileTest extends SysuiTestCase { mDialogTransitionAnimator, mConnectivityRepository, mJavaAdapter, - mFeatureFlags + mFeatureFlags, + mShadeDialogContextInteractor ); mCastTile.initialize(); @@ -578,7 +583,8 @@ public class CastTileTest extends SysuiTestCase { mDialogTransitionAnimator, mConnectivityRepository, mJavaAdapter, - mFeatureFlags + mFeatureFlags, + mShadeDialogContextInteractor ); mCastTile.initialize(); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelTest.kt index bf5f9f4872f0..c69ebab7a170 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelTest.kt @@ -99,20 +99,6 @@ class QuickSettingsShadeOverlayContentViewModelTest : SysuiTestCase() { } @Test - fun bouncerShown_hidesShade() = - testScope.runTest { - val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) - lockDevice() - sceneInteractor.showOverlay(Overlays.QuickSettingsShade, "test") - assertThat(currentOverlays).contains(Overlays.QuickSettingsShade) - - sceneInteractor.changeScene(Scenes.Bouncer, "test") - runCurrent() - - assertThat(currentOverlays).doesNotContain(Overlays.QuickSettingsShade) - } - - @Test fun shadeNotTouchable_hidesShade() = testScope.runTest { val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt index c5fac08a4b35..eb630b49359d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt @@ -22,6 +22,7 @@ import android.testing.TestableLooper.RunWithLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.compose.animation.scene.ObservableTransitionState +import com.android.compose.animation.scene.OverlayKey import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.Swipe import com.android.compose.animation.scene.UserActionResult @@ -34,7 +35,7 @@ import com.android.systemui.authentication.domain.interactor.authenticationInter import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel -import com.android.systemui.bouncer.ui.viewmodel.bouncerSceneContentViewModel +import com.android.systemui.bouncer.ui.viewmodel.bouncerOverlayContentViewModel import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor import com.android.systemui.flags.EnableSceneContainer @@ -54,6 +55,7 @@ import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.se import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.domain.startable.sceneContainerStartable +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.fakeSceneDataSource import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel @@ -87,7 +89,7 @@ import org.junit.runner.RunWith * Therefore, when adding or modifying test cases, consider whether what you're testing is better * covered by a more granular unit test. * * Please reuse the helper methods in this class (for example, [putDeviceToSleep] or - * [emulateUserDrivenTransition]). + * [emulateUserDrivenSceneTransition]). * * All tests start with the device locked and with a PIN auth method. The class offers useful * methods like [setAuthMethod], [unlockDevice], [lockDevice], etc. to help you set up a starting * state that makes more sense for your test case. @@ -101,7 +103,7 @@ import org.junit.runner.RunWith @EnableSceneContainer class SceneFrameworkIntegrationTest : SysuiTestCase() { private val kosmos = testKosmos() - private var bouncerSceneJob: Job? = null + private var bouncerOverlayJob: Job? = null @Before fun setUp() = @@ -125,7 +127,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { lockscreenUserActionsViewModel.activateIn(testScope) shadeSceneContentViewModel.activateIn(testScope) shadeUserActionsViewModel.activateIn(testScope) - bouncerSceneContentViewModel.activateIn(testScope) + bouncerOverlayContentViewModel.activateIn(testScope) sceneContainerViewModel.activateIn(testScope) assertWithMessage("Initial scene key mismatch!") @@ -141,27 +143,29 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { @Test fun clickLockButtonAndEnterCorrectPin_unlocksDevice() = kosmos.runTest { - emulateUserDrivenTransition(Scenes.Bouncer) + emulateUserDrivenOverlayTransition(show = Overlays.Bouncer) fakeSceneDataSource.pause() enterPin() emulatePendingTransitionProgress(expectedVisible = false) assertCurrentScene(Scenes.Gone) + assertOverlaysEmpty() } @Test fun swipeUpOnLockscreen_enterCorrectPin_unlocksDevice() = kosmos.runTest { val actions by collectLastValue(kosmos.lockscreenUserActionsViewModel.actions) - val upDestinationSceneKey = - (actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene - assertThat(upDestinationSceneKey).isEqualTo(Scenes.Bouncer) - emulateUserDrivenTransition(to = upDestinationSceneKey) + val upDestinationOverlayKey = + (actions?.get(Swipe.Up) as? UserActionResult.ShowOverlay)?.overlay + assertThat(upDestinationOverlayKey).isEqualTo(Overlays.Bouncer) + emulateUserDrivenOverlayTransition(show = upDestinationOverlayKey) fakeSceneDataSource.pause() enterPin() emulatePendingTransitionProgress(expectedVisible = false) assertCurrentScene(Scenes.Gone) + assertOverlaysEmpty() } @Test @@ -173,7 +177,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { val upDestinationSceneKey = (actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene assertThat(upDestinationSceneKey).isEqualTo(Scenes.Gone) - emulateUserDrivenTransition(to = upDestinationSceneKey) + emulateUserDrivenSceneTransition(to = upDestinationSceneKey) } @Test @@ -184,13 +188,13 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { assertCurrentScene(Scenes.Lockscreen) // Emulate a user swipe to the shade scene. - emulateUserDrivenTransition(to = Scenes.Shade) + emulateUserDrivenSceneTransition(to = Scenes.Shade) assertCurrentScene(Scenes.Shade) val upDestinationSceneKey = (actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene assertThat(upDestinationSceneKey).isEqualTo(Scenes.Lockscreen) - emulateUserDrivenTransition(to = Scenes.Lockscreen) + emulateUserDrivenSceneTransition(to = Scenes.Lockscreen) } @Test @@ -205,17 +209,17 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { assertCurrentScene(Scenes.Lockscreen) // Emulate a user swipe to dismiss the lockscreen. - emulateUserDrivenTransition(to = Scenes.Gone) + emulateUserDrivenSceneTransition(to = Scenes.Gone) assertCurrentScene(Scenes.Gone) // Emulate a user swipe to the shade scene. - emulateUserDrivenTransition(to = Scenes.Shade) + emulateUserDrivenSceneTransition(to = Scenes.Shade) assertCurrentScene(Scenes.Shade) val upDestinationSceneKey = (actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene assertThat(upDestinationSceneKey).isEqualTo(Scenes.Gone) - emulateUserDrivenTransition(to = Scenes.Gone) + emulateUserDrivenSceneTransition(to = Scenes.Gone) } @Test @@ -270,6 +274,8 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { wakeUpDevice() assertCurrentScene(Scenes.Lockscreen) + // set UI state to match view-model + transitionState.value = ObservableTransitionState.Idle(Scenes.Lockscreen) unlockDevice() assertCurrentScene(Scenes.Gone) } @@ -302,10 +308,10 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { kosmos.runTest { setAuthMethod(AuthenticationMethodModel.Password) val actions by collectLastValue(lockscreenUserActionsViewModel.actions) - val upDestinationSceneKey = - (actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene - assertThat(upDestinationSceneKey).isEqualTo(Scenes.Bouncer) - emulateUserDrivenTransition(to = upDestinationSceneKey) + val upDestinationOverlayKey = + (actions?.get(Swipe.Up) as? UserActionResult.ShowOverlay)?.overlay + assertThat(upDestinationOverlayKey).isEqualTo(Overlays.Bouncer) + emulateUserDrivenOverlayTransition(show = upDestinationOverlayKey) fakeSceneDataSource.pause() dismissIme() @@ -319,16 +325,16 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { kosmos.runTest { setAuthMethod(AuthenticationMethodModel.Password) val actions by collectLastValue(lockscreenUserActionsViewModel.actions) - val upDestinationSceneKey = - (actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene - assertThat(upDestinationSceneKey).isEqualTo(Scenes.Bouncer) - emulateUserDrivenTransition(to = upDestinationSceneKey) + val upDestinationOverlayKey = + (actions?.get(Swipe.Up) as? UserActionResult.ShowOverlay)?.overlay + assertThat(upDestinationOverlayKey).isEqualTo(Overlays.Bouncer) + emulateUserDrivenOverlayTransition(show = upDestinationOverlayKey) - val bouncerActionButton by collectLastValue(bouncerSceneContentViewModel.actionButton) + val bouncerActionButton by collectLastValue(bouncerOverlayContentViewModel.actionButton) assertWithMessage("Bouncer action button not visible") .that(bouncerActionButton) .isNotNull() - kosmos.bouncerSceneContentViewModel.onActionButtonClicked(bouncerActionButton!!) + kosmos.bouncerOverlayContentViewModel.onActionButtonClicked(bouncerActionButton!!) // TODO(b/369765704): Assert that an activity was started once we use ActivityStarter. } @@ -339,16 +345,16 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { setAuthMethod(AuthenticationMethodModel.Password) startPhoneCall() val actions by collectLastValue(lockscreenUserActionsViewModel.actions) - val upDestinationSceneKey = - (actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene - assertThat(upDestinationSceneKey).isEqualTo(Scenes.Bouncer) - emulateUserDrivenTransition(to = upDestinationSceneKey) + val upDestinationOverlayKey = + (actions?.get(Swipe.Up) as? UserActionResult.ShowOverlay)?.overlay + assertThat(upDestinationOverlayKey).isEqualTo(Overlays.Bouncer) + emulateUserDrivenOverlayTransition(show = upDestinationOverlayKey) - val bouncerActionButton by collectLastValue(bouncerSceneContentViewModel.actionButton) + val bouncerActionButton by collectLastValue(bouncerOverlayContentViewModel.actionButton) assertWithMessage("Bouncer action button not visible during call") .that(bouncerActionButton) .isNotNull() - kosmos.bouncerSceneContentViewModel.onActionButtonClicked(bouncerActionButton!!) + kosmos.bouncerOverlayContentViewModel.onActionButtonClicked(bouncerActionButton!!) verifyCurrent(mockTelecomManager).showInCallScreen(any()) } @@ -358,7 +364,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { kosmos.runTest { setAuthMethod(AuthenticationMethodModel.None) introduceLockedSim() - assertCurrentScene(Scenes.Bouncer) + assertCurrentOverlay(Overlays.Bouncer) } @Test @@ -366,7 +372,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { kosmos.runTest { fakeSceneDataSource.pause() introduceLockedSim() - emulatePendingTransitionProgress(expectedVisible = true) + emulatePendingTransitionProgress() enterSimPin( authMethodAfterSimUnlock = AuthenticationMethodModel.None, enableLockscreen = false, @@ -380,7 +386,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { kosmos.runTest { fakeSceneDataSource.pause() introduceLockedSim() - emulatePendingTransitionProgress(expectedVisible = true) + emulatePendingTransitionProgress() enterSimPin(authMethodAfterSimUnlock = AuthenticationMethodModel.Pin) assertCurrentScene(Scenes.Lockscreen) } @@ -396,6 +402,18 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { .isEqualTo(expected) } + private fun Kosmos.assertCurrentOverlay(expected: OverlayKey) { + assertWithMessage("Expected overlay missing!") + .that(currentValue(sceneInteractor.currentOverlays)) + .contains(expected) + } + + private fun Kosmos.assertOverlaysEmpty() { + assertWithMessage("Expected no overlays, but at least one was present") + .that(currentValue(sceneInteractor.currentOverlays)) + .isEmpty() + } + /** * Returns the [SceneKey] of the current scene as displayed in the UI. * @@ -439,9 +457,10 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { } /** - * Emulates a gradual transition to the currently pending scene that's sitting in the - * [fakeSceneDataSource]. This emits a series of progress updates to the [transitionState] and - * finishes by committing the pending scene as the current scene. + * Emulates a gradual transition to the currently pending scene and overlay that are sitting in + * the [fakeSceneDataSource]. This emits a series of progress updates to the [transitionState] + * and finishes by committing the pending scene as the current scene, and the pending overlay as + * the current overlay * * In order to use this, the [fakeSceneDataSource] must be paused before this method is called. */ @@ -450,49 +469,96 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { .that(fakeSceneDataSource.isPaused) .isTrue() - val to = fakeSceneDataSource.pendingScene ?: return - val from = getCurrentSceneInUi() + val fromScene = getCurrentSceneInUi() + val toScene = fakeSceneDataSource.pendingScene ?: fromScene + + val fromOverlays = + collectLastValue(currentValue(transitionState).currentOverlays()).invoke() ?: emptySet() + val toOverlays = fakeSceneDataSource.pendingOverlays ?: fromOverlays - if (to == from) { - return + val addedOverlays = toOverlays - fromOverlays + val removedOverlays = fromOverlays - toOverlays + check( + addedOverlays.size + removedOverlays.size < 2 && + (addedOverlays.size <= 1 || removedOverlays.size <= 1) + ) { + "Detected multiple overlays being added/removed. Currently only testing single-overlay transitions." } - // Begin to transition. - val progressFlow = MutableStateFlow(0f) - transitionState.value = - ObservableTransitionState.Transition( - fromScene = getCurrentSceneInUi(), - toScene = to, - currentScene = flowOf(to), - progress = progressFlow, - isInitiatedByUserInput = false, - isUserInputOngoing = flowOf(false), - ) + if (toScene != fromScene) { + // Begin scene transition. + val progressFlow = MutableStateFlow(0f) + transitionState.value = + ObservableTransitionState.Transition( + fromScene = getCurrentSceneInUi(), + toScene = toScene, + currentScene = flowOf(toScene), + progress = progressFlow, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), + ) - // Report progress of transition. - while (currentValue(progressFlow) < 1f) { - progressFlow.value += 0.2f + // Report progress of transition. + while (currentValue(progressFlow) < 1f) { + progressFlow.value += 0.2f + } + + // End the transition and report the change. + transitionState.value = ObservableTransitionState.Idle(toScene) } - // End the transition and report the change. - transitionState.value = ObservableTransitionState.Idle(to) + if (addedOverlays.isNotEmpty() || removedOverlays.isNotEmpty()) { + // Begin overlay transition. + val progressFlow = MutableStateFlow(0f) + transitionState.value = + if (addedOverlays.size == 1) { + ObservableTransitionState.Transition.showOverlay( + overlay = addedOverlays.first(), + fromScene = toScene, + currentOverlays = flowOf(addedOverlays), + progress = progressFlow, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), + ) + } else { + ObservableTransitionState.Transition.hideOverlay( + overlay = removedOverlays.first(), + toScene = toScene, + currentOverlays = flowOf(removedOverlays), + progress = progressFlow, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), + ) + } + + // Report progress of transition. + while (currentValue(progressFlow) < 1f) { + progressFlow.value += 0.2f + } + + // End the transition and report the change, taking any scene transition into account. + transitionState.value = ObservableTransitionState.Idle(toScene, toOverlays) + } fakeSceneDataSource.unpause(force = true) - assertWithMessage("Visibility mismatch after scene transition from $from to $to!") + assertWithMessage( + "Visibility mismatch after transition from $fromScene to $toScene and $fromOverlays to $toOverlays!" + ) .that(currentValue { sceneContainerViewModel.isVisible }) .isEqualTo(expectedVisible) - assertThat(currentValue(sceneContainerViewModel.currentScene)).isEqualTo(to) + assertThat(currentValue(sceneContainerViewModel.currentScene)).isEqualTo(toScene) + assertThat(currentValue(sceneInteractor.currentOverlays)).isEqualTo(toOverlays) - bouncerSceneJob = - if (to == Scenes.Bouncer) { + bouncerOverlayJob = + if (Overlays.Bouncer in addedOverlays) { testScope.backgroundScope.launch { - bouncerSceneContentViewModel.authMethodViewModel.collect { + bouncerOverlayContentViewModel.authMethodViewModel.collect { // Do nothing. Need this to turn this otherwise cold flow, hot. } } } else { - bouncerSceneJob?.cancel() + bouncerOverlayJob?.cancel() null } } @@ -506,7 +572,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { * * @param to The scene to transition to. */ - private fun Kosmos.emulateUserDrivenTransition(to: SceneKey?) { + private fun Kosmos.emulateUserDrivenSceneTransition(to: SceneKey?) { checkNotNull(to) fakeSceneDataSource.pause() @@ -515,6 +581,22 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { emulatePendingTransitionProgress(expectedVisible = to != Scenes.Gone) } + private fun Kosmos.emulateUserDrivenOverlayTransition( + show: OverlayKey? = null, + hide: OverlayKey? = null, + ) { + fakeSceneDataSource.pause() + if (show != null && hide != null) { + sceneInteractor.replaceOverlay(from = show, to = hide, "reason") + } else if (show != null) { + sceneInteractor.showOverlay(overlay = show, "reason") + } else if (hide != null) { + sceneInteractor.hideOverlay(overlay = hide, "reason") + } + + emulatePendingTransitionProgress() + } + /** * Locks the device. * @@ -548,7 +630,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { .that(currentValue(deviceEntryInteractor.isUnlocked)) .isFalse() - emulateUserDrivenTransition(Scenes.Bouncer) + emulateUserDrivenOverlayTransition(show = Overlays.Bouncer) fakeSceneDataSource.pause() enterPin() @@ -558,17 +640,18 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { /** * Enters the correct PIN in the bouncer UI. * - * Asserts that the current scene is [Scenes.Bouncer] and that the current bouncer UI is a PIN - * before proceeding. + * Asserts that [Overlays.Bouncer] is showing and that the current bouncer UI is a PIN before + * proceeding. * * Does not assert that the device is locked or unlocked. */ private fun Kosmos.enterPin() { - assertWithMessage("Cannot enter PIN when not on the Bouncer scene!") - .that(getCurrentSceneInUi()) - .isEqualTo(Scenes.Bouncer) + val currentOverlays by collectLastValue(currentValue(transitionState).currentOverlays()) + assertWithMessage("Cannot enter PIN when Bouncer not showing!") + .that(currentOverlays) + .contains(Overlays.Bouncer) val authMethodViewModel by - collectLastValue(bouncerSceneContentViewModel.authMethodViewModel) + collectLastValue(bouncerOverlayContentViewModel.authMethodViewModel) assertWithMessage("Cannot enter PIN when not using a PIN authentication method!") .that(authMethodViewModel) .isInstanceOf(PinBouncerViewModel::class.java) @@ -583,8 +666,8 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { /** * Enters the correct PIN in the sim bouncer UI. * - * Asserts that the current scene is [Scenes.Bouncer] and that the current bouncer UI is a PIN - * before proceeding. + * Asserts that [Overlays.Bouncer] is showing and that the current bouncer UI is a PIN before + * proceeding. * * Does not assert that the device is locked or unlocked. */ @@ -592,11 +675,12 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { authMethodAfterSimUnlock: AuthenticationMethodModel = AuthenticationMethodModel.None, enableLockscreen: Boolean = true, ) { - assertWithMessage("Cannot enter PIN when not on the Bouncer scene!") - .that(getCurrentSceneInUi()) - .isEqualTo(Scenes.Bouncer) + val currentOverlays by collectLastValue(currentValue(transitionState).currentOverlays()) + assertWithMessage("Cannot enter PIN when Bouncer not showing!") + .that(currentOverlays) + .contains(Overlays.Bouncer) val authMethodViewModel by - collectLastValue(bouncerSceneContentViewModel.authMethodViewModel) + collectLastValue(bouncerOverlayContentViewModel.authMethodViewModel) assertWithMessage("Cannot enter PIN when not using a PIN authentication method!") .that(authMethodViewModel) .isInstanceOf(PinBouncerViewModel::class.java) @@ -643,7 +727,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { /** Emulates the dismissal of the IME (soft keyboard). */ private fun Kosmos.dismissIme() { - (currentValue(bouncerSceneContentViewModel.authMethodViewModel) + (currentValue(bouncerOverlayContentViewModel.authMethodViewModel) as? PasswordBouncerViewModel) ?.let { it.onImeDismissed() } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneBackInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneBackInteractorTest.kt index 9cc6c0fa801a..034585331af6 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneBackInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneBackInteractorTest.kt @@ -53,7 +53,7 @@ class SceneBackInteractorTest : SysuiTestCase() { @Test @EnableSceneContainer - fun navigateToQs_thenBouncer_thenBack_whileLocked() = + fun navigateToQs_thenBack_whileLocked() = testScope.runTest { sceneContainerStartable.start() @@ -61,8 +61,6 @@ class SceneBackInteractorTest : SysuiTestCase() { RouteNode(Scenes.Lockscreen, null), RouteNode(Scenes.Shade, Scenes.Lockscreen), RouteNode(Scenes.QuickSettings, Scenes.Shade), - RouteNode(Scenes.Bouncer, Scenes.QuickSettings), - RouteNode(Scenes.QuickSettings, Scenes.Shade), RouteNode(Scenes.Shade, Scenes.Lockscreen), RouteNode(Scenes.Lockscreen, null), ) @@ -70,57 +68,39 @@ class SceneBackInteractorTest : SysuiTestCase() { @Test @EnableSceneContainer - fun navigateToQs_thenBouncer_thenUnlock() = + fun navigateToQs_thenUnlock() = testScope.runTest { sceneContainerStartable.start() assertRoute( RouteNode(Scenes.Lockscreen, null), RouteNode(Scenes.Shade, Scenes.Lockscreen), - RouteNode(Scenes.QuickSettings, Scenes.Shade), - RouteNode(Scenes.Bouncer, Scenes.QuickSettings, unlockDevice = true), + RouteNode(Scenes.QuickSettings, Scenes.Shade, unlockDevice = true), RouteNode(Scenes.Gone, null), ) } @Test @EnableSceneContainer - fun navigateToQs_skippingShade_thenBouncer_thenBack_whileLocked() = + fun navigateToQs_skippingShade_thenBack_whileLocked() = testScope.runTest { sceneContainerStartable.start() assertRoute( RouteNode(Scenes.Lockscreen, null), RouteNode(Scenes.QuickSettings, Scenes.Lockscreen), - RouteNode(Scenes.Bouncer, Scenes.QuickSettings), - RouteNode(Scenes.QuickSettings, Scenes.Lockscreen), RouteNode(Scenes.Lockscreen, null), ) } @Test @EnableSceneContainer - fun navigateToBouncer_thenBack_whileLocked() = + fun navigateToQs_skippingShade_thenBack_thenShade_whileLocked() = testScope.runTest { sceneContainerStartable.start() assertRoute( RouteNode(Scenes.Lockscreen, null), - RouteNode(Scenes.Bouncer, Scenes.Lockscreen), - RouteNode(Scenes.Lockscreen, null), - ) - } - - @Test - @EnableSceneContainer - fun navigateToQs_skippingShade_thenBouncer_thenBack_thenShade_whileLocked() = - testScope.runTest { - sceneContainerStartable.start() - - assertRoute( - RouteNode(Scenes.Lockscreen, null), - RouteNode(Scenes.QuickSettings, Scenes.Lockscreen), - RouteNode(Scenes.Bouncer, Scenes.QuickSettings), RouteNode(Scenes.QuickSettings, Scenes.Lockscreen), RouteNode(Scenes.Lockscreen, null), RouteNode(Scenes.Shade, Scenes.Lockscreen), @@ -178,9 +158,8 @@ class SceneBackInteractorTest : SysuiTestCase() { testScope.runTest { underTest.onSceneChange(from = Scenes.Lockscreen, to = Scenes.Shade) underTest.onSceneChange(from = Scenes.Shade, to = Scenes.QuickSettings) - underTest.onSceneChange(from = Scenes.QuickSettings, to = Scenes.Bouncer) assertThat(underTest.backStack.value.asIterable().toList()) - .isEqualTo(listOf(Scenes.QuickSettings, Scenes.Shade, Scenes.Lockscreen)) + .isEqualTo(listOf(Scenes.Shade, Scenes.Lockscreen)) underTest.updateBackStack { stack -> // Reverse the stack, just to see if it can be done: @@ -188,7 +167,7 @@ class SceneBackInteractorTest : SysuiTestCase() { } assertThat(underTest.backStack.value.asIterable().toList()) - .isEqualTo(listOf(Scenes.Lockscreen, Scenes.Shade, Scenes.QuickSettings)) + .isEqualTo(listOf(Scenes.Lockscreen, Scenes.Shade)) } private suspend fun TestScope.assertRoute(vararg route: RouteNode) { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt index 23a0f6224fb7..559e363d8937 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt @@ -799,4 +799,88 @@ class SceneInteractorTest : SysuiTestCase() { verify(processor, never()).onSceneAboutToChange(any(), any()) assertThat(fakeSceneDataSource.freezeAndAnimateToCurrentStateCallCount).isEqualTo(0) } + + @Test + fun topmostContent_sceneChange_noOverlays() = + kosmos.runTest { + val topmostContent by collectLastValue(underTest.topmostContent) + + kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( + SuccessFingerprintAuthenticationStatus(0, true) + ) + + underTest.snapToScene(Scenes.Lockscreen, "reason") + + assertThat(topmostContent).isEqualTo(Scenes.Lockscreen) + + underTest.changeScene(Scenes.Gone, "reason") + + assertThat(topmostContent).isEqualTo(Scenes.Gone) + } + + @Test + fun topmostContent_sceneChange_withOverlay() = + kosmos.runTest { + kosmos.enableDualShade() + runCurrent() + + val topmostContent by collectLastValue(underTest.topmostContent) + + kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( + SuccessFingerprintAuthenticationStatus(0, true) + ) + + underTest.snapToScene(Scenes.Lockscreen, "reason") + underTest.showOverlay(Overlays.NotificationsShade, "reason") + + assertThat(topmostContent).isEqualTo(Overlays.NotificationsShade) + + underTest.changeScene(Scenes.Gone, "reason") + + assertThat(topmostContent).isEqualTo(Overlays.NotificationsShade) + } + + @Test + fun topmostContent_overlayChange_higherZOrder() = + kosmos.runTest { + kosmos.enableDualShade() + runCurrent() + + val topmostContent by collectLastValue(underTest.topmostContent) + + kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( + SuccessFingerprintAuthenticationStatus(0, true) + ) + + underTest.snapToScene(Scenes.Lockscreen, "reason") + underTest.showOverlay(Overlays.NotificationsShade, "reason") + + assertThat(topmostContent).isEqualTo(Overlays.NotificationsShade) + + underTest.showOverlay(Overlays.QuickSettingsShade, "reason") + + assertThat(topmostContent).isEqualTo(Overlays.QuickSettingsShade) + } + + @Test + fun topmostContent_overlayChange_lowerZOrder() = + kosmos.runTest { + kosmos.enableDualShade() + runCurrent() + + val topmostContent by collectLastValue(underTest.topmostContent) + + kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( + SuccessFingerprintAuthenticationStatus(0, true) + ) + + underTest.snapToScene(Scenes.Lockscreen, "reason") + underTest.showOverlay(Overlays.QuickSettingsShade, "reason") + + assertThat(topmostContent).isEqualTo(Overlays.QuickSettingsShade) + + underTest.showOverlay(Overlays.NotificationsShade, "reason") + + assertThat(topmostContent).isEqualTo(Overlays.QuickSettingsShade) + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt index ae77ac4cc327..9adf24f32c0c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt @@ -475,17 +475,18 @@ class SceneContainerStartableTest : SysuiTestCase() { @Test fun switchFromBouncerToGoneWhenDeviceUnlocked() = testScope.runTest { - val currentSceneKey by collectLastValue(sceneInteractor.currentScene) + val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) prepareState( authenticationMethod = AuthenticationMethodModel.Pin, isDeviceUnlocked = false, - initialSceneKey = Scenes.Bouncer, + initialSceneKey = Scenes.Lockscreen, + initialOverlays = setOf(Overlays.Bouncer), ) - assertThat(currentSceneKey).isEqualTo(Scenes.Bouncer) + assertThat(currentOverlays).contains(Overlays.Bouncer) underTest.start() updateFingerprintAuthStatus(isSuccess = true) - assertThat(currentSceneKey).isEqualTo(Scenes.Gone) + assertThat(currentOverlays).doesNotContain(Overlays.Bouncer) } @Test @@ -548,6 +549,7 @@ class SceneContainerStartableTest : SysuiTestCase() { fun switchFromBouncerToQuickSettingsWhenDeviceUnlocked_whenLeaveOpenShade() = testScope.runTest { val currentSceneKey by collectLastValue(sceneInteractor.currentScene) + val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) val backStack by collectLastValue(sceneBackInteractor.backStack) kosmos.sysuiStatusBarStateController.leaveOpen = true // leave shade open @@ -566,14 +568,16 @@ class SceneContainerStartableTest : SysuiTestCase() { runCurrent() assertThat(currentSceneKey).isEqualTo(Scenes.QuickSettings) - sceneInteractor.changeScene(Scenes.Bouncer, "switching to bouncer for test") - transitionState.value = ObservableTransitionState.Idle(Scenes.Bouncer) + sceneInteractor.showOverlay(Overlays.Bouncer, "showing bouncer for test") + transitionState.value = + ObservableTransitionState.Idle(Scenes.QuickSettings, setOf(Overlays.Bouncer)) runCurrent() - assertThat(currentSceneKey).isEqualTo(Scenes.Bouncer) + assertThat(currentOverlays).contains(Overlays.Bouncer) assertThat(backStack?.asIterable()?.last()).isEqualTo(Scenes.Lockscreen) updateFingerprintAuthStatus(isSuccess = true) assertThat(currentSceneKey).isEqualTo(Scenes.QuickSettings) + assertThat(currentOverlays).doesNotContain(Overlays.Bouncer) assertThat(backStack?.asIterable()?.last()).isEqualTo(Scenes.Gone) } @@ -581,6 +585,7 @@ class SceneContainerStartableTest : SysuiTestCase() { fun switchFromBouncerToGoneWhenDeviceUnlocked_whenDoNotLeaveOpenShade() = testScope.runTest { val currentSceneKey by collectLastValue(sceneInteractor.currentScene) + val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) kosmos.sysuiStatusBarStateController.leaveOpen = false // don't leave shade open val transitionState = @@ -598,13 +603,15 @@ class SceneContainerStartableTest : SysuiTestCase() { runCurrent() assertThat(currentSceneKey).isEqualTo(Scenes.QuickSettings) - sceneInteractor.changeScene(Scenes.Bouncer, "switching to bouncer for test") - transitionState.value = ObservableTransitionState.Idle(Scenes.Bouncer) + sceneInteractor.showOverlay(Overlays.Bouncer, "showing bouncer for test") + transitionState.value = + ObservableTransitionState.Idle(Scenes.QuickSettings, setOf(Overlays.Bouncer)) runCurrent() - assertThat(currentSceneKey).isEqualTo(Scenes.Bouncer) + assertThat(currentOverlays).contains(Overlays.Bouncer) updateFingerprintAuthStatus(isSuccess = true) assertThat(currentSceneKey).isEqualTo(Scenes.Gone) + assertThat(currentOverlays).doesNotContain(Overlays.Bouncer) } @Test @@ -664,14 +671,21 @@ class SceneContainerStartableTest : SysuiTestCase() { fun switchToGoneWhenDeviceIsUnlockedAndUserIsOnBouncerWithBypassDisabled() = testScope.runTest { val currentSceneKey by collectLastValue(sceneInteractor.currentScene) - prepareState(isBypassEnabled = false, initialSceneKey = Scenes.Bouncer) - assertThat(currentSceneKey).isEqualTo(Scenes.Bouncer) + val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) + prepareState( + isBypassEnabled = false, + initialSceneKey = Scenes.Lockscreen, + initialOverlays = setOf(Overlays.Bouncer), + ) + assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen) + assertThat(currentOverlays).contains(Overlays.Bouncer) underTest.start() // Authenticate using a passive auth method like face auth while bypass is disabled. faceAuthRepository.isAuthenticated.value = true assertThat(currentSceneKey).isEqualTo(Scenes.Gone) + assertThat(currentOverlays).doesNotContain(Overlays.Bouncer) } @Test @@ -1192,14 +1206,7 @@ class SceneContainerStartableTest : SysuiTestCase() { runCurrent() clearInvocations(sysUiState) - listOf( - Scenes.Gone, - Scenes.Lockscreen, - Scenes.Bouncer, - Scenes.Gone, - Scenes.Shade, - Scenes.QuickSettings, - ) + listOf(Scenes.Gone, Scenes.Lockscreen, Scenes.Gone, Scenes.Shade, Scenes.QuickSettings) .forEachIndexed { index, sceneKey -> if (sceneKey == Scenes.Gone) { updateFingerprintAuthStatus(isSuccess = true) @@ -1385,19 +1392,13 @@ class SceneContainerStartableTest : SysuiTestCase() { verify(falsingCollector, never()).onSuccessfulUnlock() // Move around scenes without unlocking. - listOf( - Scenes.Shade, - Scenes.QuickSettings, - Scenes.Shade, - Scenes.Lockscreen, - Scenes.Bouncer, - ) - .forEach { sceneKey -> - sceneInteractor.changeScene(sceneKey, "reason") - transitionStateFlow.value = ObservableTransitionState.Idle(sceneKey) - runCurrent() - verify(falsingCollector, never()).onSuccessfulUnlock() - } + listOf(Scenes.Shade, Scenes.QuickSettings, Scenes.Shade, Scenes.Lockscreen).forEach { + sceneKey -> + sceneInteractor.changeScene(sceneKey, "reason") + transitionStateFlow.value = ObservableTransitionState.Idle(sceneKey) + runCurrent() + verify(falsingCollector, never()).onSuccessfulUnlock() + } // Changing to the Gone scene should report a successful unlock. kosmos.authenticationInteractor.authenticate(FakeAuthenticationRepository.DEFAULT_PIN) @@ -1444,19 +1445,13 @@ class SceneContainerStartableTest : SysuiTestCase() { verify(falsingCollector, times(1)).onSuccessfulUnlock() // Move around scenes without unlocking. - listOf( - Scenes.Shade, - Scenes.QuickSettings, - Scenes.Shade, - Scenes.Lockscreen, - Scenes.Bouncer, - ) - .forEach { sceneKey -> - sceneInteractor.changeScene(sceneKey, "reason") - transitionStateFlow.value = ObservableTransitionState.Idle(sceneKey) - runCurrent() - verify(falsingCollector, times(1)).onSuccessfulUnlock() - } + listOf(Scenes.Shade, Scenes.QuickSettings, Scenes.Shade, Scenes.Lockscreen).forEach { + sceneKey -> + sceneInteractor.changeScene(sceneKey, "reason") + transitionStateFlow.value = ObservableTransitionState.Idle(sceneKey) + runCurrent() + verify(falsingCollector, times(1)).onSuccessfulUnlock() + } kosmos.authenticationInteractor.authenticate(FakeAuthenticationRepository.DEFAULT_PIN) runCurrent() @@ -1565,13 +1560,13 @@ class SceneContainerStartableTest : SysuiTestCase() { runCurrent() verify(falsingCollector).onBouncerHidden() - sceneInteractor.changeScene(Scenes.Bouncer, "reason") + sceneInteractor.showOverlay(Overlays.Bouncer, "reason") runCurrent() verify(falsingCollector).onBouncerShown() updateFingerprintAuthStatus(isSuccess = true) runCurrent() - sceneInteractor.changeScene(Scenes.Gone, "reason") + sceneInteractor.hideOverlay(Overlays.Bouncer, "reason") runCurrent() verify(falsingCollector, times(2)).onBouncerHidden() } @@ -1579,7 +1574,7 @@ class SceneContainerStartableTest : SysuiTestCase() { @Test fun switchesToBouncer_whenSimBecomesLocked() = testScope.runTest { - val currentSceneKey by collectLastValue(sceneInteractor.currentScene) + val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) prepareState( initialSceneKey = Scenes.Lockscreen, @@ -1592,17 +1587,18 @@ class SceneContainerStartableTest : SysuiTestCase() { kosmos.fakeMobileConnectionsRepository.isAnySimSecure.value = true runCurrent() - assertThat(currentSceneKey).isEqualTo(Scenes.Bouncer) + assertThat(currentOverlays).contains(Overlays.Bouncer) } @Test fun switchesToLockscreen_whenSimBecomesUnlocked() = testScope.runTest { kosmos.fakeMobileConnectionsRepository.isAnySimSecure.value = true - val currentSceneKey by collectLastValue(sceneInteractor.currentScene) + val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) prepareState( - initialSceneKey = Scenes.Bouncer, + initialSceneKey = Scenes.Lockscreen, + initialOverlays = setOf(Overlays.Bouncer), authenticationMethod = AuthenticationMethodModel.Pin, isDeviceUnlocked = false, ) @@ -1611,7 +1607,7 @@ class SceneContainerStartableTest : SysuiTestCase() { kosmos.fakeMobileConnectionsRepository.isAnySimSecure.value = false runCurrent() - assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen) + assertThat(currentOverlays).doesNotContain(Overlays.Bouncer) } @Test @@ -1700,7 +1696,7 @@ class SceneContainerStartableTest : SysuiTestCase() { assertThat(currentScene).isEqualTo(Scenes.Lockscreen) verify(notificationShadeWindowController).setKeyguardShowing(true) - emulateSceneTransition(transitionStateFlow, Scenes.Bouncer) + emulateOverlayTransition(transitionStateFlow, Overlays.Bouncer) verify(notificationShadeWindowController, times(1)).setKeyguardShowing(true) emulateSceneTransition(transitionStateFlow, Scenes.Lockscreen) @@ -1748,9 +1744,9 @@ class SceneContainerStartableTest : SysuiTestCase() { verify(centralSurfaces).setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true) clearInvocations(centralSurfaces) - emulateSceneTransition( + emulateOverlayTransition( transitionStateFlow = transitionStateFlow, - toScene = Scenes.Bouncer, + toOverlay = Overlays.Bouncer, verifyBeforeTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, @@ -1839,9 +1835,9 @@ class SceneContainerStartableTest : SysuiTestCase() { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) clearInvocations(centralSurfaces) - emulateSceneTransition( + emulateOverlayTransition( transitionStateFlow = transitionStateFlow, - toScene = Scenes.Bouncer, + toOverlay = Overlays.Bouncer, verifyBeforeTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, @@ -1926,9 +1922,9 @@ class SceneContainerStartableTest : SysuiTestCase() { assertThat(currentDesiredOverlays).isEmpty() clearInvocations(centralSurfaces) - emulateSceneTransition( + emulateOverlayTransition( transitionStateFlow = transitionStateFlow, - toScene = Scenes.Bouncer, + toOverlay = Overlays.Bouncer, verifyBeforeTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, @@ -2019,9 +2015,9 @@ class SceneContainerStartableTest : SysuiTestCase() { assertThat(currentDesiredOverlays).isEmpty() clearInvocations(centralSurfaces) - emulateSceneTransition( + emulateOverlayTransition( transitionStateFlow = transitionStateFlow, - toScene = Scenes.Bouncer, + toOverlay = Overlays.Bouncer, verifyBeforeTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, @@ -2098,32 +2094,42 @@ class SceneContainerStartableTest : SysuiTestCase() { fun respondToFalsingDetections() = testScope.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene) + val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) val transitionStateFlow = prepareState() underTest.start() runCurrent() - emulateSceneTransition(transitionStateFlow, toScene = Scenes.Bouncer) - assertThat(currentScene).isNotEqualTo(Scenes.Lockscreen) + emulateOverlayTransition( + transitionStateFlow = transitionStateFlow, + toOverlay = Overlays.Bouncer, + ) + assertThat(currentScene).isEqualTo(Scenes.Lockscreen) + assertThat(currentOverlays).contains(Overlays.Bouncer) kosmos.falsingManager.sendFalsingBelief() + runCurrent() assertThat(currentScene).isEqualTo(Scenes.Lockscreen) + assertThat(currentOverlays).isEmpty() } @Test fun handleBouncerOverscroll() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene) + val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) val transitionStateFlow = prepareState() underTest.start() runCurrent() - emulateSceneTransition(transitionStateFlow, toScene = Scenes.Bouncer) - assertThat(currentScene).isEqualTo(Scenes.Bouncer) + emulateOverlayTransition( + transitionStateFlow = transitionStateFlow, + toOverlay = Overlays.Bouncer, + ) + assertThat(currentOverlays).contains(Overlays.Bouncer) transitionStateFlow.value = - ObservableTransitionState.Transition( - fromScene = Scenes.Bouncer, + ObservableTransitionState.Transition.hideOverlay( + overlay = Overlays.Bouncer, toScene = Scenes.Lockscreen, - currentScene = flowOf(Scenes.Bouncer), + currentOverlays = flowOf(setOf(Overlays.Bouncer)), progress = flowOf(-0.4f), isInitiatedByUserInput = true, isUserInputOngoing = flowOf(true), @@ -2137,16 +2143,17 @@ class SceneContainerStartableTest : SysuiTestCase() { fun switchToLockscreen_whenShadeBecomesNotTouchable() = testScope.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene) + val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) val isShadeTouchable by collectLastValue(kosmos.shadeInteractor.isShadeTouchable) val transitionStateFlow = prepareState() underTest.start() assertThat(currentScene).isEqualTo(Scenes.Lockscreen) // Flung to bouncer, 90% of the way there: transitionStateFlow.value = - ObservableTransitionState.Transition( + ObservableTransitionState.Transition.showOverlay( + overlay = Overlays.Bouncer, fromScene = Scenes.Lockscreen, - toScene = Scenes.Bouncer, - currentScene = flowOf(Scenes.Bouncer), + currentOverlays = flowOf(setOf(Overlays.Bouncer)), progress = flowOf(0.9f), isInitiatedByUserInput = true, isUserInputOngoing = flowOf(false), @@ -2159,22 +2166,27 @@ class SceneContainerStartableTest : SysuiTestCase() { assertThat(isShadeTouchable).isFalse() assertThat(currentScene).isEqualTo(Scenes.Lockscreen) + assertThat(currentOverlays).doesNotContain(Overlays.Bouncer) } @Test fun switchToGone_extendUnlock() = testScope.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene) + val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) prepareState( - initialSceneKey = Scenes.Bouncer, + initialSceneKey = Scenes.Lockscreen, + initialOverlays = setOf(Overlays.Bouncer), authenticationMethod = AuthenticationMethodModel.Pin, ) - assertThat(currentScene).isEqualTo(Scenes.Bouncer) + assertThat(currentScene).isEqualTo(Scenes.Lockscreen) + assertThat(currentOverlays).contains(Overlays.Bouncer) underTest.start() fakeTrustRepository.setCurrentUserTrusted(true) assertThat(currentScene).isEqualTo(Scenes.Gone) + assertThat(currentOverlays).doesNotContain(Overlays.Bouncer) assertThat(uiEventLoggerFake[0].eventId) .isEqualTo(BouncerUiEvent.BOUNCER_DISMISS_EXTENDED_ACCESS.id) assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1) @@ -2285,12 +2297,15 @@ class SceneContainerStartableTest : SysuiTestCase() { fun notifyKeyguardDismissCallbacks_whenUnlockingFromBouncer_onDismissSucceeded() = testScope.runTest { val currentSceneKey by collectLastValue(sceneInteractor.currentScene) + val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) prepareState( authenticationMethod = AuthenticationMethodModel.Pin, isDeviceUnlocked = false, - initialSceneKey = Scenes.Bouncer, + initialSceneKey = Scenes.Lockscreen, + initialOverlays = setOf(Overlays.Bouncer), ) - assertThat(currentSceneKey).isEqualTo(Scenes.Bouncer) + assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen) + assertThat(currentOverlays).contains(Overlays.Bouncer) underTest.start() // run all pending dismiss succeeded/cancelled calls from setup: @@ -2311,7 +2326,7 @@ class SceneContainerStartableTest : SysuiTestCase() { fun notifyKeyguardDismissCallbacks_whenLeavingBouncer_onDismissCancelled() = testScope.runTest { val isUnlocked by collectLastValue(kosmos.deviceEntryInteractor.isUnlocked) - val currentScene by collectLastValue(sceneInteractor.currentScene) + val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) prepareState() underTest.start() runCurrent() @@ -2323,13 +2338,13 @@ class SceneContainerStartableTest : SysuiTestCase() { kosmos.dismissCallbackRegistry.addCallback(dismissCallback) // Switch to bouncer: - sceneInteractor.changeScene(Scenes.Bouncer, "") - assertThat(currentScene).isEqualTo(Scenes.Bouncer) + sceneInteractor.showOverlay(Overlays.Bouncer, "") + assertThat(currentOverlays).contains(Overlays.Bouncer) runCurrent() // Return to lockscreen when isUnlocked=false: - sceneInteractor.changeScene(Scenes.Lockscreen, "") - assertThat(currentScene).isEqualTo(Scenes.Lockscreen) + sceneInteractor.hideOverlay(Overlays.Bouncer, "") + assertThat(currentOverlays).doesNotContain(Overlays.Bouncer) assertThat(isUnlocked).isFalse() runCurrent() kosmos.fakeExecutor.runAllReady() @@ -2844,6 +2859,7 @@ class SceneContainerStartableTest : SysuiTestCase() { isDeviceUnlocked: Boolean = false, isBypassEnabled: Boolean = false, initialSceneKey: SceneKey? = null, + initialOverlays: Set<OverlayKey> = emptySet(), authenticationMethod: AuthenticationMethodModel? = null, isLockscreenEnabled: Boolean = true, startsAwake: Boolean = true, @@ -2870,7 +2886,7 @@ class SceneContainerStartableTest : SysuiTestCase() { runCurrent() val transitionStateFlow = MutableStateFlow<ObservableTransitionState>( - ObservableTransitionState.Idle(Scenes.Lockscreen) + ObservableTransitionState.Idle(Scenes.Lockscreen, initialOverlays) ) sceneInteractor.setTransitionState(transitionStateFlow) initialSceneKey?.let { @@ -2881,9 +2897,12 @@ class SceneContainerStartableTest : SysuiTestCase() { runCurrent() } - transitionStateFlow.value = ObservableTransitionState.Idle(it) + transitionStateFlow.value = ObservableTransitionState.Idle(it, initialOverlays) sceneInteractor.changeScene(it, "prepareState, initialSceneKey isn't null") } + for (overlay in initialOverlays) { + sceneInteractor.showOverlay(overlay, "prepareState, initialOverlays isn't empty") + } if (startsAwake) { powerInteractor.setAwakeForTest() } else { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/ScrimStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/ScrimStartableTest.kt index 572bc4dc4247..46f497054ab9 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/ScrimStartableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/ScrimStartableTest.kt @@ -18,6 +18,7 @@ package com.android.systemui.scene.domain.startable import androidx.test.filters.SmallTest import com.android.compose.animation.scene.ObservableTransitionState +import com.android.compose.animation.scene.OverlayKey import com.android.compose.animation.scene.SceneKey import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository @@ -33,6 +34,7 @@ import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticati import com.android.systemui.kosmos.testScope import com.android.systemui.scene.data.repository.setSceneTransition import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.settings.brightness.domain.interactor.brightnessMirrorShowingInteractor import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor @@ -90,34 +92,28 @@ class ScrimStartableTest : SysuiTestCase() { TestSpec( id = 2, expectedState = ScrimState.BOUNCER, - Preconditions( - isOnKeyguard = true, - isCurrentSceneBouncer = true, - ), + Preconditions(isOnKeyguard = true, isIdleOnBouncer = true), ), TestSpec( id = 3, expectedState = ScrimState.BOUNCER_SCRIMMED, Preconditions( isOnKeyguard = true, - isCurrentSceneBouncer = true, + isIdleOnBouncer = true, isBouncerScrimmingNeeded = true, ), ), TestSpec( id = 4, expectedState = ScrimState.BRIGHTNESS_MIRROR, - Preconditions( - isOnKeyguard = true, - isBrightnessMirrorVisible = true, - ), + Preconditions(isOnKeyguard = true, isBrightnessMirrorVisible = true), ), TestSpec( id = 5, expectedState = ScrimState.BRIGHTNESS_MIRROR, Preconditions( isOnKeyguard = true, - isCurrentSceneBouncer = true, + isIdleOnBouncer = true, isBiometricWakeAndUnlock = true, isBrightnessMirrorVisible = true, ), @@ -125,42 +121,27 @@ class ScrimStartableTest : SysuiTestCase() { TestSpec( id = 6, expectedState = ScrimState.SHADE_LOCKED, - Preconditions( - isOnKeyguard = true, - isCurrentSceneShade = true, - ), + Preconditions(isOnKeyguard = true, isCurrentSceneShade = true), ), TestSpec( id = 7, expectedState = ScrimState.PULSING, - Preconditions( - isOnKeyguard = true, - isDozing = true, - isPulsing = true, - ), + Preconditions(isOnKeyguard = true, isDozing = true, isPulsing = true), ), TestSpec( id = 8, expectedState = ScrimState.OFF, - Preconditions( - isOnKeyguard = true, - hasPendingScreenOffCallback = true, - ), + Preconditions(isOnKeyguard = true, hasPendingScreenOffCallback = true), ), TestSpec( id = 9, expectedState = ScrimState.AOD, - Preconditions( - isOnKeyguard = true, - isDozing = true, - ), + Preconditions(isOnKeyguard = true, isDozing = true), ), TestSpec( id = 10, expectedState = ScrimState.GLANCEABLE_HUB, - Preconditions( - isIdleOnCommunal = true, - ), + Preconditions(isIdleOnCommunal = true), ), TestSpec( id = 11, @@ -170,39 +151,23 @@ class ScrimStartableTest : SysuiTestCase() { TestSpec( id = 12, expectedState = ScrimState.UNLOCKED, - Preconditions( - isDeviceEntered = true, - ), + Preconditions(isDeviceEntered = true), ), TestSpec( id = 13, expectedState = ScrimState.UNLOCKED, - Preconditions( - isOnKeyguard = true, - isBiometricWakeAndUnlock = true, - ), - ), - TestSpec( - id = 14, - expectedState = ScrimState.KEYGUARD, - Preconditions(), + Preconditions(isOnKeyguard = true, isBiometricWakeAndUnlock = true), ), + TestSpec(id = 14, expectedState = ScrimState.KEYGUARD, Preconditions()), TestSpec( id = 15, expectedState = ScrimState.DREAMING, - Preconditions( - isOnKeyguard = true, - isOccluded = true, - isDreaming = true, - ), + Preconditions(isOnKeyguard = true, isOccluded = true, isDreaming = true), ), TestSpec( id = 16, expectedState = ScrimState.UNLOCKED, - Preconditions( - isOnKeyguard = true, - isOccluded = true, - ), + Preconditions(isOnKeyguard = true, isOccluded = true), ), ) } @@ -253,9 +218,7 @@ class ScrimStartableTest : SysuiTestCase() { } /** Sets up the state to match what's specified in the given [preconditions]. */ - private fun TestScope.setUpWith( - preconditions: Preconditions, - ) { + private fun TestScope.setUpWith(preconditions: Preconditions) { kosmos.fakeKeyguardBouncerRepository.setAlternateVisible( preconditions.isAlternateBouncerVisible ) @@ -272,17 +235,12 @@ class ScrimStartableTest : SysuiTestCase() { when { preconditions.isTransitioningToShade -> - whenTransitioning( - from = Scenes.Lockscreen, - to = Scenes.Shade, - ) + whenTransitioning(from = Scenes.Lockscreen, to = Scenes.Shade) preconditions.isTransitioningAwayFromKeyguard -> - whenTransitioning( - from = Scenes.Lockscreen, - to = Scenes.Gone, - ) + whenTransitioning(from = Scenes.Lockscreen, to = Scenes.Gone) preconditions.isCurrentSceneShade -> whenIdle(on = Scenes.Shade) - preconditions.isCurrentSceneBouncer -> whenIdle(on = Scenes.Bouncer) + preconditions.isIdleOnBouncer -> + whenIdle(on = Scenes.Lockscreen, overlays = setOf(Overlays.Bouncer)) preconditions.isIdleOnCommunal -> whenIdle(on = Scenes.Communal) } @@ -323,9 +281,12 @@ class ScrimStartableTest : SysuiTestCase() { } /** Sets up an idle state on the given [on] scene. */ - private fun whenIdle(on: SceneKey) { - kosmos.setSceneTransition(ObservableTransitionState.Idle(on)) + private fun whenIdle(on: SceneKey, overlays: Set<OverlayKey> = emptySet()) { + kosmos.setSceneTransition(ObservableTransitionState.Idle(on, overlays)) kosmos.sceneInteractor.changeScene(on, "") + for (overlay in overlays) { + kosmos.sceneInteractor.showOverlay(overlay, "") + } } /** Sets up a transitioning state between the [given] and [to] scenes. */ @@ -351,7 +312,7 @@ class ScrimStartableTest : SysuiTestCase() { /** Whether any non-shade nor QS scene is transitioning to a shade or QS scene. */ val isTransitioningToShade: Boolean = false, val isOccluded: Boolean = false, - val isCurrentSceneBouncer: Boolean = false, + val isIdleOnBouncer: Boolean = false, val isBiometricWakeAndUnlock: Boolean = false, /** Whether there's an active transition from lockscreen or bouncer to gone. */ val isTransitioningAwayFromKeyguard: Boolean = false, @@ -388,7 +349,7 @@ class ScrimStartableTest : SysuiTestCase() { assertWithMessage( "isCurrentSceneBouncer cannot be true without isOnKeyguard also being true" ) - .that(!isCurrentSceneBouncer || isOnKeyguard) + .that(!isIdleOnBouncer || isOnKeyguard) .isTrue() assertWithMessage( @@ -400,13 +361,13 @@ class ScrimStartableTest : SysuiTestCase() { assertWithMessage( "isCurrentSceneBouncer cannot be true at the same time as isCurrentSceneShade" ) - .that(!isCurrentSceneBouncer || !isCurrentSceneShade) + .that(!isIdleOnBouncer || !isCurrentSceneShade) .isTrue() assertWithMessage( "isCurrentSceneBouncer cannot be true at the same time as isIdleOnCommunal" ) - .that(!isCurrentSceneBouncer || !isIdleOnCommunal) + .that(!isIdleOnBouncer || !isIdleOnCommunal) .isTrue() assertWithMessage( @@ -422,7 +383,7 @@ class ScrimStartableTest : SysuiTestCase() { assertWithMessage( "isDeviceEntered cannot be true at the same time as isCurrentSceneBouncer" ) - .that(!isDeviceEntered || !isCurrentSceneBouncer) + .that(!isDeviceEntered || !isIdleOnBouncer) .isTrue() assertWithMessage( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/StatusBarStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/StatusBarStartableTest.kt index 99146d8b1267..eebbc843206b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/StatusBarStartableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/StatusBarStartableTest.kt @@ -21,6 +21,7 @@ import android.provider.DeviceConfig import android.view.WindowManagerPolicyConstants import androidx.test.filters.SmallTest import com.android.compose.animation.scene.ObservableTransitionState +import com.android.compose.animation.scene.OverlayKey import com.android.compose.animation.scene.SceneKey import com.android.internal.config.sysui.SystemUiDeviceConfigFlags import com.android.internal.statusbar.statusBarService @@ -40,6 +41,7 @@ import com.android.systemui.power.shared.model.WakeSleepReason import com.android.systemui.power.shared.model.WakefulnessState import com.android.systemui.scene.data.repository.setSceneTransition import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor import com.android.systemui.testKosmos @@ -246,16 +248,14 @@ class StatusBarStartableTest : SysuiTestCase() { } /** Sets up the state to match what's specified in the given [preconditions]. */ - private fun TestScope.setUpWith( - preconditions: Preconditions, - ) { + private fun TestScope.setUpWith(preconditions: Preconditions) { if (!preconditions.isKeyguardShowing) { kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( SuccessFingerprintAuthenticationStatus(0, true) ) } if (preconditions.isForceHideHomeAndRecents) { - whenIdle(Scenes.Bouncer) + whenIdle(Scenes.Lockscreen, setOf(Overlays.Bouncer)) } else if (preconditions.isKeyguardShowing) { whenIdle(Scenes.Lockscreen) } else { @@ -308,9 +308,12 @@ class StatusBarStartableTest : SysuiTestCase() { } /** Sets up an idle state on the given [on] scene. */ - private fun whenIdle(on: SceneKey) { - kosmos.setSceneTransition(ObservableTransitionState.Idle(on)) + private fun whenIdle(on: SceneKey, overlays: Set<OverlayKey> = emptySet()) { + kosmos.setSceneTransition(ObservableTransitionState.Idle(on, overlays)) kosmos.sceneInteractor.changeScene(on, "") + for (overlay in overlays) { + kosmos.sceneInteractor.showOverlay(overlay, "") + } } data class Preconditions( @@ -350,11 +353,7 @@ class StatusBarStartableTest : SysuiTestCase() { } } - data class TestSpec( - val id: Int, - val expectedFlags: Int, - val preconditions: Preconditions, - ) { + data class TestSpec(val id: Int, val expectedFlags: Int, val preconditions: Preconditions) { override fun toString(): String { return "id=$id, expected=$expectedFlags, preconditions=$preconditions" } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegatorTest.kt index 2720c5722376..c9dab3479e49 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegatorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegatorTest.kt @@ -55,9 +55,9 @@ class SceneDataSourceDelegatorTest : SysuiTestCase() { testScope.runTest { val currentScene by collectLastValue(underTest.currentScene) underTest.setDelegate(null) - assertThat(currentScene).isNotEqualTo(Scenes.Bouncer) + assertThat(currentScene).isNotEqualTo(Scenes.Gone) - underTest.changeScene(toScene = Scenes.Bouncer) + underTest.changeScene(toScene = Scenes.Gone) assertThat(currentScene).isEqualTo(initialSceneKey) } @@ -73,11 +73,11 @@ class SceneDataSourceDelegatorTest : SysuiTestCase() { fun currentScene_withDelegate_changesScenes() = testScope.runTest { val currentScene by collectLastValue(underTest.currentScene) - assertThat(currentScene).isNotEqualTo(Scenes.Bouncer) + assertThat(currentScene).isNotEqualTo(Scenes.Gone) - underTest.changeScene(toScene = Scenes.Bouncer) + underTest.changeScene(toScene = Scenes.Gone) - assertThat(currentScene).isEqualTo(Scenes.Bouncer) + assertThat(currentScene).isEqualTo(Scenes.Gone) } @Test @@ -85,8 +85,8 @@ class SceneDataSourceDelegatorTest : SysuiTestCase() { testScope.runTest { val currentScene by collectLastValue(underTest.currentScene) - fakeSceneDataSource.changeScene(toScene = Scenes.Bouncer) + fakeSceneDataSource.changeScene(toScene = Scenes.Gone) - assertThat(currentScene).isEqualTo(Scenes.Bouncer) + assertThat(currentScene).isEqualTo(Scenes.Gone) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/view/SceneJankMonitorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/view/SceneJankMonitorTest.kt index 984f8fd13cde..9b00e217466c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/view/SceneJankMonitorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/view/SceneJankMonitorTest.kt @@ -31,6 +31,7 @@ import com.android.systemui.kosmos.runCurrent import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.testScope import com.android.systemui.lifecycle.activateIn +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat @@ -158,22 +159,22 @@ class SceneJankMonitorTest : SysuiTestCase() { underTest.onTransitionStart( view = mock(), from = Scenes.Lockscreen, - to = Scenes.Bouncer, + to = Overlays.Bouncer, cuj = null, ) verifyCujCounts(beginAppearCount = 1) - underTest.onTransitionEnd(from = Scenes.Lockscreen, to = Scenes.Bouncer, cuj = null) + underTest.onTransitionEnd(from = Scenes.Lockscreen, to = Overlays.Bouncer, cuj = null) verifyCujCounts(beginAppearCount = 1, endAppearCount = 1) // Bouncer disappear CUJ but it doesn't log because the device isn't unlocked. underTest.onTransitionStart( view = mock(), - from = Scenes.Bouncer, + from = Overlays.Bouncer, to = Scenes.Lockscreen, cuj = null, ) verifyCujCounts(beginAppearCount = 1, endAppearCount = 1) - underTest.onTransitionEnd(from = Scenes.Bouncer, to = Scenes.Lockscreen, cuj = null) + underTest.onTransitionEnd(from = Overlays.Bouncer, to = Scenes.Lockscreen, cuj = null) verifyCujCounts(beginAppearCount = 1, endAppearCount = 1) if (!testUnlockedDisappearance) { @@ -190,12 +191,12 @@ class SceneJankMonitorTest : SysuiTestCase() { // Bouncer disappear CUJ and it doeslog because the device is unlocked. underTest.onTransitionStart( view = mock(), - from = Scenes.Bouncer, + from = Overlays.Bouncer, to = Scenes.Gone, cuj = null, ) verifyCujCounts(beginAppearCount = 1, endAppearCount = 1, beginDisappearCount = 1) - underTest.onTransitionEnd(from = Scenes.Bouncer, to = Scenes.Gone, cuj = null) + underTest.onTransitionEnd(from = Overlays.Bouncer, to = Scenes.Gone, cuj = null) verifyCujCounts( beginAppearCount = 1, endAppearCount = 1, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerHapticsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerHapticsViewModelTest.kt index 7330b517ba67..f04f0a3577d8 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerHapticsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerHapticsViewModelTest.kt @@ -164,7 +164,7 @@ class SceneContainerHapticsViewModelTest : SysuiTestCase() { kosmos.enableDualShade() // GIVEN an invalid scene transition to play haptics val invalidTransition = - createTransitionState(from = Scenes.Bouncer, to = Overlays.NotificationsShade) + createTransitionState(from = Scenes.QuickSettings, to = Scenes.Gone) // WHEN the transition occurs sceneInteractor.setTransitionState(MutableStateFlow(invalidTransition)) @@ -199,7 +199,7 @@ class SceneContainerHapticsViewModelTest : SysuiTestCase() { kosmos.enableDualShade() // GIVEN an invalid scene transition to play haptics val invalidTransition = - createTransitionState(from = Scenes.Bouncer, to = Overlays.NotificationsShade) + createTransitionState(from = Scenes.QuickSettings, to = Scenes.Gone) // WHEN the transition occurs sceneInteractor.setTransitionState(MutableStateFlow(invalidTransition)) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImplTest.kt index 1859e250c6ad..d7b8aff97376 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImplTest.kt @@ -78,7 +78,7 @@ class PanelExpansionInteractorImplTest : SysuiTestCase() { changeScene(Scenes.Lockscreen) { assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) - changeScene(Scenes.Bouncer) { assertThat(panelExpansion).isEqualTo(1f) } + showOverlay(Overlays.Bouncer) { assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) changeScene(Scenes.Shade) { assertThat(panelExpansion).isEqualTo(1f) } @@ -133,7 +133,7 @@ class PanelExpansionInteractorImplTest : SysuiTestCase() { changeScene(Scenes.Lockscreen) { assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) - changeScene(Scenes.Bouncer) { assertThat(panelExpansion).isEqualTo(1f) } + showOverlay(Overlays.Bouncer) { assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) showOverlay(Overlays.NotificationsShade) { assertThat(panelExpansion).isEqualTo(1f) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModelTest.kt index 2038adcfc21f..d5537a611f13 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModelTest.kt @@ -32,6 +32,7 @@ import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.kosmos.testScope import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat @@ -125,7 +126,7 @@ class NotificationShadeWindowModelTest : SysuiTestCase() { to = KeyguardState.DREAMING, value = 1f, transitionState = TransitionState.FINISHED, - ), + ) ) assertThat(isKeyguardOccluded).isTrue() @@ -154,7 +155,7 @@ class NotificationShadeWindowModelTest : SysuiTestCase() { to = KeyguardState.OCCLUDED, value = 1f, transitionState = TransitionState.FINISHED, - ), + ) ) assertThat(isKeyguardOccluded).isTrue() } @@ -173,7 +174,8 @@ class NotificationShadeWindowModelTest : SysuiTestCase() { runCurrent() assertThat(bouncerShowing).isFalse() - transitionState.value = ObservableTransitionState.Idle(Scenes.Bouncer) + transitionState.value = + ObservableTransitionState.Idle(Scenes.Lockscreen, setOf(Overlays.Bouncer)) runCurrent() assertThat(bouncerShowing).isTrue() } @@ -204,7 +206,7 @@ class NotificationShadeWindowModelTest : SysuiTestCase() { val transitionState = MutableStateFlow<ObservableTransitionState>( - ObservableTransitionState.Idle(Scenes.Bouncer) + ObservableTransitionState.Idle(Scenes.Lockscreen, setOf(Overlays.Bouncer)) ) kosmos.sceneInteractor.setTransitionState(transitionState) runCurrent() @@ -219,7 +221,8 @@ class NotificationShadeWindowModelTest : SysuiTestCase() { AuthenticationMethodModel.Password ) // go back to bouncer - transitionState.value = ObservableTransitionState.Idle(Scenes.Bouncer) + transitionState.value = + ObservableTransitionState.Idle(Scenes.Lockscreen, setOf(Overlays.Bouncer)) runCurrent() assertThat(bouncerRequiresIme).isTrue() } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt index 83361dad9ff0..c10fd5e18fc5 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt @@ -19,6 +19,7 @@ package com.android.systemui.statusbar import android.os.IBinder import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags +import android.platform.test.annotations.RequiresFlagsDisabled import android.testing.TestableLooper.RunWithLooper import android.view.Choreographer import android.view.View @@ -34,7 +35,6 @@ import com.android.systemui.kosmos.testScope import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.res.R import com.android.systemui.shade.ShadeExpansionChangeEvent -import com.android.systemui.shared.Flags as SharedFlags import com.android.systemui.statusbar.phone.BiometricUnlockController import com.android.systemui.statusbar.phone.DozeParameters import com.android.systemui.statusbar.phone.ScrimController @@ -44,11 +44,10 @@ import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController import com.android.systemui.testKosmos import com.android.systemui.util.WallpaperController import com.android.systemui.util.mockito.eq +import com.android.systemui.wallpapers.domain.interactor.WallpaperInteractor import com.android.systemui.window.domain.interactor.WindowRootViewBlurInteractor import com.android.wm.shell.appzoomout.AppZoomOut import com.google.common.truth.Truth.assertThat -import java.util.Optional -import java.util.function.Consumer import org.junit.Before import org.junit.Rule import org.junit.Test @@ -69,6 +68,8 @@ import org.mockito.Mockito.reset import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.junit.MockitoJUnit +import java.util.Optional +import java.util.function.Consumer @RunWith(AndroidJUnit4::class) @RunWithLooper @@ -76,6 +77,7 @@ import org.mockito.junit.MockitoJUnit class NotificationShadeDepthControllerTest : SysuiTestCase() { private val kosmos = testKosmos() + private val applicationScope = kosmos.testScope.backgroundScope @Mock private lateinit var windowRootViewBlurInteractor: WindowRootViewBlurInteractor @Mock private lateinit var statusBarStateController: StatusBarStateController @Mock private lateinit var blurUtils: BlurUtils @@ -84,6 +86,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { @Mock private lateinit var keyguardInteractor: KeyguardInteractor @Mock private lateinit var choreographer: Choreographer @Mock private lateinit var wallpaperController: WallpaperController + @Mock private lateinit var wallpaperInteractor: WallpaperInteractor @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController @Mock private lateinit var dumpManager: DumpManager @Mock private lateinit var appZoomOutOptional: Optional<AppZoomOut> @@ -128,12 +131,14 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { keyguardInteractor, choreographer, wallpaperController, + wallpaperInteractor, notificationShadeWindowController, dozeParameters, context, ResourcesSplitShadeStateController(), windowRootViewBlurInteractor, appZoomOutOptional, + applicationScope, dumpManager, configurationController, ) @@ -310,19 +315,21 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { } @Test - @DisableFlags(SharedFlags.FLAG_AMBIENT_AOD) - fun onDozeAmountChanged_appliesBlur() { + fun onDozeAmountChanged_doesNotApplyBlurWithAmbientAod() { + notificationShadeDepthController.wallpaperSupportsAmbientMode = false + statusBarStateListener.onDozeAmountChanged(1f, 1f) notificationShadeDepthController.updateBlurCallback.doFrame(0) - verify(blurUtils).applyBlur(any(), eq(maxBlur), eq(false)) + verify(blurUtils).applyBlur(any(), eq(0), eq(false)) } @Test - @EnableFlags(SharedFlags.FLAG_AMBIENT_AOD) - fun onDozeAmountChanged_doesNotApplyBlurWithAmbientAod() { + fun onDozeAmountChanged_appliesBlurWithAmbientAod() { + notificationShadeDepthController.wallpaperSupportsAmbientMode = true + statusBarStateListener.onDozeAmountChanged(1f, 1f) notificationShadeDepthController.updateBlurCallback.doFrame(0) - verify(blurUtils).applyBlur(any(), eq(0), eq(false)) + verify(blurUtils).applyBlur(any(), eq(maxBlur), eq(false)) } @Test @@ -353,6 +360,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { } @Test + @RequiresFlagsDisabled(Flags.FLAG_NOTIFICATION_SHADE_BLUR) fun updateBlurCallback_setsOpaque_whenScrim() { scrimVisibilityCaptor.value.accept(ScrimController.OPAQUE) notificationShadeDepthController.updateBlurCallback.doFrame(0) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt index 7a982a30981f..6a222c619332 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt @@ -253,6 +253,7 @@ class StatusBarStateControllerImplTest(flags: FlagsParameterization) : SysuiTest underTest.addCallback(listener) val currentScene by collectLastValue(sceneInteractor.currentScene) + val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) val deviceUnlockStatus by collectLastValue(kosmos.deviceUnlockedInteractor.deviceUnlockStatus) @@ -269,9 +270,9 @@ class StatusBarStateControllerImplTest(flags: FlagsParameterization) : SysuiTest // Call start to begin hydrating based on the scene framework: underTest.start() - sceneInteractor.changeScene(toScene = Scenes.Bouncer, loggingReason = "reason") + sceneInteractor.showOverlay(overlay = Overlays.Bouncer, loggingReason = "reason") runCurrent() - assertThat(currentScene).isEqualTo(Scenes.Bouncer) + assertThat(currentOverlays).contains(Overlays.Bouncer) assertThat(statusBarState).isEqualTo(StatusBarState.KEYGUARD) sceneInteractor.changeScene(toScene = Scenes.Shade, loggingReason = "reason") @@ -373,13 +374,12 @@ class StatusBarStateControllerImplTest(flags: FlagsParameterization) : SysuiTest // Call start to begin hydrating based on the scene framework: underTest.start() - sceneInteractor.changeScene(Scenes.Bouncer, loggingReason = "reason") + sceneInteractor.showOverlay(Overlays.Bouncer, loggingReason = "reason") runCurrent() - assertThat(currentScene).isEqualTo(Scenes.Bouncer) - assertThat(currentOverlays).isEmpty() + assertThat(currentOverlays).contains(Overlays.Bouncer) assertThat(statusBarState).isEqualTo(StatusBarState.KEYGUARD) - sceneInteractor.changeScene(Scenes.Lockscreen, loggingReason = "reason") + sceneInteractor.hideOverlay(Overlays.Bouncer, loggingReason = "reason") runCurrent() sceneInteractor.showOverlay(Overlays.NotificationsShade, loggingReason = "reason") @@ -507,6 +507,11 @@ class StatusBarStateControllerImplTest(flags: FlagsParameterization) : SysuiTest testScope.runTest { underTest.start() underTest.setLeaveOpenOnKeyguardHide(true) + kosmos.sceneInteractor.snapToScene(Scenes.Lockscreen, "") + kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( + SuccessFingerprintAuthenticationStatus(0, true) + ) + runCurrent() keyguardTransitionRepository.sendTransitionSteps( from = KeyguardState.AOD, @@ -515,6 +520,7 @@ class StatusBarStateControllerImplTest(flags: FlagsParameterization) : SysuiTest ) assertThat(underTest.leaveOpenOnKeyguardHide()).isEqualTo(true) + kosmos.sceneInteractor.changeScene(Scenes.Gone, "") kosmos.setTransition( sceneTransition = Idle(Scenes.Gone), stateTransition = diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt index 719924c865fd..1b5b0d6ff897 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt @@ -72,7 +72,6 @@ import org.mockito.kotlin.whenever /** Tests for [OngoingActivityChipsViewModel] when the [StatusBarNotifChips] flag is disabled. */ @SmallTest @RunWith(AndroidJUnit4::class) -@EnableFlags(StatusBarChipsModernization.FLAG_NAME) @DisableFlags(StatusBarNotifChips.FLAG_NAME) class OngoingActivityChipsViewModelTest : SysuiTestCase() { private val kosmos = testKosmos().useUnconfinedTestDispatcher() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt index 67415de86d9b..e7be20e7c3cb 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt @@ -88,6 +88,7 @@ class StackStateAnimatorTest : SysuiTestCase() { /* delay= */ 0L, /* duration= */ ANIMATION_DURATION_HEADS_UP_APPEAR.toLong(), /* isHeadsUpAppear= */ true, + /* isHeadsUpCycling= */ false, /* onEndRunnable= */ null, ) } @@ -111,6 +112,7 @@ class StackStateAnimatorTest : SysuiTestCase() { /* delay= */ 0L, /* duration= */ ANIMATION_DURATION_HEADS_UP_APPEAR.toLong(), /* isHeadsUpAppear= */ true, + /* isHeadsUpCycling= */ false, /* onEndRunnable= */ null, ) } @@ -128,6 +130,7 @@ class StackStateAnimatorTest : SysuiTestCase() { /* delay= */ eq(0L), /* translationDirection= */ eq(0f), /* isHeadsUpAnimation= */ eq(true), + /* isHeadsUpCycling= */ eq(false), /* onStartedRunnable= */ any(), /* onFinishedRunnable= */ runnableCaptor.capture(), /* animationListener= */ any(), diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt index c630a1c1e006..e7c571dfcaf3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt @@ -62,6 +62,8 @@ import com.android.systemui.res.R import com.android.systemui.scene.data.repository.Idle import com.android.systemui.scene.data.repository.Transition import com.android.systemui.scene.data.repository.setTransition +import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.domain.interactor.enableDualShade @@ -363,6 +365,8 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S assertThat(alpha).isEqualTo(1f) // Start transitioning to glanceable hub + kosmos.sceneInteractor.changeScene(Scenes.Lockscreen, "") + val progress = 0.6f kosmos.setTransition( sceneTransition = Transition(from = Scenes.Lockscreen, to = Scenes.Communal), @@ -396,6 +400,7 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S assertThat(alpha).isIn(Range.closed(0f, 1f)) // Finish transition to glanceable hub + kosmos.sceneInteractor.changeScene(Scenes.Communal, "") kosmos.setTransition( sceneTransition = Idle(Scenes.Communal), stateTransition = @@ -432,6 +437,8 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S assertThat(alpha).isEqualTo(1f) // Start transitioning to glanceable hub + kosmos.sceneInteractor.snapToScene(Scenes.Lockscreen, "") + val progress = 0.6f kosmos.setTransition( sceneTransition = Transition(from = Scenes.Lockscreen, to = Scenes.Communal), @@ -464,6 +471,7 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S assertThat(alpha).isEqualTo(0) // Finish transition to glanceable hub + kosmos.sceneInteractor.changeScene(Scenes.Communal, "") kosmos.setTransition( sceneTransition = Idle(Scenes.Communal), stateTransition = @@ -492,6 +500,8 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S assertThat(alpha).isEqualTo(0f) // Start transitioning to glanceable hub + kosmos.sceneInteractor.changeScene(Scenes.Lockscreen, "") + val progress = 0.6f kosmos.setTransition( sceneTransition = Transition(from = Scenes.Lockscreen, to = Scenes.Communal), @@ -524,6 +534,7 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S assertThat(alpha).isEqualTo(0) // Finish transition to glanceable hub + kosmos.sceneInteractor.changeScene(Scenes.Communal, "") kosmos.setTransition( sceneTransition = Idle(Scenes.Communal), stateTransition = @@ -587,7 +598,7 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S assertThat(isOnLockscreen).isTrue() kosmos.setTransition( - sceneTransition = Idle(Scenes.Bouncer), + sceneTransition = Idle(Scenes.Lockscreen, setOf(Overlays.Bouncer)), stateTransition = TransitionStep(from = LOCKSCREEN, to = PRIMARY_BOUNCER), ) assertThat(isOnLockscreen).isTrue() @@ -601,7 +612,7 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S val isOnLockscreen by collectLastValue(underTest.isOnLockscreen) setTransition( - sceneTransition = Idle(Scenes.Bouncer), + sceneTransition = Idle(Scenes.Lockscreen, setOf(Overlays.Bouncer)), stateTransition = TransitionStep(from = LOCKSCREEN, to = PRIMARY_BOUNCER), ) assertThat(isOnLockscreen).isTrue() @@ -659,6 +670,7 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S assertThat(isOnGlanceableHubWithoutShade).isFalse() // Move to glanceable hub + kosmos.sceneInteractor.changeScene(Scenes.Communal, "") kosmos.setTransition( sceneTransition = Idle(Scenes.Communal), stateTransition = TransitionStep(from = LOCKSCREEN, to = GLANCEABLE_HUB), @@ -681,6 +693,7 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S shadeTestUtil.setQsExpansion(0f) shadeTestUtil.setLockscreenShadeExpansion(0f) + kosmos.sceneInteractor.changeScene(Scenes.Communal, "") kosmos.setTransition( sceneTransition = Idle(Scenes.Communal), stateTransition = TransitionStep(from = LOCKSCREEN, to = GLANCEABLE_HUB), diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/domain/interactor/KeyguardBypassInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/domain/interactor/KeyguardBypassInteractorTest.kt index 091fbb65bcf9..c966f52b6c73 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/domain/interactor/KeyguardBypassInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/domain/interactor/KeyguardBypassInteractorTest.kt @@ -29,6 +29,7 @@ import com.android.systemui.keyguard.domain.interactor.keyguardQuickAffordanceIn import com.android.systemui.keyguard.domain.interactor.pulseExpansionInteractor import com.android.systemui.kosmos.testScope import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.shadeTestUtil import com.android.systemui.testKosmos @@ -147,10 +148,11 @@ class KeyguardBypassInteractorTest : SysuiTestCase() { } private fun setScene(bouncerShowing: Boolean, onLockscreenScene: Boolean) { - if (bouncerShowing) { - kosmos.sceneInteractor.changeScene(Scenes.Bouncer, "reason") - } else if (onLockscreenScene) { + if (onLockscreenScene) { kosmos.sceneInteractor.changeScene(Scenes.Lockscreen, "reason") + if (bouncerShowing) { + kosmos.sceneInteractor.showOverlay(Overlays.Bouncer, "reason") + } } else { kosmos.sceneInteractor.changeScene(Scenes.Shade, "reason") } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt index 8a796fc33608..7e8ee1b156df 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt @@ -522,11 +522,12 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { } @Test - fun isHomeStatusBarAllowedByScene_sceneBouncer_false() = + fun isHomeStatusBarAllowedByScene_overlayBouncer_false() = kosmos.runTest { val latest by collectLastValue(underTest.isHomeStatusBarAllowedByScene) - kosmos.sceneContainerRepository.snapToScene(Scenes.Bouncer) + kosmos.sceneContainerRepository.snapToScene(Scenes.Lockscreen) + kosmos.sceneContainerRepository.showOverlay(Overlays.Bouncer) assertThat(latest).isFalse() } @@ -1052,7 +1053,8 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible) val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis) - kosmos.sceneContainerRepository.snapToScene(Scenes.Bouncer) + kosmos.sceneContainerRepository.snapToScene(Scenes.Lockscreen) + kosmos.sceneContainerRepository.showOverlay(Overlays.Bouncer) assertThat(clockVisible!!.visibility).isEqualTo(View.INVISIBLE) assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt index 4ab1c0b1af6b..fea0320fc7e3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt @@ -154,7 +154,8 @@ class KeyguardStatusBarViewModelTest(flags: FlagsParameterization) : SysuiTestCa testScope.runTest { val latest by collectLastValue(underTest.isVisible) - kosmos.sceneContainerRepository.snapToScene(Scenes.Bouncer) + kosmos.sceneContainerRepository.snapToScene(Scenes.Lockscreen) + kosmos.sceneContainerRepository.showOverlay(Overlays.Bouncer) assertThat(latest).isFalse() } diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java index a0bf7f00b11c..d3218ad8c9fb 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java @@ -203,8 +203,6 @@ public interface QS extends FragmentBase { */ void setIsNotificationPanelFullWidth(boolean isFullWidth); - default void setQSExpandingOrCollapsing(boolean isQSExpandingOrCollapsing) {} - /** * Callback for when QSPanel container is scrolled */ diff --git a/packages/SystemUI/res/anim/volume_dialog_ringer_close.xml b/packages/SystemUI/res/anim/volume_dialog_ringer_close.xml new file mode 100644 index 000000000000..e7ba52c3fae0 --- /dev/null +++ b/packages/SystemUI/res/anim/volume_dialog_ringer_close.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2025 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<pathInterpolator + xmlns:android="http://schemas.android.com/apk/res/android" + android:controlX1="0.20" + android:controlY1="0.00" + android:controlX2="0.00" + android:controlY2="1.00" />
\ No newline at end of file diff --git a/packages/SystemUI/res/anim/volume_dialog_ringer_open.xml b/packages/SystemUI/res/anim/volume_dialog_ringer_open.xml new file mode 100644 index 000000000000..3b18eefc2552 --- /dev/null +++ b/packages/SystemUI/res/anim/volume_dialog_ringer_open.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2025 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<pathInterpolator + xmlns:android="http://schemas.android.com/apk/res/android" + android:controlX1="0.05" + android:controlY1="0.70" + android:controlX2="0.10" + android:controlY2="1.00" />
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/media_output_list_group_divider.xml b/packages/SystemUI/res/layout/media_output_list_group_divider.xml index c351912de295..fa5ad0d981c1 100644 --- a/packages/SystemUI/res/layout/media_output_list_group_divider.xml +++ b/packages/SystemUI/res/layout/media_output_list_group_divider.xml @@ -20,14 +20,14 @@ android:id="@+id/device_container" android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_marginStart="@dimen/media_output_dialog_margin_horizontal" + android:layout_marginEnd="56dp" android:orientation="vertical"> <TextView android:id="@+id/title" android:layout_width="wrap_content" android:layout_height="36dp" android:layout_gravity="center_vertical|start" - android:layout_marginStart="@dimen/media_output_dialog_margin_horizontal" - android:layout_marginEnd="56dp" android:ellipsize="end" android:maxLines="1" android:fontFamily="@*android:string/config_headlineFontFamilyMedium" diff --git a/packages/SystemUI/res/layout/media_output_list_item_advanced.xml b/packages/SystemUI/res/layout/media_output_list_item_advanced.xml index d297ec46e1e1..6b868b3c7379 100644 --- a/packages/SystemUI/res/layout/media_output_list_item_advanced.xml +++ b/packages/SystemUI/res/layout/media_output_list_item_advanced.xml @@ -20,7 +20,8 @@ android:id="@+id/device_container" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingHorizontal="@dimen/media_output_dialog_margin_horizontal" + android:layout_marginHorizontal="@dimen/media_output_dialog_margin_horizontal" + android:focusable="true" android:baselineAligned="false"> <FrameLayout android:layout_weight="1" @@ -77,6 +78,7 @@ </FrameLayout> <LinearLayout + android:id="@+id/text_content" android:orientation="vertical" android:layout_width="wrap_content" android:layout_gravity="center_vertical|start" @@ -86,7 +88,7 @@ android:layout_marginStart="72dp"> <TextView android:id="@+id/title" - android:importantForAccessibility="no" + android:focusable="false" android:layout_width="wrap_content" android:layout_height="wrap_content" android:ellipsize="end" @@ -96,6 +98,7 @@ android:textSize="16sp"/> <TextView android:id="@+id/subtitle" + android:focusable="false" android:layout_width="wrap_content" android:layout_height="wrap_content" android:marqueeRepeatLimit="marquee_forever" @@ -124,7 +127,6 @@ android:layout_width="24dp" android:layout_height="24dp" android:layout_marginEnd="16dp" - android:indeterminate="true" android:layout_gravity="end|center" android:indeterminateOnly="true" android:importantForAccessibility="no" @@ -135,30 +137,23 @@ android:layout_width="@dimen/media_output_dialog_item_height" android:layout_height="@dimen/media_output_dialog_item_height" android:visibility="gone" - android:layout_marginBottom="6dp" android:layout_marginStart="7dp" - android:layout_gravity="end|center" - android:gravity="center" android:background="@drawable/media_output_item_background_active"> <CheckBox android:id="@+id/check_box" - android:focusable="false" - android:importantForAccessibility="no" - android:layout_gravity="center" - android:layout_width="24dp" - android:layout_height="24dp" - android:button="@drawable/media_output_item_check_box" + android:foreground="@drawable/media_output_item_check_box" + android:foregroundGravity="center" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:button="@null" android:visibility="gone" /> - <ImageView - android:id="@+id/media_output_item_end_click_icon" - android:layout_width="24dp" - android:layout_height="24dp" - android:focusable="false" - android:importantForAccessibility="no" - android:layout_gravity="center" - android:indeterminate="true" - android:indeterminateOnly="true" + <ImageButton + android:id="@+id/end_area_image_button" + android:background="@android:color/transparent" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:contentDescription="@null" android:visibility="gone"/> </FrameLayout> </LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/values-land-television/dimens.xml b/packages/SystemUI/res/values-land-television/dimens.xml index d3bafbca4bb7..b1a46a6805ff 100644 --- a/packages/SystemUI/res/values-land-television/dimens.xml +++ b/packages/SystemUI/res/values-land-television/dimens.xml @@ -20,7 +20,7 @@ <dimen name="volume_dialog_panel_width">48dp</dimen> <dimen name="volume_dialog_panel_width_half">24dp</dimen> <dimen name="volume_dialog_panel_transparent_padding">24dp</dimen> - <dimen name="volume_dialog_slider_width">4dp</dimen> + <dimen name="volume_dialog_slider_width_legacy">4dp</dimen> <dimen name="volume_dialog_slider_corner_radius">@dimen/volume_dialog_slider_width</dimen> <dimen name="volume_dialog_background_blur_radius">31dp</dimen> <dimen name="volume_tool_tip_right_margin">136dp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index e97919e092a9..084495f4b196 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -592,6 +592,12 @@ <!-- Content description of the button to expand the group of devices. [CHAR LIMIT=NONE] --> <string name="accessibility_expand_group">Expand group.</string> + <!-- Content description of the button to add a device to a group. [CHAR LIMIT=NONE] --> + <string name="accessibility_add_device_to_group">Add device to group.</string> + + <!-- Content description of the button to remove a device from a group. [CHAR LIMIT=NONE] --> + <string name="accessibility_remove_device_from_group">Remove device from group.</string> + <!-- Content description of the button to open the application . [CHAR LIMIT=NONE] --> <string name="accessibility_open_application">Open application.</string> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index f6d0f4e26d26..63101d430c6c 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -156,7 +156,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.res.R; import com.android.systemui.scene.domain.interactor.SceneInteractor; import com.android.systemui.scene.shared.flag.SceneContainerFlag; -import com.android.systemui.scene.shared.model.Scenes; +import com.android.systemui.scene.shared.model.Overlays; import com.android.systemui.settings.UserTracker; import com.android.systemui.shade.ShadeDisplayAware; import com.android.systemui.shared.system.TaskStackChangeListener; @@ -2925,12 +2925,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab ) { SceneContainerFlag.assertInNewMode(); return isPrimaryBouncerFullyShown(transitionState) - || transitionState.isTransitioning(null, Scenes.Bouncer); + || transitionState.isTransitioning(null, Overlays.Bouncer); } private boolean isPrimaryBouncerFullyShown(ObservableTransitionState transitionState) { SceneContainerFlag.assertInNewMode(); - return transitionState.isIdle(Scenes.Bouncer); + return transitionState.isIdle(Overlays.Bouncer); } /** diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt index 7039d5e7d9b0..75503e8575f0 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt @@ -147,9 +147,7 @@ constructor( /** The scene to show when bouncer is dismissed. */ val dismissDestination: Flow<SceneKey> = - sceneBackInteractor.backScene - .filter { it != Scenes.Bouncer } - .map { it ?: Scenes.Lockscreen } + sceneBackInteractor.backScene.map { it ?: Scenes.Lockscreen } /** Notifies that the user has places down a pointer, not necessarily dragging just yet. */ fun onDown() { diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt index 116c9415bd11..7f268315e566 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt @@ -10,7 +10,7 @@ import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags import com.android.systemui.bouncer.ui.BouncerDialogFactory import com.android.systemui.bouncer.ui.viewmodel.BouncerContainerViewModel -import com.android.systemui.bouncer.ui.viewmodel.BouncerSceneContentViewModel +import com.android.systemui.bouncer.ui.viewmodel.BouncerOverlayContentViewModel import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application @@ -46,7 +46,7 @@ constructor( val keyguardInteractor: KeyguardInteractor, val selectedUserInteractor: SelectedUserInteractor, val legacyInteractor: PrimaryBouncerInteractor, - val viewModelFactory: BouncerSceneContentViewModel.Factory, + val viewModelFactory: BouncerOverlayContentViewModel.Factory, val dialogFactory: BouncerDialogFactory, val bouncerContainerViewModelFactory: BouncerContainerViewModel.Factory, ) diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt index 2dce28409b57..33cebf70554f 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt @@ -6,11 +6,12 @@ import androidx.activity.OnBackPressedDispatcherOwner import androidx.activity.setViewTreeOnBackPressedDispatcherOwner import androidx.compose.ui.platform.ComposeView import androidx.lifecycle.Lifecycle +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.bouncer.ui.BouncerDialogFactory import com.android.systemui.bouncer.ui.composable.BouncerContainer import com.android.systemui.bouncer.ui.viewmodel.BouncerContainerViewModel -import com.android.systemui.bouncer.ui.viewmodel.BouncerSceneContentViewModel +import com.android.systemui.bouncer.ui.viewmodel.BouncerOverlayContentViewModel import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.lifecycle.WindowLifecycleState import com.android.systemui.lifecycle.repeatWhenAttached @@ -20,7 +21,6 @@ import com.android.systemui.util.kotlin.sample import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.awaitCancellation -import com.android.app.tracing.coroutines.launchTraced as launch /** View binder responsible for binding the compose version of the bouncer. */ object ComposeBouncerViewBinder { @@ -32,7 +32,7 @@ object ComposeBouncerViewBinder { legacyInteractor: PrimaryBouncerInteractor, keyguardInteractor: KeyguardInteractor, selectedUserInteractor: SelectedUserInteractor, - viewModelFactory: BouncerSceneContentViewModel.Factory, + viewModelFactory: BouncerOverlayContentViewModel.Factory, dialogFactory: BouncerDialogFactory, bouncerContainerViewModelFactory: BouncerContainerViewModel.Factory, ) { diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/composable/BouncerContainer.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/composable/BouncerContainer.kt index c59c6816a350..204eb3ec9753 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/composable/BouncerContainer.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/composable/BouncerContainer.kt @@ -24,14 +24,14 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import com.android.compose.theme.PlatformTheme import com.android.systemui.bouncer.ui.BouncerDialogFactory -import com.android.systemui.bouncer.ui.viewmodel.BouncerSceneContentViewModel +import com.android.systemui.bouncer.ui.viewmodel.BouncerOverlayContentViewModel import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.lifecycle.rememberViewModel /** Container that includes the compose bouncer and is meant to be included in legacy keyguard. */ @Composable fun BouncerContainer( - viewModelFactory: BouncerSceneContentViewModel.Factory, + viewModelFactory: BouncerOverlayContentViewModel.Factory, dialogFactory: BouncerDialogFactory, ) { PlatformTheme { diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerOverlayContentViewModel.kt index 5deb751ced27..b944d1acac9d 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerOverlayContentViewModel.kt @@ -51,8 +51,8 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map -/** Models UI state for the content of the bouncer scene. */ -class BouncerSceneContentViewModel +/** Models UI state for the content of the bouncer overlay. */ +class BouncerOverlayContentViewModel @AssistedInject constructor( @Application private val applicationContext: Context, @@ -445,6 +445,6 @@ constructor( @AssistedFactory interface Factory { - fun create(): BouncerSceneContentViewModel + fun create(): BouncerOverlayContentViewModel } } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModel.kt index f50a2ab1d803..aafc0e1a9c67 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModel.kt @@ -20,29 +20,24 @@ import com.android.compose.animation.scene.Back import com.android.compose.animation.scene.Swipe import com.android.compose.animation.scene.UserAction import com.android.compose.animation.scene.UserActionResult -import com.android.systemui.bouncer.domain.interactor.BouncerInteractor +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.ui.viewmodel.UserActionsViewModel import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject -import kotlinx.coroutines.flow.map /** * Models UI state for user actions that can lead to navigation to other scenes when showing the - * bouncer scene. + * bouncer overlay. */ -class BouncerUserActionsViewModel -@AssistedInject -constructor(private val bouncerInteractor: BouncerInteractor) : UserActionsViewModel() { +class BouncerUserActionsViewModel @AssistedInject constructor() : UserActionsViewModel() { override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) { - bouncerInteractor.dismissDestination - .map { prevScene -> - mapOf( - Back to UserActionResult(prevScene), - Swipe.Down to UserActionResult(prevScene), - ) - } - .collect { actions -> setActions(actions) } + setActions( + mapOf( + Back to UserActionResult.HideOverlay(Overlays.Bouncer), + Swipe.Down to UserActionResult.HideOverlay(Overlays.Bouncer), + ) + ) } @AssistedFactory diff --git a/packages/SystemUI/src/com/android/systemui/common/shared/colors/SurfaceEffectColors.kt b/packages/SystemUI/src/com/android/systemui/common/shared/colors/SurfaceEffectColors.kt index 7c2fc7fe3aee..5e8c21f9abf5 100644 --- a/packages/SystemUI/src/com/android/systemui/common/shared/colors/SurfaceEffectColors.kt +++ b/packages/SystemUI/src/com/android/systemui/common/shared/colors/SurfaceEffectColors.kt @@ -20,12 +20,19 @@ import android.content.res.Resources object SurfaceEffectColors { @JvmStatic - fun Resources.surfaceEffect0(): Int { - return getColor(com.android.internal.R.color.surface_effect_0) + fun surfaceEffect0(r: Resources): Int { + return r.getColor(com.android.internal.R.color.surface_effect_0) + } + @JvmStatic + fun surfaceEffect1(r: Resources): Int { + return r.getColor(com.android.internal.R.color.surface_effect_1) + } + @JvmStatic + fun surfaceEffect2(r: Resources): Int { + return r.getColor(com.android.internal.R.color.surface_effect_2) } - @JvmStatic - fun Resources.surfaceEffect1(): Int { - return getColor(com.android.internal.R.color.surface_effect_1) + fun surfaceEffect3(r: Resources): Int { + return r.getColor(com.android.internal.R.color.surface_effect_3) } } diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt index 59beb1ed6c10..c8280d7fa152 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt @@ -113,7 +113,7 @@ constructor( val canShowEditMode = allOf( keyguardTransitionInteractor.isFinishedIn( - scene = Scenes.Gone, + content = Scenes.Gone, stateWithoutSceneContainer = KeyguardState.GONE, ), communalInteractor.editModeOpen, diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalUserActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalUserActionsViewModel.kt index be1d005a0198..a1ff552827b2 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalUserActionsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalUserActionsViewModel.kt @@ -20,6 +20,7 @@ import com.android.compose.animation.scene.Swipe import com.android.compose.animation.scene.UserAction import com.android.compose.animation.scene.UserActionResult import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.ui.viewmodel.UserActionsViewModel import com.android.systemui.shade.domain.interactor.ShadeInteractor @@ -55,9 +56,11 @@ constructor( shadeModeInteractor.shadeMode, ) { isDeviceUnlocked, shadeMode -> buildList { - val bouncerOrGone = - if (isDeviceUnlocked) Scenes.Gone else Scenes.Bouncer - add(Swipe.Up to bouncerOrGone) + if (isDeviceUnlocked) { + add(Swipe.Up to Scenes.Gone) + } else { + add(Swipe.Up to Overlays.Bouncer) + } add(Swipe.End to Scenes.Lockscreen) diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt index 6473b1c30586..d7859c985c7b 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt @@ -168,7 +168,7 @@ constructor( override val isCommunalContentFlowFrozen: Flow<Boolean> = allOf( keyguardTransitionInteractor.isFinishedIn( - scene = Scenes.Communal, + content = Scenes.Communal, stateWithoutSceneContainer = KeyguardState.GLANCEABLE_HUB, ), keyguardInteractor.isKeyguardOccluded, @@ -204,7 +204,7 @@ constructor( override val isFocusable: Flow<Boolean> = combine( keyguardTransitionInteractor.isFinishedIn( - scene = Scenes.Communal, + content = Scenes.Communal, stateWithoutSceneContainer = KeyguardState.GLANCEABLE_HUB, ), communalInteractor.isIdleOnCommunal, diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt index 514242b41eb1..69378b475938 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt @@ -20,6 +20,7 @@ import android.app.StatusBarManager import android.content.Context import android.hardware.face.FaceManager import android.os.CancellationSignal +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.internal.logging.InstanceId import com.android.internal.logging.UiEventLogger import com.android.systemui.Dumpable @@ -59,8 +60,8 @@ import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes -import com.android.systemui.scene.shared.model.Scenes.Bouncer import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.user.data.model.SelectionStatus import com.android.systemui.user.data.repository.UserRepository @@ -87,7 +88,6 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn -import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext /** @@ -138,7 +138,7 @@ interface DeviceEntryFaceAuthRepository { private data class AuthenticationRequest( val uiEvent: FaceAuthUiEvent, - val fallbackToDetection: Boolean + val fallbackToDetection: Boolean, ) @SysUISingleton @@ -248,11 +248,11 @@ constructor( Pair(isLockedOut.isFalse(), "isNotInLockOutState"), Pair( keyguardRepository.isKeyguardDismissible.isFalse(), - "keyguardIsNotDismissible" + "keyguardIsNotDismissible", ), Pair( biometricSettingsRepository.isFaceAuthCurrentlyAllowed, - "isFaceAuthCurrentlyAllowed" + "isFaceAuthCurrentlyAllowed", ), Pair(isAuthenticated.isFalse(), "faceNotAuthenticated"), ) @@ -273,15 +273,15 @@ constructor( biometricSettingsRepository.isFaceAuthCurrentlyAllowed .isFalse() .or(keyguardRepository.isKeyguardDismissible), - "faceAuthIsNotCurrentlyAllowedOrCurrentUserIsTrusted" + "faceAuthIsNotCurrentlyAllowedOrCurrentUserIsTrusted", ), // We don't want to run face detect if fingerprint can be used to unlock the // device // but it's not possible to authenticate with FP from the bouncer (UDFPS) Pair( and(isUdfps(), deviceEntryFingerprintAuthRepository.isRunning).isFalse(), - "udfpsAuthIsNotPossibleAnymore" - ) + "udfpsAuthIsNotPossibleAnymore", + ), ) .andAllFlows("canFaceDetectRun", faceDetectLog) .flowOn(backgroundDispatcher) @@ -318,7 +318,7 @@ constructor( powerInteractor.isAsleep, combine( keyguardTransitionInteractor.isFinishedIn( - scene = Scenes.Gone, + content = Scenes.Gone, stateWithoutSceneContainer = KeyguardState.GONE, ), keyguardInteractor.statusBarState, @@ -350,7 +350,7 @@ constructor( faceAuthLogger.clearingPendingAuthRequest( loggingContext, pendingAuthenticateRequest.value?.uiEvent, - pendingAuthenticateRequest.value?.fallbackToDetection + pendingAuthenticateRequest.value?.fallbackToDetection, ) pendingAuthenticateRequest.value = null } @@ -387,7 +387,7 @@ constructor( ), Pair( biometricSettingsRepository.isFaceAuthEnrolledAndEnabled, - "isFaceAuthEnrolledAndEnabled" + "isFaceAuthEnrolledAndEnabled", ), Pair( if (SceneContainerFlag.isEnabled) { @@ -399,13 +399,13 @@ constructor( } else { keyguardRepository.isKeyguardGoingAway.isFalse() }, - "keyguardNotGoingAway" + "keyguardNotGoingAway", ), Pair( keyguardTransitionInteractor .isInTransitionWhere(toStatePredicate = KeyguardState::deviceIsAsleepInState) .isFalse(), - "deviceNotTransitioningToAsleepState" + "deviceNotTransitioningToAsleepState", ), Pair( keyguardInteractor.isSecureCameraActive @@ -413,29 +413,31 @@ constructor( .or( alternateBouncerInteractor.isVisible.or( if (SceneContainerFlag.isEnabled) { - sceneInteractor.get().transitionState.map { it.isIdle(Bouncer) } + sceneInteractor.get().transitionState.map { + it.isIdle(overlay = Overlays.Bouncer) + } } else { keyguardInteractor.primaryBouncerShowing } ) ), - "secureCameraNotActiveOrAnyBouncerIsShowing" + "secureCameraNotActiveOrAnyBouncerIsShowing", ), Pair( biometricSettingsRepository.isFaceAuthSupportedInCurrentPosture, - "isFaceAuthSupportedInCurrentPosture" + "isFaceAuthSupportedInCurrentPosture", ), Pair( biometricSettingsRepository.isCurrentUserInLockdown.isFalse(), - "userHasNotLockedDownDevice" + "userHasNotLockedDownDevice", ), Pair(keyguardRepository.isKeyguardShowing, "isKeyguardShowing"), Pair( userRepository.selectedUser .map { it.selectionStatus == SelectionStatus.SELECTION_IN_PROGRESS } .isFalse(), - "userSwitchingInProgress" - ) + "userSwitchingInProgress", + ), ) } @@ -486,7 +488,7 @@ constructor( errorCode, errString, errorStatus.isLockoutError(), - errorStatus.isCancellationError() + errorStatus.isCancellationError(), ) onFaceAuthRequestCompleted() } @@ -524,7 +526,7 @@ constructor( faceAuthLogger.attemptingRetryAfterHardwareError(retryCount) requestAuthenticate( FaceAuthUiEvent.FACE_AUTH_TRIGGERED_RETRY_AFTER_HW_UNAVAILABLE, - fallbackToDetection = false + fallbackToDetection = false, ) } } @@ -551,7 +553,7 @@ constructor( if (pendingAuthenticateRequest.value != null) { faceAuthLogger.ignoredFaceAuthTrigger( pendingAuthenticateRequest.value?.uiEvent, - "Previously queued trigger skipped due to new request" + "Previously queued trigger skipped due to new request", ) } faceAuthLogger.queueingRequest(uiEvent, fallbackToDetection) @@ -574,7 +576,7 @@ constructor( pending?.uiEvent, canRunAuth, canRunDetect, - cancelInProgress + cancelInProgress, ) return@combine null } else { @@ -613,7 +615,7 @@ constructor( 0, null, keyguardSessionId, - uiEvent.extraInfo + uiEvent.extraInfo, ) faceAuthLogger.authenticating(uiEvent) faceManager?.authenticate( @@ -624,28 +626,28 @@ constructor( SysUiFaceAuthenticateOptions( currentUserId, uiEvent, - wakeReason = uiEvent.extraInfo + wakeReason = uiEvent.extraInfo, ) - .toFaceAuthenticateOptions() + .toFaceAuthenticateOptions(), ) } } else if (canRunDetection.value) { if (fallbackToDetection) { faceAuthLogger.ignoredFaceAuthTrigger( uiEvent, - "face auth gating check is false, falling back to detection." + "face auth gating check is false, falling back to detection.", ) detect(uiEvent) } else { faceAuthLogger.ignoredFaceAuthTrigger( uiEvent = uiEvent, - "face auth gating check is false and fallback to detection is not requested" + "face auth gating check is false and fallback to detection is not requested", ) } } else { faceAuthLogger.ignoredFaceAuthTrigger( uiEvent, - "face auth & detect gating check is false" + "face auth & detect gating check is false", ) } } @@ -669,7 +671,7 @@ constructor( it, detectionCallback, SysUiFaceAuthenticateOptions(currentUserId, uiEvent, uiEvent.extraInfo) - .toFaceAuthenticateOptions() + .toFaceAuthenticateOptions(), ) } } @@ -695,7 +697,7 @@ constructor( _isAuthRunning.value, _isLockedOut.value, cancellationInProgress.value, - pendingAuthenticateRequest.value?.uiEvent + pendingAuthenticateRequest.value?.uiEvent, ) _authenticationStatus.value = ErrorFaceAuthenticationStatus.cancelNotReceivedError() onFaceAuthRequestCompleted() @@ -759,7 +761,7 @@ private fun Flow<Boolean>.isFalse(): Flow<Boolean> { private fun List<Pair<Flow<Boolean>, String>>.andAllFlows( combinedLoggingInfo: String, - tableLogBuffer: TableLogBuffer + tableLogBuffer: TableLogBuffer, ): Flow<Boolean> { return combine(this.map { it.first }) { val combinedValue = diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt index 5fc924b14814..58310e9c3e9a 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt @@ -30,6 +30,7 @@ import com.android.systemui.scene.data.model.asIterable import com.android.systemui.scene.domain.SceneFrameworkTableLog import com.android.systemui.scene.domain.interactor.SceneBackInteractor import com.android.systemui.scene.domain.interactor.SceneInteractor +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.util.kotlin.pairwise import com.android.systemui.utils.coroutines.flow.mapLatestConflated @@ -238,8 +239,8 @@ constructor( if (alternateBouncerInteractor.canShowAlternateBouncer.value) { alternateBouncerInteractor.forceShow() } else { - sceneInteractor.changeScene( - toScene = Scenes.Bouncer, + sceneInteractor.showOverlay( + overlay = Overlays.Bouncer, loggingReason = "request to unlock device while authentication required", ) } diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntrySourceInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntrySourceInteractor.kt index eb82c7d4ce6a..5de2ed231275 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntrySourceInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntrySourceInteractor.kt @@ -33,6 +33,7 @@ import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticati import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_DISMISS_BOUNCER import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_NONE @@ -84,14 +85,14 @@ constructor( sceneInteractor: SceneInteractor, dumpManager: DumpManager, ) : FlowDumperImpl(dumpManager) { - private val isShowingBouncerScene: Flow<Boolean> = + private val isShowingBouncerOverlay: Flow<Boolean> = sceneInteractor.transitionState .map { transitionState -> - transitionState.isIdle(Scenes.Bouncer) || - transitionState.isTransitioning(null, Scenes.Bouncer) + transitionState.isIdle(overlay = Overlays.Bouncer) || + transitionState.isTransitioning(null, Overlays.Bouncer) } .distinctUntilChanged() - .dumpWhileCollecting("isShowingBouncerScene") + .dumpWhileCollecting("isShowingBouncerOverlay") private val isUnlockedWithStrongFaceUnlock = deviceEntryFaceAuthInteractor.authenticationStatus @@ -115,14 +116,14 @@ constructor( isUnlockedWithStrongFaceUnlock, sceneContainerOcclusionInteractor.isOccludingActivityShown, sceneInteractor.currentScene, - isShowingBouncerScene, + isShowingBouncerOverlay, ) { isAlternateBouncerVisible, isBypassAvailable, isFaceStrongBiometric, isOccluded, currentScene, - isShowingBouncerScene -> + isShowingBouncerOverlay -> val isUnlockingAllowed = keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(isFaceStrongBiometric) val bypass = isBypassAvailable || authController.isUdfpsFingerDown() @@ -140,7 +141,7 @@ constructor( isUnlockingAllowed && isOccluded -> MODE_UNLOCK_COLLAPSING - (isShowingBouncerScene || isAlternateBouncerVisible) && isUnlockingAllowed -> + (isShowingBouncerOverlay || isAlternateBouncerVisible) && isUnlockingAllowed -> MODE_DISMISS_BOUNCER isUnlockingAllowed && bypass -> MODE_UNLOCK_COLLAPSING @@ -158,14 +159,16 @@ constructor( alternateBouncerInteractor.isVisible, authenticationInteractor.authenticationMethod, sceneInteractor.currentScene, + sceneInteractor.currentOverlays, isUnlockedWithStrongFingerprintUnlock, - isShowingBouncerScene, + isShowingBouncerOverlay, ) { alternateBouncerVisible, authenticationMethod, currentScene, + currentOverlays, isFingerprintStrongBiometric, - isShowingBouncerScene -> + isShowingBouncerOverlay -> val unlockingAllowed = keyguardUpdateMonitor.isUnlockingWithBiometricAllowed( isFingerprintStrongBiometric @@ -185,11 +188,12 @@ constructor( unlockingAllowed && currentScene == Scenes.Dream -> MODE_WAKE_AND_UNLOCK_FROM_DREAM - isShowingBouncerScene && unlockingAllowed -> MODE_DISMISS_BOUNCER + isShowingBouncerOverlay && unlockingAllowed -> MODE_DISMISS_BOUNCER unlockingAllowed -> MODE_UNLOCK_COLLAPSING - currentScene != Scenes.Bouncer && !alternateBouncerVisible -> MODE_SHOW_BOUNCER + Overlays.Bouncer !in currentOverlays && !alternateBouncerVisible -> + MODE_SHOW_BOUNCER else -> MODE_NONE } diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt index 4b90e1d52ea0..0d0105404726 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt @@ -50,6 +50,7 @@ import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.res.R import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.user.data.model.SelectionStatus import com.android.systemui.user.data.repository.UserRepository @@ -237,7 +238,7 @@ constructor( private val isBouncerVisible: Flow<Boolean> by lazy { if (SceneContainerFlag.isEnabled) { - sceneInteractor.get().transitionState.map { it.isIdle(Scenes.Bouncer) } + sceneInteractor.get().transitionState.map { it.isIdle(Overlays.Bouncer) } } else { primaryBouncerInteractor.get().isShowing } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java index 3dd2561614b7..599c945db064 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java @@ -51,7 +51,7 @@ import com.android.systemui.dreams.dagger.DreamOverlayModule; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.keyguard.shared.model.KeyguardState; import com.android.systemui.res.R; -import com.android.systemui.scene.shared.model.Scenes; +import com.android.systemui.scene.shared.model.Overlays; import com.android.systemui.shade.ShadeExpansionChangeEvent; import com.android.systemui.shade.domain.interactor.ShadeInteractor; import com.android.systemui.statusbar.BlurUtils; @@ -281,7 +281,7 @@ public class DreamOverlayContainerViewController extends mView, FlowKt.distinctUntilChanged(combineFlows( mKeyguardTransitionInteractor.isFinishedIn( - Scenes.Bouncer, KeyguardState.PRIMARY_BOUNCER), + Overlays.Bouncer, KeyguardState.PRIMARY_BOUNCER), mKeyguardTransitionInteractor.isFinishedIn( KeyguardState.ALTERNATE_BOUNCER), mShadeInteractor.isAnyExpanded(), diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java index 3132ec2b98e3..a2bcb98e36fe 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java @@ -47,7 +47,7 @@ import androidx.lifecycle.ServiceLifecycleDispatcher; import androidx.lifecycle.ViewModelStore; import com.android.app.viewcapture.ViewCaptureAwareWindowManager; -import com.android.compose.animation.scene.SceneKey; +import com.android.compose.animation.scene.OverlayKey; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; import com.android.internal.policy.PhoneWindow; @@ -72,6 +72,7 @@ import com.android.systemui.navigationbar.gestural.domain.GestureInteractor; import com.android.systemui.navigationbar.gestural.domain.TaskMatcher; import com.android.systemui.scene.domain.interactor.SceneInteractor; import com.android.systemui.scene.shared.flag.SceneContainerFlag; +import com.android.systemui.scene.shared.model.Overlays; import com.android.systemui.scene.shared.model.Scenes; import com.android.systemui.shade.ShadeExpansionChangeEvent; import com.android.systemui.touch.TouchInsetManager; @@ -82,6 +83,7 @@ import kotlinx.coroutines.Job; import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.concurrent.CancellationException; import java.util.function.Consumer; @@ -221,10 +223,11 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ } }; - private final Consumer<SceneKey> mCurrentSceneConsumer = new Consumer<>() { + private final Consumer<Set<OverlayKey>> mCurrentOverlaysConsumer = new Consumer<>() { @Override - public void accept(SceneKey currentScene) { - mExecutor.execute(() -> updateBouncerShowingLocked(currentScene == Scenes.Bouncer)); + public void accept(Set<OverlayKey> currentOverlays) { + mExecutor.execute(() -> + updateBouncerShowingLocked(currentOverlays.contains(Overlays.Bouncer))); } }; @@ -430,8 +433,8 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ mFlows.add(collectFlow(getLifecycle(), communalInteractor.isCommunalVisible(), mCommunalVisibleConsumer)); if (SceneContainerFlag.isEnabled()) { - mFlows.add(collectFlow(getLifecycle(), sceneInteractor.getCurrentScene(), - mCurrentSceneConsumer)); + mFlows.add(collectFlow(getLifecycle(), sceneInteractor.getCurrentOverlays(), + mCurrentOverlaysConsumer)); } else { mFlows.add(collectFlow(getLifecycle(), keyguardInteractor.primaryBouncerShowing, mBouncerShowingConsumer)); diff --git a/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModel.kt index 7437d3e215d5..de869961fb9f 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModel.kt @@ -20,6 +20,7 @@ import com.android.compose.animation.scene.Swipe import com.android.compose.animation.scene.UserAction import com.android.compose.animation.scene.UserActionResult import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.ui.viewmodel.UserActionsViewModel import com.android.systemui.shade.domain.interactor.ShadeInteractor @@ -55,9 +56,11 @@ constructor( shadeModeInteractor.shadeMode, ) { isDeviceUnlocked, shadeMode -> buildList { - val bouncerOrGone = - if (isDeviceUnlocked) Scenes.Gone else Scenes.Bouncer - add(Swipe.Up to bouncerOrGone) + if (isDeviceUnlocked) { + add(Swipe.Up to Scenes.Gone) + } else { + add(Swipe.Up to Overlays.Bouncer) + } addAll( when (shadeMode) { diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/TouchpadTutorialScreensProvider.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/TouchpadTutorialScreensProvider.kt index 7d2492a41e82..bea79fdb6aee 100644 --- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/TouchpadTutorialScreensProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/TouchpadTutorialScreensProvider.kt @@ -21,8 +21,16 @@ import androidx.compose.runtime.Composable interface TouchpadTutorialScreensProvider { @Composable - fun BackGesture(onDoneButtonClicked: () -> Unit, onBack: () -> Unit, isAutoProceed: Boolean) + fun BackGesture( + onDoneButtonClicked: () -> Unit, + onBack: () -> Unit, + onAutoProceed: (suspend () -> Unit)?, + ) @Composable - fun HomeGesture(onDoneButtonClicked: () -> Unit, onBack: () -> Unit, isAutoProceed: Boolean) + fun HomeGesture( + onDoneButtonClicked: () -> Unit, + onBack: () -> Unit, + onAutoProceed: (suspend () -> Unit)?, + ) } diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt index ee875c484ca6..8e4e4c09e9f2 100644 --- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt +++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt @@ -108,6 +108,7 @@ fun ActionTutorialContent( actionState: TutorialActionState, onDoneButtonClicked: () -> Unit, config: TutorialScreenConfig, + onAutoProceed: (suspend () -> Unit)? = null, ) { Column( verticalArrangement = Arrangement.Center, @@ -137,9 +138,12 @@ fun ActionTutorialContent( onDoneButtonClicked = onDoneButtonClicked, modifier = Modifier.padding(horizontal = 60.dp).graphicsLayer { alpha = buttonAlpha }, enabled = actionState is Finished, - isNext = config.hasNextButton, + isNext = onAutoProceed != null, ) } + if (actionState is Finished) { + LaunchedEffect(Unit) { onAutoProceed?.invoke() } + } } @Composable diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialScreenConfig.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialScreenConfig.kt index 65adc148b6ae..eda23a51a1ae 100644 --- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialScreenConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialScreenConfig.kt @@ -28,7 +28,6 @@ data class TutorialScreenConfig( val colors: Colors, val strings: Strings, val animations: Animations, - val hasNextButton: Boolean = false, ) { data class Colors( diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt index 086705fbd7ba..8cc1865a6b7e 100644 --- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt @@ -147,7 +147,7 @@ fun KeyboardTouchpadTutorialContainer( .BackGesture( onDoneButtonClicked = vm::onDoneButtonClicked, onBack = vm::onBack, - isAutoProceed = isAutoProceed, + onAutoProceed = if (isAutoProceed) vm::onAutoProceed else null, ) HOME_GESTURE -> touchpadScreens @@ -155,7 +155,7 @@ fun KeyboardTouchpadTutorialContainer( .HomeGesture( onDoneButtonClicked = vm::onDoneButtonClicked, onBack = vm::onBack, - isAutoProceed = isScopeAll, + onAutoProceed = if (isScopeAll) vm::onAutoProceed else null, ) ACTION_KEY -> ActionKeyTutorialScreen( diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModel.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModel.kt index eeb4b697a5fc..90e3af4db6fa 100644 --- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModel.kt @@ -42,6 +42,8 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import java.util.Optional +import kotlin.time.Duration.Companion.seconds +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -49,6 +51,7 @@ import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterNot import kotlinx.coroutines.flow.runningFold +@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class) class KeyboardTouchpadTutorialViewModel( private val gesturesInteractor: Optional<TouchpadGesturesInteractor>, private val keyboardTouchpadConnectionInteractor: KeyboardTouchpadConnectionInteractor, @@ -149,7 +152,16 @@ class KeyboardTouchpadTutorialViewModel( clearDeviceStateForScreen(_screen.value) } + suspend fun onAutoProceed() { + delay(AUTO_PROCEED_DELAY) + progressToNextScreen() + } + fun onDoneButtonClicked() { + progressToNextScreen() + } + + private fun progressToNextScreen() { var nextScreen = screenSequence.nextScreen(_screen.value) while (nextScreen != null) { if (requiredHardwarePresent(nextScreen)) { @@ -250,6 +262,10 @@ class KeyboardTouchpadTutorialViewModel( private object SingleScreenOnly : ScreenSequence { override fun nextScreen(current: Screen): Screen? = null } + + companion object { + private val AUTO_PROCEED_DELAY = 3.seconds + } } enum class RequiredHardware { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt index b3027edb5f0a..b8ee20bc973b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt @@ -19,6 +19,7 @@ package com.android.systemui.keyguard import android.annotation.WorkerThread import android.content.ComponentCallbacks2 import android.util.Log +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application @@ -31,7 +32,6 @@ import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.filter -import com.android.app.tracing.coroutines.launchTraced as launch /** * Releases cached resources on allocated by keyguard. @@ -54,7 +54,7 @@ constructor( Log.d(LOG_TAG, "Resource trimmer registered.") applicationScope.launch(context = bgDispatcher) { keyguardTransitionInteractor - .isFinishedIn(scene = Scenes.Gone, stateWithoutSceneContainer = GONE) + .isFinishedIn(content = Scenes.Gone, stateWithoutSceneContainer = GONE) .filter { isOnGone -> isOnGone } .collect { onKeyguardGone() } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt index 6367b5c2df8e..65ea6735c64b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt @@ -22,12 +22,11 @@ import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository import com.android.systemui.res.R import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag -import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.shade.ShadeDisplayAware import javax.inject.Inject import kotlinx.coroutines.flow.Flow @@ -59,9 +58,9 @@ constructor( private val isSideFpsIndicatorOnPrimaryBouncerEnabled: Boolean get() = context.resources.getBoolean(R.bool.config_show_sidefps_hint_on_bouncer) - private val isBouncerSceneActive: Flow<Boolean> = + private val isBouncerOverlayActive: Flow<Boolean> = if (SceneContainerFlag.isEnabled) { - sceneInteractor.currentScene.map { it == Scenes.Bouncer }.distinctUntilChanged() + sceneInteractor.currentOverlays.map { Overlays.Bouncer in it }.distinctUntilChanged() } else { flowOf(false) } @@ -73,7 +72,7 @@ constructor( primaryBouncerInteractor.startingToHide, primaryBouncerInteractor.startingDisappearAnimation.filterNotNull(), // Bouncer scene visibility changes. - isBouncerSceneActive, + isBouncerOverlayActive, deviceEntryFingerprintAuthRepository.shouldUpdateIndicatorVisibility.filter { it }, ) .map { @@ -105,7 +104,7 @@ constructor( private fun isBouncerActive(): Boolean { if (SceneContainerFlag.isEnabled) { - return sceneInteractor.currentScene.value == Scenes.Bouncer + return Overlays.Bouncer in sceneInteractor.currentOverlays.value } return primaryBouncerInteractor.isBouncerShowing() && !primaryBouncerInteractor.isAnimatingAway() diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBypassInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBypassInteractor.kt index d793064f918e..762403d8cf31 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBypassInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBypassInteractor.kt @@ -21,6 +21,7 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.data.repository.KeyguardBypassRepository import com.android.systemui.scene.domain.interactor.SceneInteractor +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.util.kotlin.FlowDumperImpl @@ -61,7 +62,9 @@ constructor( .flatMapLatest { isBypassAvailable -> if (isBypassAvailable) { combine( - sceneInteractor.currentScene.map { scene -> scene == Scenes.Bouncer }, + sceneInteractor.currentOverlays.map { overlays -> + Overlays.Bouncer in overlays + }, alternateBouncerInteractor.isVisible, sceneInteractor.currentScene.map { scene -> scene == Scenes.Lockscreen }, keyguardQuickAffordanceInteractor.launchingAffordance, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt index d8c707f9baeb..4c238f4e9f8a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt @@ -101,7 +101,7 @@ constructor( .map {} } else { transitionInteractor - .isFinishedIn(scene = Scenes.Gone, stateWithoutSceneContainer = GONE) + .isFinishedIn(content = Scenes.Gone, stateWithoutSceneContainer = GONE) .filter { it } .map {} } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt index f84d29f57cc7..7977000ed5c8 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt @@ -248,7 +248,7 @@ constructor( val topClippingBounds: Flow<Int?> by lazy { combineTransform( keyguardTransitionInteractor - .transitionValue(scene = Scenes.Gone, stateWithoutSceneContainer = GONE) + .transitionValue(content = Scenes.Gone, stateWithoutSceneContainer = GONE) .map { it == 1f } .onStart { emit(false) } .distinctUntilChanged(), diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt index 278a98f8b157..f2d7d914c817 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt @@ -97,7 +97,7 @@ constructor( powerInteractor.detailedWakefulness .sample( transitionInteractor.isFinishedIn( - scene = Scenes.Gone, + content = Scenes.Gone, stateWithoutSceneContainer = KeyguardState.GONE, ), ::Pair, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt index b9784f14fa1c..8e0cb84c1781 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt @@ -63,7 +63,7 @@ constructor( edgeWithoutSceneContainer = Edge.create(to = KeyguardState.GONE), ), transitionInteractor.isFinishedIn( - scene = Scenes.Gone, + content = Scenes.Gone, stateWithoutSceneContainer = KeyguardState.GONE, ), notificationLaunchInteractor.isLaunchAnimationRunning, @@ -74,18 +74,14 @@ constructor( // If the notification launch animation is running, leave the alpha at 0f. // The ActivityLaunchAnimator will morph it from the notification at the // appropriate time. - return@combine KeyguardSurfaceBehindModel( - alpha = 0f, - ) + return@combine KeyguardSurfaceBehindModel(alpha = 0f) } else if ( inWindowLauncherUnlockAnimationInteractor.get().isLauncherUnderneath() ) { // The Launcher icons have their own translation/alpha animations during the // in-window animation. We'll just make the surface visible and let Launcher // do its thing. - return@combine KeyguardSurfaceBehindModel( - alpha = 1f, - ) + return@combine KeyguardSurfaceBehindModel(alpha = 1f) } else { // Otherwise, animate a surface in via alpha/translation, and apply the // swipe velocity (if available) to the translation spring. @@ -113,10 +109,10 @@ constructor( notificationLaunchInteractor.isLaunchAnimationRunning .sample( transitionInteractor.isFinishedIn( - scene = Scenes.Gone, + content = Scenes.Gone, stateWithoutSceneContainer = KeyguardState.GONE, ), - ::Pair + ::Pair, ) .map { (animationRunning, isOnGone) -> animationRunning && !isOnGone } .onStart { emit(false) } @@ -126,10 +122,9 @@ constructor( * means we're going to animate the surface, even if animators aren't yet running). */ val isAnimatingSurface = - combine( - repository.isAnimatingSurface, - isNotificationLaunchAnimationRunningOnKeyguard, - ) { animatingSurface, animatingLaunch -> + combine(repository.isAnimatingSurface, isNotificationLaunchAnimationRunningOnKeyguard) { + animatingSurface, + animatingLaunch -> animatingSurface || animatingLaunch } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt index 3b4a1488095a..af58d10f3066 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt @@ -23,6 +23,7 @@ import com.android.app.tracing.coroutines.flow.filterTraced import com.android.app.tracing.coroutines.flow.traceAs import com.android.app.tracing.coroutines.launchTraced as launch import com.android.app.tracing.coroutines.traceCoroutine +import com.android.compose.animation.scene.ContentKey import com.android.compose.animation.scene.ObservableTransitionState import com.android.compose.animation.scene.SceneKey import com.android.systemui.Flags.keyguardTransitionForceFinishOnScreenOff @@ -236,28 +237,28 @@ constructor( if (!SceneContainerFlag.isEnabled) { return flow } - if (edge.isSceneWildcardEdge()) { + if (edge.isContentWildcardEdge()) { return simulateTransitionStepsForSceneTransitions(edge) } return flow.filterTraced("stl-filter") { step -> - val fromScene = + val fromContent = when (edge) { - is Edge.StateToState -> edge.from?.mapToSceneContainerScene() - is Edge.StateToScene -> edge.from?.mapToSceneContainerScene() - is Edge.SceneToState -> edge.from + is Edge.StateToState -> edge.from?.mapToSceneContainerContent() + is Edge.StateToContent -> edge.from?.mapToSceneContainerContent() + is Edge.ContentToState -> edge.from } - val toScene = + val toContent = when (edge) { - is Edge.StateToState -> edge.to?.mapToSceneContainerScene() - is Edge.StateToScene -> edge.to - is Edge.SceneToState -> edge.to?.mapToSceneContainerScene() + is Edge.StateToState -> edge.to?.mapToSceneContainerContent() + is Edge.StateToContent -> edge.to + is Edge.ContentToState -> edge.to?.mapToSceneContainerContent() } val isTransitioningBetweenLockscreenStates = - fromScene.isLockscreenOrNull() && toScene.isLockscreenOrNull() + fromContent.isLockscreenOrNull() && toContent.isLockscreenOrNull() val isTransitioningBetweenDesiredScenes = - sceneInteractor.transitionState.value.isTransitioning(fromScene, toScene) + sceneInteractor.transitionState.value.isTransitioning(fromContent, toContent) // When in STL A -> B settles in A we can't do the same in KTF as KTF requires us to // start B -> A to get back to A. [LockscreenSceneTransitionInteractor] will emit these @@ -266,8 +267,8 @@ constructor( // this condition to not filter out the STARTED and FINISHED step of the "artificially" // reversed B -> A transition. val belongsToInstantReversedTransition = - sceneInteractor.transitionState.value.isIdle(toScene) && - sceneTransitionPair.value.previousValue.isTransitioning(toScene, fromScene) + sceneInteractor.topmostContent.value == toContent && + sceneTransitionPair.value.previousValue.isTransitioning(toContent, fromContent) // We can't compare the terminal step with the current sceneTransition because // a) STL has no guarantee that it will settle in Idle() when finished/canceled @@ -276,7 +277,7 @@ constructor( val terminalStepBelongsToPreviousTransition = (step.transitionState == TransitionState.FINISHED || step.transitionState == TransitionState.CANCELED) && - sceneTransitionPair.value.previousValue.isTransitioning(fromScene, toScene) + sceneTransitionPair.value.previousValue.isTransitioning(fromContent, toContent) return@filterTraced isTransitioningBetweenLockscreenStates || isTransitioningBetweenDesiredScenes || @@ -285,7 +286,7 @@ constructor( } } - private fun SceneKey?.isLockscreenOrNull() = this == Scenes.Lockscreen || this == null + private fun ContentKey?.isLockscreenOrNull() = this == Scenes.Lockscreen || this == null /** * This function will return a flow that simulates TransitionSteps based on STL movements @@ -317,8 +318,8 @@ constructor( when (edge) { is Edge.StateToState -> throw IllegalStateException("Should not be reachable.") - is Edge.SceneToState -> it.isTransitioning(from = edge.from) - is Edge.StateToScene -> it.isTransitioning(to = edge.to) + is Edge.ContentToState -> it.isTransitioning(from = edge.from) + is Edge.StateToContent -> it.isTransitioning(to = edge.to) } if (!isMatchingTransition) { return@flatMapLatestWithFinished flowOf() @@ -361,41 +362,43 @@ constructor( */ private fun <T> Flow<T>.flatMapLatestWithFinished( transform: suspend (T) -> Flow<TransitionStep> - ): Flow<TransitionStep> = channelFlow { - var job: Job? = null - var startedEmitted = false - - coroutineScope { - collect { value -> - traceCoroutine("cancelAndJoin") { job?.cancelAndJoin() } - - job = launch("inner") { - val innerFlow = transform(value) - try { - innerFlow.collect { step -> - if (step.transitionState == TransitionState.STARTED) { - startedEmitted = true + ): Flow<TransitionStep> = + channelFlow { + var job: Job? = null + var startedEmitted = false + + coroutineScope { + collect { value -> + traceCoroutine("cancelAndJoin") { job?.cancelAndJoin() } + + job = + launch("inner") { + val innerFlow = transform(value) + try { + innerFlow.collect { step -> + if (step.transitionState == TransitionState.STARTED) { + startedEmitted = true + } + traceCoroutine("send($step)") { send(step) } + } + } finally { + if (startedEmitted) { + val step = + TransitionStep( + from = UNDEFINED, + to = UNDEFINED, + value = 1f, + transitionState = TransitionState.FINISHED, + ) + traceCoroutine("send($step)") { send(step) } + startedEmitted = false + } + } } - traceCoroutine("send($step)") { send(step) } - } - } finally { - if (startedEmitted) { - val step = - TransitionStep( - from = UNDEFINED, - to = UNDEFINED, - value = 1f, - transitionState = TransitionState.FINISHED, - ) - traceCoroutine("send($step)") { send(step) } - startedEmitted = false - } } } } - } - } - .traceAs("flatMapLatestWithFinished") + .traceAs("flatMapLatestWithFinished") /** * Converts old KTF states to UNDEFINED when [SceneContainerFlag] is enabled. @@ -414,17 +417,17 @@ constructor( from = edge.from?.mapToSceneContainerState(), to = edge.to?.mapToSceneContainerState(), ) - is Edge.SceneToState -> Edge.create(UNDEFINED, edge.to) - is Edge.StateToScene -> Edge.create(edge.from, UNDEFINED) + is Edge.ContentToState -> Edge.create(UNDEFINED, edge.to) + is Edge.StateToContent -> Edge.create(edge.from, UNDEFINED) } } fun transitionValue( - scene: SceneKey? = null, + content: ContentKey? = null, stateWithoutSceneContainer: KeyguardState, ): Flow<Float> { - return if (SceneContainerFlag.isEnabled && scene != null) { - sceneInteractor.transitionProgress(scene) + return if (SceneContainerFlag.isEnabled && content != null) { + sceneInteractor.transitionProgress(content) } else { transitionValue(stateWithoutSceneContainer) } @@ -439,7 +442,7 @@ constructor( fun transitionValue(state: KeyguardState): Flow<Float> { if (SceneContainerFlag.isEnabled && state != state.mapToSceneContainerState()) { Log.e(TAG, "SceneContainer is enabled but a deprecated state $state is used.") - return transitionValue(state.mapToSceneContainerScene()!!, state) + return transitionValue(state.mapToSceneContainerContent()!!, state) } return getTransitionValueFlow(state) } @@ -533,13 +536,13 @@ constructor( */ fun isInTransition(edge: Edge, edgeWithoutSceneContainer: Edge? = null): Flow<Boolean> { return if (SceneContainerFlag.isEnabled) { - if (edge.isSceneWildcardEdge()) { + if (edge.isContentWildcardEdge()) { sceneInteractor.transitionState.map { when (edge) { is Edge.StateToState -> throw IllegalStateException("Should not be reachable.") - is Edge.SceneToState -> it.isTransitioning(from = edge.from) - is Edge.StateToScene -> it.isTransitioning(to = edge.to) + is Edge.ContentToState -> it.isTransitioning(from = edge.from) + is Edge.StateToContent -> it.isTransitioning(to = edge.to) } } } else { @@ -581,10 +584,15 @@ constructor( return finishedKeyguardState.map { stateMatcher(it) }.distinctUntilChanged() } - fun isFinishedIn(scene: SceneKey, stateWithoutSceneContainer: KeyguardState): Flow<Boolean> { + fun isFinishedIn( + content: ContentKey, + stateWithoutSceneContainer: KeyguardState, + ): Flow<Boolean> { return if (SceneContainerFlag.isEnabled) { - sceneInteractor.transitionState.map { - it.isIdle(scene) || it.isTransitioning(from = scene) + combine(sceneInteractor.topmostContent, sceneInteractor.transitionState) { + topmostContent, + state -> + topmostContent == content || state.isTransitioning(from = content) } } else { isFinishedIn(stateWithoutSceneContainer) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt index 61cf2cdab92d..68d595ebf0b6 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt @@ -61,7 +61,7 @@ constructor( private val defaultSurfaceBehindVisibility = combine( transitionInteractor.isFinishedIn( - scene = Scenes.Gone, + content = Scenes.Gone, stateWithoutSceneContainer = KeyguardState.GONE, ), wakeToGoneInteractor.canWakeDirectlyToGone, @@ -121,7 +121,7 @@ constructor( when { state.isTransitioning(from = Scenes.Lockscreen, to = Scenes.Gone) -> isDeviceEnteredDirectly - state.isTransitioning(from = Scenes.Bouncer, to = Scenes.Gone) -> + state.isTransitioning(from = Overlays.Bouncer, to = Scenes.Gone) -> (state as Transition).progress.map { progress -> progress > FromPrimaryBouncerTransitionInteractor @@ -179,7 +179,7 @@ constructor( edgeWithoutSceneContainer = Edge.create(to = KeyguardState.GONE), ), transitionInteractor.isFinishedIn( - scene = Scenes.Gone, + content = Scenes.Gone, stateWithoutSceneContainer = KeyguardState.GONE, ), surfaceBehindInteractor.isAnimatingSurface, @@ -202,21 +202,23 @@ constructor( when (it) { is Idle -> { when (it.currentScene) { - in keyguardScenes -> flowOf(true) - in nonKeyguardScenes -> flowOf(false) - in keyguardAgnosticScenes -> isDeviceNotEnteredDirectly + in keyguardContent -> flowOf(true) + in nonKeyguardContent -> flowOf(false) + in keyguardAgnosticContent -> isDeviceNotEnteredDirectly else -> throw IllegalStateException("Unknown scene: ${it.currentScene}") } } is Transition -> { when { - it.isTransitioningSets(from = keyguardScenes) -> flowOf(true) - it.isTransitioningSets(from = nonKeyguardScenes) -> flowOf(false) - it.isTransitioningSets(from = keyguardAgnosticScenes) -> + it.isTransitioningSets(from = keyguardContent) -> flowOf(true) + it.isTransitioningSets(from = nonKeyguardContent) -> flowOf(false) + it.isTransitioningSets(from = keyguardAgnosticContent) -> isDeviceNotEnteredDirectly else -> - throw IllegalStateException("Unknown scene: ${it.fromContent}") + throw IllegalStateException( + "Unknown content: ${it.fromContent}" + ) } } } @@ -330,22 +332,23 @@ constructor( companion object { /** - * Scenes that are part of the keyguard and are shown when the device is locked or when the + * Content that is part of the keyguard and are shown when the device is locked or when the * keyguard still needs to be dismissed. */ - val keyguardScenes = setOf(Scenes.Lockscreen, Scenes.Bouncer, Scenes.Communal, Scenes.Dream) + val keyguardContent = + setOf(Scenes.Lockscreen, Overlays.Bouncer, Scenes.Communal, Scenes.Dream) /** - * Scenes that don't belong in the keyguard family and cannot show when the device is locked - * or when the keyguard still needs to be dismissed. + * Content that doesn't belong in the keyguard family and cannot show when the device is + * locked or when the keyguard still needs to be dismissed. */ - private val nonKeyguardScenes = setOf(Scenes.Gone) + private val nonKeyguardContent = setOf(Scenes.Gone) /** - * Scenes that can show regardless of device lock or keyguard dismissal states. Other + * Content that can show regardless of device lock or keyguard dismissal states. Other * sources of state need to be consulted to know whether the device has been entered or not. */ - private val keyguardAgnosticScenes = + private val keyguardAgnosticContent = setOf( Scenes.Shade, Scenes.QuickSettings, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/Edge.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/Edge.kt index 1306b2690c05..4cc4d271357c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/Edge.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/Edge.kt @@ -16,7 +16,7 @@ package com.android.systemui.keyguard.shared.model import android.util.Log -import com.android.compose.animation.scene.SceneKey +import com.android.compose.animation.scene.ContentKey import com.android.systemui.keyguard.shared.model.KeyguardState.UNDEFINED import com.android.systemui.scene.shared.flag.SceneContainerFlag @@ -29,8 +29,8 @@ sealed class Edge { fun verifyValidKeyguardStates() { when (this) { is StateToState -> verifyValidKeyguardStates(from, to) - is SceneToState -> verifyValidKeyguardStates(null, to) - is StateToScene -> verifyValidKeyguardStates(from, null) + is ContentToState -> verifyValidKeyguardStates(null, to) + is StateToContent -> verifyValidKeyguardStates(from, null) } } @@ -66,19 +66,16 @@ sealed class Edge { } } else { if (from == UNDEFINED || to == UNDEFINED) { - Log.e( - TAG, - "UNDEFINED should not be used when scene container is disabled", - ) + Log.e(TAG, "UNDEFINED should not be used when scene container is disabled") } } } - fun isSceneWildcardEdge(): Boolean { + fun isContentWildcardEdge(): Boolean { return when (this) { is StateToState -> false - is SceneToState -> to == null - is StateToScene -> from == null + is ContentToState -> to == null + is StateToContent -> from == null } } @@ -89,9 +86,9 @@ sealed class Edge { } } - data class StateToScene(val from: KeyguardState? = null, val to: SceneKey) : Edge() + data class StateToContent(val from: KeyguardState? = null, val to: ContentKey) : Edge() - data class SceneToState(val from: SceneKey, val to: KeyguardState? = null) : Edge() + data class ContentToState(val from: ContentKey, val to: KeyguardState? = null) : Edge() companion object { private const val TAG = "Edge" @@ -102,11 +99,11 @@ sealed class Edge { @JvmStatic @JvmOverloads - fun create(from: KeyguardState? = null, to: SceneKey) = StateToScene(from, to) + fun create(from: KeyguardState? = null, to: ContentKey) = StateToContent(from, to) @JvmStatic @JvmOverloads - fun create(from: SceneKey, to: KeyguardState? = null) = SceneToState(from, to) + fun create(from: ContentKey, to: KeyguardState? = null) = ContentToState(from, to) /** * This edge is a placeholder for when an edge needs to be passed but there is no edge for diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt index f0e79b8590a0..21bfb5f79abb 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt @@ -16,8 +16,9 @@ package com.android.systemui.keyguard.shared.model import android.util.Log -import com.android.compose.animation.scene.SceneKey +import com.android.compose.animation.scene.ContentKey import com.android.systemui.scene.shared.flag.SceneContainerFlag +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes /** List of all possible states to transition to/from */ @@ -130,7 +131,7 @@ enum class KeyguardState { } } - fun mapToSceneContainerScene(): SceneKey? { + fun mapToSceneContainerContent(): ContentKey? { return when (this) { OFF, DOZING, @@ -140,7 +141,7 @@ enum class KeyguardState { OCCLUDED, LOCKSCREEN -> Scenes.Lockscreen GLANCEABLE_HUB -> Scenes.Communal - PRIMARY_BOUNCER -> Scenes.Bouncer + PRIMARY_BOUNCER -> Overlays.Bouncer GONE -> Scenes.Gone UNDEFINED -> null } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt index 43cce4b1c40c..74d471c7c1d6 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt @@ -27,7 +27,7 @@ import com.android.systemui.keyguard.ui.transitions.BlurConfig import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition import com.android.systemui.scene.shared.flag.SceneContainerFlag -import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.ui.composable.transitions.TO_BOUNCER_FADE_FRACTION import javax.inject.Inject import kotlinx.coroutines.flow.Flow @@ -49,7 +49,7 @@ constructor( animationFlow .setup( duration = FromAlternateBouncerTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION, - edge = Edge.create(from = ALTERNATE_BOUNCER, to = Scenes.Bouncer), + edge = Edge.create(from = ALTERNATE_BOUNCER, to = Overlays.Bouncer), ) .setupWithoutSceneContainer( edge = Edge.create(from = ALTERNATE_BOUNCER, to = PRIMARY_BOUNCER) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt index 8b5e3b9756e3..a22d21ae1371 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt @@ -26,7 +26,7 @@ import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.BlurConfig import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition -import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.scene.shared.model.Overlays import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.emptyFlow @@ -44,7 +44,7 @@ constructor(blurConfig: BlurConfig, animationFlow: KeyguardTransitionAnimationFl animationFlow .setup( duration = FromAodTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION, - edge = Edge.create(from = AOD, to = Scenes.Bouncer), + edge = Edge.create(from = AOD, to = Overlays.Bouncer), ) .setupWithoutSceneContainer(edge = Edge.create(from = AOD, to = PRIMARY_BOUNCER)) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt index ff96e035bc81..2c712d106a6e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt @@ -26,7 +26,7 @@ import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.BlurConfig import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition -import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.scene.shared.model.Overlays import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.emptyFlow @@ -45,7 +45,7 @@ constructor(private val blurConfig: BlurConfig, animationFlow: KeyguardTransitio animationFlow .setup( duration = TO_PRIMARY_BOUNCER_DURATION, - edge = Edge.create(from = DOZING, to = Scenes.Bouncer), + edge = Edge.create(from = DOZING, to = Overlays.Bouncer), ) .setupWithoutSceneContainer(edge = Edge.create(from = DOZING, to = PRIMARY_BOUNCER)) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt index def87a8e2717..d4676bc9c146 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt @@ -40,6 +40,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING import com.android.systemui.keyguard.shared.model.TransitionState.STARTED import com.android.systemui.keyguard.ui.StateToValue +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shade.ui.viewmodel.NotificationShadeWindowModel @@ -182,7 +183,7 @@ constructor( edgeWithoutSceneContainer = Edge.create(from = LOCKSCREEN, to = GONE), ), keyguardTransitionInteractor.isInTransition( - edge = Edge.create(from = Scenes.Bouncer, to = LOCKSCREEN), + edge = Edge.create(from = Overlays.Bouncer, to = LOCKSCREEN), edgeWithoutSceneContainer = Edge.create(from = PRIMARY_BOUNCER, to = LOCKSCREEN), ), @@ -226,7 +227,7 @@ constructor( .map { it > 1f - offToLockscreenTransitionViewModel.alphaStartAt } .onStart { emit(false) }, keyguardTransitionInteractor - .transitionValue(scene = Scenes.Gone, stateWithoutSceneContainer = GONE) + .transitionValue(content = Scenes.Gone, stateWithoutSceneContainer = GONE) .map { it == 1f } .onStart { emit(false) }, ) @@ -361,7 +362,7 @@ constructor( .map { it > 0f } .onStart { emit(false) }, keyguardTransitionInteractor.isFinishedIn( - scene = Scenes.Gone, + content = Scenes.Gone, stateWithoutSceneContainer = GONE, ), deviceEntryInteractor.isBypassEnabled, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt index b96c879157dc..3758afa61ed4 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt @@ -27,7 +27,7 @@ import com.android.systemui.keyguard.ui.transitions.BlurConfig import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition import com.android.systemui.scene.shared.flag.SceneContainerFlag -import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.ui.composable.transitions.TO_BOUNCER_FADE_FRACTION import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds @@ -50,7 +50,7 @@ constructor( animationFlow .setup( duration = FromLockscreenTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION, - edge = Edge.create(from = LOCKSCREEN, to = Scenes.Bouncer), + edge = Edge.create(from = LOCKSCREEN, to = Overlays.Bouncer), ) .setupWithoutSceneContainer(edge = Edge.create(from = LOCKSCREEN, to = PRIMARY_BOUNCER)) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModel.kt index 06c27d31cc3b..0eeb6c66c3bc 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModel.kt @@ -21,6 +21,7 @@ import com.android.compose.animation.scene.UserAction import com.android.compose.animation.scene.UserActionResult import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.ui.viewmodel.UserActionsViewModel import com.android.systemui.shade.domain.interactor.ShadeInteractor @@ -60,7 +61,11 @@ constructor( occlusionInteractor.isOccludingActivityShown, ) { isDeviceUnlocked, shadeMode, isOccluded -> buildList { - add(Swipe.Up to if (isDeviceUnlocked) Scenes.Gone else Scenes.Bouncer) + if (isDeviceUnlocked) { + add(Swipe.Up to Scenes.Gone) + } else { + add(Swipe.Up to Overlays.Bouncer) + } addAll( when (shadeMode) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt index dc75829f95ee..32bad8ed0146 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt @@ -26,7 +26,7 @@ import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.BlurConfig import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition -import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.scene.shared.model.Overlays import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.flow.Flow @@ -49,7 +49,7 @@ constructor( animationFlow .setup( duration = FromPrimaryBouncerTransitionInteractor.TO_AOD_DURATION, - edge = Edge.create(from = Scenes.Bouncer, to = AOD), + edge = Edge.create(from = Overlays.Bouncer, to = AOD), ) .setupWithoutSceneContainer(edge = Edge.create(from = PRIMARY_BOUNCER, to = AOD)) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModel.kt index 7f431bc3e0f9..1b533440454c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModel.kt @@ -26,7 +26,7 @@ import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.BlurConfig import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition -import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.scene.shared.model.Overlays import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.emptyFlow @@ -49,7 +49,7 @@ constructor( animationFlow .setup( duration = TO_DOZING_DURATION, - edge = Edge.create(from = Scenes.Bouncer, to = DOZING), + edge = Edge.create(from = Overlays.Bouncer, to = DOZING), ) .setupWithoutSceneContainer(edge = Edge.create(from = PRIMARY_BOUNCER, to = DOZING)) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt index 9cb03874e412..5a111aa519fc 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt @@ -28,7 +28,7 @@ import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.BlurConfig import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition -import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.scene.shared.model.Overlays import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.flow.Flow @@ -50,7 +50,7 @@ constructor( animationFlow .setup( duration = FromPrimaryBouncerTransitionInteractor.TO_LOCKSCREEN_DURATION, - edge = Edge.create(from = Scenes.Bouncer, to = LOCKSCREEN), + edge = Edge.create(from = Overlays.Bouncer, to = LOCKSCREEN), ) .setupWithoutSceneContainer(edge = Edge.create(from = PRIMARY_BOUNCER, to = LOCKSCREEN)) diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt index 55d8b8c4def0..bfd48c825424 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt @@ -683,7 +683,7 @@ constructor( internal fun listenForAnyStateToGoneKeyguardTransition(scope: CoroutineScope): Job { return scope.launch { keyguardTransitionInteractor - .isFinishedIn(scene = Scenes.Gone, stateWithoutSceneContainer = GONE) + .isFinishedIn(content = Scenes.Gone, stateWithoutSceneContainer = GONE) .filter { it } .collect { showMediaCarousel() diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt index b64cb3dd456a..f06c4cce0daa 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt @@ -58,6 +58,7 @@ import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.CrossFadeHelper import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.SysuiStatusBarStateController +import com.android.systemui.statusbar.featurepods.popups.StatusBarPopupChips import com.android.systemui.statusbar.notification.stack.StackStateAnimator import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.policy.ConfigurationController @@ -229,7 +230,7 @@ constructor( else result.setIntersect(animationStartClipping, targetClipping) } - private val mediaHosts = arrayOfNulls<MediaHost>(LOCATION_COMMUNAL_HUB + 1) + private val mediaHosts = arrayOfNulls<MediaHost>(LOCATION_STATUS_BAR_POPUP + 1) /** * The last location where this view was at before going to the desired location. This is useful @@ -374,6 +375,15 @@ constructor( } } + /** Is the Media Control StatusBarPopup showing */ + var isMediaControlPopupShowing: Boolean = false + set(value) { + if (field != value && StatusBarPopupChips.isEnabled) { + field = value + updateDesiredLocation(forceNoAnimation = true) + } + } + /** Are location changes currently blocked? */ private val blockLocationChanges: Boolean get() { @@ -1225,6 +1235,7 @@ constructor( // Keep the current location until we're allowed to again return desiredLocation } + val onLockscreen = (!bypassController.bypassEnabled && (statusbarState == StatusBarState.KEYGUARD)) @@ -1234,6 +1245,8 @@ constructor( (onCommunalNotDreaming && qsExpansion == 0.0f) || onCommunalDreamingAndShadeExpanding val location = when { + isMediaControlPopupShowing && StatusBarPopupChips.isEnabled -> + LOCATION_STATUS_BAR_POPUP dreamOverlayActive && dreamMediaComplicationActive -> LOCATION_DREAM_OVERLAY onCommunal -> LOCATION_COMMUNAL_HUB (qsExpansion > 0.0f || inSplitShade) && !onLockscreen -> LOCATION_QS @@ -1377,6 +1390,9 @@ constructor( /** Attached to a view in the communal UI grid */ const val LOCATION_COMMUNAL_HUB = 4 + /** Attached to a popup that is shown with a media control chip in the status bar */ + const val LOCATION_STATUS_BAR_POPUP = 5 + /** Attached at the root of the hierarchy in an overlay */ const val IN_OVERLAY = -1000 @@ -1422,6 +1438,7 @@ private annotation class TransformationType MediaHierarchyManager.LOCATION_LOCKSCREEN, MediaHierarchyManager.LOCATION_DREAM_OVERLAY, MediaHierarchyManager.LOCATION_COMMUNAL_HUB, + MediaHierarchyManager.LOCATION_STATUS_BAR_POPUP, MediaHierarchyManager.LOCATION_UNKNOWN, ], ) diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaUiEventLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaUiEventLogger.kt index 09f973cca343..f0da226129bf 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaUiEventLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaUiEventLogger.kt @@ -45,7 +45,7 @@ class MediaUiEventLogger @Inject constructor(private val logger: UiEventLogger) uid: Int, packageName: String, instanceId: InstanceId, - playbackLocation: Int + playbackLocation: Int, ) { val event = when (playbackLocation) { @@ -61,7 +61,7 @@ class MediaUiEventLogger @Inject constructor(private val logger: UiEventLogger) uid: Int, packageName: String, instanceId: InstanceId, - playbackLocation: Int + playbackLocation: Int, ) { val event = when (playbackLocation) { @@ -112,7 +112,7 @@ class MediaUiEventLogger @Inject constructor(private val logger: UiEventLogger) MediaUiEvent.OPEN_SETTINGS_LONG_PRESS, uid, packageName, - instanceId + instanceId, ) } @@ -156,6 +156,8 @@ class MediaUiEventLogger @Inject constructor(private val logger: UiEventLogger) MediaUiEvent.MEDIA_CAROUSEL_LOCATION_DREAM MediaHierarchyManager.LOCATION_COMMUNAL_HUB -> MediaUiEvent.MEDIA_CAROUSEL_LOCATION_COMMUNAL + MediaHierarchyManager.LOCATION_STATUS_BAR_POPUP -> + MediaUiEvent.MEDIA_CAROUSEL_LOCATION_STATUS_BAR_POPUP else -> throw IllegalArgumentException("Unknown media carousel location $location") } logger.log(event) @@ -166,7 +168,7 @@ class MediaUiEventLogger @Inject constructor(private val logger: UiEventLogger) MediaUiEvent.MEDIA_RECOMMENDATION_ADDED, 0, packageName, - instanceId + instanceId, ) } @@ -175,7 +177,7 @@ class MediaUiEventLogger @Inject constructor(private val logger: UiEventLogger) MediaUiEvent.MEDIA_RECOMMENDATION_REMOVED, 0, packageName, - instanceId + instanceId, ) } @@ -184,7 +186,7 @@ class MediaUiEventLogger @Inject constructor(private val logger: UiEventLogger) MediaUiEvent.MEDIA_RECOMMENDATION_ACTIVATED, uid, packageName, - instanceId + instanceId, ) } @@ -194,7 +196,7 @@ class MediaUiEventLogger @Inject constructor(private val logger: UiEventLogger) 0, packageName, instanceId, - position + position, ) } @@ -203,7 +205,7 @@ class MediaUiEventLogger @Inject constructor(private val logger: UiEventLogger) MediaUiEvent.MEDIA_RECOMMENDATION_CARD_TAP, 0, packageName, - instanceId + instanceId, ) } @@ -212,7 +214,7 @@ class MediaUiEventLogger @Inject constructor(private val logger: UiEventLogger) MediaUiEvent.MEDIA_OPEN_BROADCAST_DIALOG, uid, packageName, - instanceId + instanceId, ) } @@ -221,7 +223,7 @@ class MediaUiEventLogger @Inject constructor(private val logger: UiEventLogger) MediaUiEvent.MEDIA_CAROUSEL_SINGLE_PLAYER, uid, packageName, - instanceId + instanceId, ) } @@ -230,7 +232,7 @@ class MediaUiEventLogger @Inject constructor(private val logger: UiEventLogger) MediaUiEvent.MEDIA_CAROUSEL_MULTIPLE_PLAYERS, uid, packageName, - instanceId + instanceId, ) } } @@ -280,6 +282,8 @@ enum class MediaUiEvent(val metricId: Int) : UiEventLogger.UiEventEnum { MEDIA_CAROUSEL_LOCATION_DREAM(1040), @UiEvent(doc = "The media carousel moved to the communal hub UI") MEDIA_CAROUSEL_LOCATION_COMMUNAL(1520), + @UiEvent(doc = "The media carousel moved to the status bar popup") + MEDIA_CAROUSEL_LOCATION_STATUS_BAR_POPUP(2170), @UiEvent(doc = "A media recommendation card was added to the media carousel") MEDIA_RECOMMENDATION_ADDED(1041), @UiEvent(doc = "A media recommendation card was removed from the media carousel") diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java index 45a3a8ce60c4..662a9c739ff6 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java +++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java @@ -49,6 +49,7 @@ public interface MediaModule { String KEYGUARD = "media_keyguard"; String DREAM = "dream"; String COMMUNAL_HUB = "communal_Hub"; + String POPUP = "popup"; /** */ @Provides @@ -102,7 +103,26 @@ public interface MediaModule { @Provides @SysUISingleton @Named(COMMUNAL_HUB) - static MediaHost providesCommunalMediaHost(MediaHost.MediaHostStateHolder stateHolder, + static MediaHost providesCommunalMediaHost( + MediaHost.MediaHostStateHolder stateHolder, + MediaHierarchyManager hierarchyManager, + MediaDataManager dataManager, + MediaHostStatesManager statesManager, + MediaCarouselController carouselController, + MediaCarouselControllerLogger logger) { + return new MediaHost( + stateHolder, + hierarchyManager, + dataManager, + statesManager, + carouselController, + logger); + } + + @Provides + @SysUISingleton + @Named(POPUP) + static MediaHost providesPopupMediaHost(MediaHost.MediaHostStateHolder stateHolder, MediaHierarchyManager hierarchyManager, MediaDataManager dataManager, MediaHostStatesManager statesManager, MediaCarouselController carouselController, MediaCarouselControllerLogger logger) { diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterLegacy.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterLegacy.java index ea4418427698..f8e57ef489aa 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterLegacy.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterLegacy.java @@ -36,6 +36,7 @@ import android.view.ViewGroup; import android.view.animation.LinearInterpolator; import android.widget.CheckBox; import android.widget.FrameLayout; +import android.widget.ImageButton; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.SeekBar; @@ -44,7 +45,6 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; -import androidx.core.widget.CompoundButtonCompat; import androidx.recyclerview.widget.RecyclerView; import com.android.media.flags.Flags; @@ -130,6 +130,7 @@ public class MediaOutputAdapterLegacy extends MediaOutputAdapterBase { final ViewGroup mContainerLayout; final FrameLayout mItemLayout; final FrameLayout mIconAreaLayout; + final ViewGroup mTextContent; final TextView mTitleText; final TextView mSubTitleText; final TextView mVolumeValueText; @@ -138,7 +139,7 @@ public class MediaOutputAdapterLegacy extends MediaOutputAdapterBase { final ImageView mStatusIcon; final CheckBox mCheckBox; final ViewGroup mEndTouchArea; - final ImageView mEndClickIcon; + final ImageButton mEndClickIcon; @VisibleForTesting MediaOutputSeekbar mSeekBar; private final float mInactiveRadius; @@ -152,6 +153,7 @@ public class MediaOutputAdapterLegacy extends MediaOutputAdapterBase { super(view, context); mContainerLayout = view.requireViewById(R.id.device_container); mItemLayout = view.requireViewById(R.id.item_layout); + mTextContent = view.requireViewById(R.id.text_content); mTitleText = view.requireViewById(R.id.title); mSubTitleText = view.requireViewById(R.id.subtitle); mTitleIcon = view.requireViewById(R.id.title_icon); @@ -160,7 +162,7 @@ public class MediaOutputAdapterLegacy extends MediaOutputAdapterBase { mStatusIcon = view.requireViewById(R.id.media_output_item_status); mCheckBox = view.requireViewById(R.id.check_box); mEndTouchArea = view.requireViewById(R.id.end_action_area); - mEndClickIcon = view.requireViewById(R.id.media_output_item_end_click_icon); + mEndClickIcon = view.requireViewById(R.id.end_area_image_button); mVolumeValueText = view.requireViewById(R.id.volume_value); mIconAreaLayout = view.requireViewById(R.id.icon_area); mInactiveRadius = mContext.getResources().getDimension( @@ -178,9 +180,7 @@ public class MediaOutputAdapterLegacy extends MediaOutputAdapterBase { mStatusIcon.setVisibility(View.GONE); mEndTouchArea.setVisibility(View.GONE); mEndClickIcon.setVisibility(View.GONE); - mEndTouchArea.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); mContainerLayout.setOnClickListener(null); - mContainerLayout.setContentDescription(null); mTitleText.setTextColor(mController.getColorItemContent()); mSubTitleText.setTextColor(mController.getColorItemContent()); mVolumeValueText.setTextColor(mController.getColorItemContent()); @@ -188,7 +188,7 @@ public class MediaOutputAdapterLegacy extends MediaOutputAdapterBase { updateIconAreaClickListener(null); mSeekBar.setProgressTintList( ColorStateList.valueOf(mController.getColorSeekbarProgress())); - enableFocusPropertyForView(mContainerLayout); + updateContainerContentA11yImportance(true /* isImportant */); renderItem(mediaItem, position); } @@ -256,10 +256,10 @@ public class MediaOutputAdapterLegacy extends MediaOutputAdapterBase { mSeekBar.setVisibility(showSeekBar ? View.VISIBLE : View.GONE); if (showSeekBar) { initSeekbar(device, isCurrentSeekbarInvisible); - disableFocusPropertyForView(mContainerLayout); + updateContainerContentA11yImportance(false /* isImportant */); mSeekBar.setContentDescription(contentDescription); } else { - enableFocusPropertyForView(mContainerLayout); + updateContainerContentA11yImportance(true /* isImportant */); } } @@ -268,18 +268,22 @@ public class MediaOutputAdapterLegacy extends MediaOutputAdapterBase { boolean isCurrentSeekbarInvisible = mSeekBar.getVisibility() == View.GONE; mSeekBar.setVisibility(View.VISIBLE); initGroupSeekbar(isCurrentSeekbarInvisible); - disableFocusPropertyForView(mContainerLayout); + updateContainerContentA11yImportance(false /* isImportant */); mSeekBar.setContentDescription(contentDescription); } - private void disableFocusPropertyForView(View view) { - view.setFocusable(false); - view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); - } - - private void enableFocusPropertyForView(View view) { - view.setFocusable(true); - view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO); + /** + * Sets the a11y importance for the device container and it's text content. Making the + * container not important for a11y is required when the seekbar is visible. + */ + private void updateContainerContentA11yImportance(boolean isImportant) { + mContainerLayout.setFocusable(isImportant); + mContainerLayout.setImportantForAccessibility( + isImportant ? View.IMPORTANT_FOR_ACCESSIBILITY_YES + : View.IMPORTANT_FOR_ACCESSIBILITY_NO); + mTextContent.setImportantForAccessibility( + isImportant ? View.IMPORTANT_FOR_ACCESSIBILITY_YES + : View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); } void updateSubtitle(@Nullable String subtitle) { @@ -576,26 +580,22 @@ public class MediaOutputAdapterLegacy extends MediaOutputAdapterBase { mEndClickIcon.setImageTintList( ColorStateList.valueOf(mController.getColorItemContent())); mEndClickIcon.setOnClickListener(clickListener); - mEndTouchArea.setOnClickListener(v -> mEndClickIcon.performClick()); Drawable drawable = mContext.getDrawable(iconDrawableId); mEndClickIcon.setImageDrawable(drawable); if (drawable instanceof AnimatedVectorDrawable) { ((AnimatedVectorDrawable) drawable).start(); } - if (Flags.enableOutputSwitcherDeviceGrouping()) { - mEndClickIcon.setContentDescription(mContext.getString(accessibilityStringId)); - } + mEndClickIcon.setContentDescription(mContext.getString(accessibilityStringId)); } private void updateEndAreaForGroupCheckBox(@NonNull MediaDevice device, @NonNull GroupStatus groupStatus) { boolean isEnabled = isGroupCheckboxEnabled(groupStatus); - mEndTouchArea.setOnClickListener( - isEnabled ? (v) -> mCheckBox.performClick() : null); - mEndTouchArea.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); updateEndAreaColor(groupStatus.selected() ? mController.getColorSeekbarProgress() : mController.getColorItemBackground()); - mEndTouchArea.setContentDescription(getDeviceItemContentDescription(device)); + mCheckBox.setContentDescription(mContext.getString( + groupStatus.selected() ? R.string.accessibility_remove_device_from_group + : R.string.accessibility_add_device_to_group)); mCheckBox.setOnCheckedChangeListener(null); mCheckBox.setChecked(groupStatus.selected()); mCheckBox.setOnCheckedChangeListener( @@ -606,10 +606,7 @@ public class MediaOutputAdapterLegacy extends MediaOutputAdapterBase { } private void setCheckBoxColor(CheckBox checkBox, int color) { - int[][] states = {{android.R.attr.state_checked}, {}}; - int[] colors = {color, color}; - CompoundButtonCompat.setButtonTintList(checkBox, new - ColorStateList(states, colors)); + checkBox.setForegroundTintList(ColorStateList.valueOf(color)); } private boolean shouldShowGroupCheckbox(@NonNull GroupStatus groupStatus) { diff --git a/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt b/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt index 0bf4c7e8d9f9..ea515c96d3f0 100644 --- a/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt +++ b/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt @@ -103,7 +103,7 @@ constructor( it.scene == Scenes.QuickSettings || Overlays.QuickSettingsShade in it.overlays }, - SYSUI_STATE_BOUNCER_SHOWING to { it.scene == Scenes.Bouncer }, + SYSUI_STATE_BOUNCER_SHOWING to { Overlays.Bouncer in it.overlays }, SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING to { it.scene == Scenes.Lockscreen && !it.invisibleDueToOcclusion diff --git a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt index c43c1a999fcb..18b4b7d2b5cf 100644 --- a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt @@ -22,7 +22,6 @@ import com.android.systemui.lifecycle.ExclusiveActivatable import com.android.systemui.lifecycle.Hydrator import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor import com.android.systemui.scene.domain.interactor.SceneInteractor -import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel import com.android.systemui.statusbar.disableflags.domain.interactor.DisableFlagsInteractor @@ -96,18 +95,6 @@ constructor( launch { hydrator.activate() } launch { - sceneInteractor.currentScene.collect { currentScene -> - when (currentScene) { - // TODO(b/369513770): The ShadeSession should be preserved in this scenario. - Scenes.Bouncer -> - shadeInteractor.collapseNotificationsShade( - loggingReason = "bouncer shown while shade is open" - ) - } - } - } - - launch { shadeInteractor.isShadeTouchable .distinctUntilChanged() .filter { !it } diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt index 5930a24e01a0..6ad8bae05d7a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt @@ -306,7 +306,6 @@ constructor( sceneState, viewModel.containerViewModel.editModeViewModel.isEditing, snapshotFlow { viewModel.expansionState }.map { it.progress }, - snapshotFlow { viewModel.isQSExpandingOrCollapsing }, ) } @@ -538,10 +537,6 @@ constructor( return qqsVisible.value } - override fun setQSExpandingOrCollapsing(isQSExpandingOrCollapsing: Boolean) { - viewModel.isQSExpandingOrCollapsing = isQSExpandingOrCollapsing - } - private fun setListenerCollections() { lifecycleScope.launch { lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { @@ -882,7 +877,6 @@ private suspend fun synchronizeQsState( state: MutableSceneTransitionLayoutState, editMode: Flow<Boolean>, expansion: Flow<Float>, - isQSExpandingOrCollapsing: Flow<Boolean>, ) { coroutineScope { val animationScope = this @@ -894,46 +888,31 @@ private suspend fun synchronizeQsState( currentTransition = null } - var lastValidProgress = 0f - combine(editMode, expansion, isQSExpandingOrCollapsing, ::Triple).collectLatest { - (editMode, progress, isQSExpandingOrCollapsing) -> + editMode.combine(expansion, ::Pair).collectLatest { (editMode, progress) -> if (editMode && state.currentScene != SceneKeys.EditMode) { state.setTargetScene(SceneKeys.EditMode, animationScope)?.second?.join() } else if (!editMode && state.currentScene == SceneKeys.EditMode) { state.setTargetScene(SceneKeys.QuickSettings, animationScope)?.second?.join() } - if (!editMode) { - if (!isQSExpandingOrCollapsing) { - if (progress == 0f) { - snapTo(QuickQuickSettings) - return@collectLatest - } + when (progress) { + 0f -> snapTo(QuickQuickSettings) + 1f -> snapTo(QuickSettings) + else -> { + val transition = currentTransition + if (transition != null) { + transition.progress = progress + return@collectLatest + } - if (progress == 1f) { - snapTo(QuickSettings) - return@collectLatest + val newTransition = + ExpansionTransition(progress).also { currentTransition = it } + state.startTransitionImmediately( + animationScope = animationScope, + transition = newTransition, + ) } } - - var progress = progress - if (progress >= 0f || progress <= 1f) { - lastValidProgress = progress - } else { - progress = lastValidProgress - } - - val transition = currentTransition - if (transition != null) { - transition.progress = progress - return@collectLatest - } - - val newTransition = ExpansionTransition(progress).also { currentTransition = it } - state.startTransitionImmediately( - animationScope = animationScope, - transition = newTransition, - ) } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt index b829bbce2f18..767acc5fa76a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt @@ -35,6 +35,7 @@ import com.android.systemui.animation.ShadeInterpolation import com.android.systemui.classifier.Classifier import com.android.systemui.classifier.domain.interactor.FalsingInteractor import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor +import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.Edge @@ -60,7 +61,7 @@ import com.android.systemui.qs.panels.ui.viewmodel.MediaInRowInLandscapeViewMode import com.android.systemui.qs.panels.ui.viewmodel.QuickQuickSettingsViewModel import com.android.systemui.qs.ui.viewmodel.QuickSettingsContainerViewModel import com.android.systemui.res.R -import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.shade.LargeScreenHeaderHelper import com.android.systemui.shade.ShadeDisplayAware import com.android.systemui.shade.domain.interactor.ShadeInteractor @@ -306,8 +307,6 @@ constructor( val animateTilesExpansion: Boolean get() = inFirstPage && !mediaSuddenlyAppearingInLandscape - var isQSExpandingOrCollapsing by mutableStateOf(false) - private val inFirstPage: Boolean get() = inFirstPageViewModel.inFirstPage @@ -434,7 +433,7 @@ constructor( initialValue = false, source = keyguardTransitionInteractor.isInTransition( - Edge.create(to = Scenes.Bouncer), + Edge.create(to = Overlays.Bouncer), Edge.create(to = KeyguardState.PRIMARY_BOUNCER), ), ) @@ -541,7 +540,6 @@ constructor( println("proposedTranslation", proposedTranslation) println("expansionState", expansionState) println("forceQS", forceQs) - println("isShadeExpandingOrCollapsing", isQSExpandingOrCollapsing) printSection("Derived values") { println("headerTranslation", headerTranslation) println("translationScaleY", translationScaleY) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java index 30c2adf89e9b..c60e3da9d833 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java @@ -54,6 +54,7 @@ import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.res.R; +import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor; import com.android.systemui.statusbar.connectivity.NetworkController; import com.android.systemui.statusbar.connectivity.SignalCallback; import com.android.systemui.statusbar.connectivity.WifiIndicators; @@ -89,6 +90,7 @@ public class CastTile extends QSTileImpl<BooleanState> { private final Callback mCallback = new Callback(); private final TileJavaAdapter mJavaAdapter; private final FeatureFlags mFeatureFlags; + private final ShadeDialogContextInteractor mShadeDialogContextInteractor; private boolean mCastTransportAllowed; private boolean mHotspotConnected; @@ -110,7 +112,8 @@ public class CastTile extends QSTileImpl<BooleanState> { DialogTransitionAnimator dialogTransitionAnimator, ConnectivityRepository connectivityRepository, TileJavaAdapter javaAdapter, - FeatureFlags featureFlags + FeatureFlags featureFlags, + ShadeDialogContextInteractor shadeDialogContextInteractor ) { super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); @@ -120,6 +123,7 @@ public class CastTile extends QSTileImpl<BooleanState> { mDialogTransitionAnimator = dialogTransitionAnimator; mJavaAdapter = javaAdapter; mFeatureFlags = featureFlags; + mShadeDialogContextInteractor = shadeDialogContextInteractor; mController.observe(this, mCallback); mKeyguard.observe(this, mCallback); if (!mFeatureFlags.isEnabled(SIGNAL_CALLBACK_DEPRECATION)) { @@ -220,7 +224,7 @@ public class CastTile extends QSTileImpl<BooleanState> { mUiHandler.post(() -> { final DialogHolder holder = new DialogHolder(); final Dialog dialog = MediaRouteDialogPresenter.createDialog( - mContext, + mShadeDialogContextInteractor.getContext(), ROUTE_TYPE_REMOTE_DISPLAY, v -> { ActivityTransitionAnimator.Controller controller = diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt index 0add3f515ebf..f75dadd5ef0f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt @@ -18,7 +18,6 @@ package com.android.systemui.qs.ui.viewmodel import com.android.systemui.lifecycle.ExclusiveActivatable import com.android.systemui.scene.domain.interactor.SceneInteractor -import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape @@ -47,18 +46,6 @@ constructor( override suspend fun onActivated(): Nothing { coroutineScope { launch { - sceneInteractor.currentScene.collect { currentScene -> - when (currentScene) { - // TODO(b/369513770): The ShadeSession should be preserved in this scenario. - Scenes.Bouncer -> - shadeInteractor.collapseQuickSettingsShade( - loggingReason = "bouncer shown while shade is open" - ) - } - } - } - - launch { shadeInteractor.isShadeTouchable .distinctUntilChanged() .filter { !it } diff --git a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt index ea11d202b119..2dc4a8d53920 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt @@ -39,7 +39,7 @@ import dagger.multibindings.IntoMap @Module( includes = [ - BouncerSceneModule::class, + BouncerOverlayModule::class, CommunalSceneModule::class, DreamSceneModule::class, EmptySceneModule::class, @@ -98,13 +98,16 @@ interface SceneContainerFrameworkModule { Scenes.Communal, Scenes.Dream, Scenes.Lockscreen, - Scenes.Bouncer, Scenes.QuickSettings, Scenes.Shade, ), initialSceneKey = Scenes.Lockscreen, overlayKeys = - listOfNotNull(Overlays.NotificationsShade, Overlays.QuickSettingsShade), + listOfNotNull( + Overlays.NotificationsShade, + Overlays.QuickSettingsShade, + Overlays.Bouncer, + ), navigationDistances = mapOf( Scenes.Gone to 0, @@ -113,7 +116,6 @@ interface SceneContainerFrameworkModule { Scenes.Dream to 2, Scenes.Shade to 3, Scenes.QuickSettings to 4, - Scenes.Bouncer to 5, ), transitionsBuilder = SceneContainerTransitions(), ) diff --git a/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt index 8845bb7ceeb9..a982f70d80e2 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt @@ -18,6 +18,7 @@ package com.android.systemui.scene import com.android.systemui.scene.domain.SceneDomainModule import com.android.systemui.scene.domain.resolver.HomeSceneFamilyResolverModule +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.SceneContainerConfig import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.ui.composable.SceneContainerTransitions @@ -28,7 +29,7 @@ import dagger.Provides @Module( includes = [ - BouncerSceneModule::class, + BouncerOverlayModule::class, EmptySceneModule::class, GoneSceneModule::class, LockscreenSceneModule::class, @@ -47,11 +48,10 @@ object ShadelessSceneContainerFrameworkModule { return SceneContainerConfig( // Note that this list is in z-order. The first one is the bottom-most and the last one // is top-most. - sceneKeys = listOf(Scenes.Gone, Scenes.Lockscreen, Scenes.Bouncer), + sceneKeys = listOf(Scenes.Gone, Scenes.Lockscreen), initialSceneKey = Scenes.Lockscreen, - overlayKeys = emptyList(), - navigationDistances = - mapOf(Scenes.Gone to 0, Scenes.Lockscreen to 0, Scenes.Bouncer to 1), + overlayKeys = listOf(Overlays.Bouncer), + navigationDistances = mapOf(Scenes.Gone to 0, Scenes.Lockscreen to 0), transitionsBuilder = SceneContainerTransitions(), ) } diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt index c96ea03f057a..64abb843fad8 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt @@ -136,7 +136,7 @@ constructor( when (this) { Overlays.NotificationsShade -> false Overlays.QuickSettingsShade -> false - Scenes.Bouncer -> false + Overlays.Bouncer -> false Scenes.Communal -> true Scenes.Dream -> false Scenes.Gone -> true diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt index 01180859b1d2..07f13cbdd0e7 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt @@ -42,6 +42,7 @@ import com.android.systemui.util.kotlin.pairwise import dagger.Lazy import javax.inject.Inject import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted @@ -203,20 +204,30 @@ constructor( repository.isSceneContainerUserInputOngoing /** - * The amount of transition into or out of the given [scene]. + * The amount of transition into or out of the given [content]. * * The value will be `0` if not in this scene or `1` when fully in the given scene. */ - fun transitionProgress(scene: SceneKey): Flow<Float> { + @OptIn(ExperimentalCoroutinesApi::class) + fun transitionProgress(content: ContentKey): Flow<Float> { return transitionState.flatMapLatest { transition -> when (transition) { is ObservableTransitionState.Idle -> { - flowOf(if (transition.currentScene == scene) 1f else 0f) + flowOf( + if ( + transition.currentScene == content || + content in transition.currentOverlays + ) { + 1f + } else { + 0f + } + ) } is ObservableTransitionState.Transition -> { when { - transition.toContent == scene -> transition.progress - transition.fromContent == scene -> transition.progress.map { 1f - it } + transition.toContent == content -> transition.progress + transition.fromContent == content -> transition.progress.map { 1f - it } else -> flowOf(0f) } } @@ -238,6 +249,9 @@ constructor( * If [forceSettleToTargetScene] is `true` and the target scene is the same as the current * scene, any current transition will be canceled and an animation to the target scene will be * started. + * + * If [Overlays.Bouncer] is showing, we trigger an instant scene change as it will not be user- + * visible, and trigger a transition to hide the bouncer. */ @JvmOverloads fun changeScene( @@ -249,6 +263,7 @@ constructor( ) { val currentSceneKey = currentScene.value val resolvedScene = sceneFamilyResolvers.get()[toScene]?.resolvedScene?.value ?: toScene + val bouncerShowing = Overlays.Bouncer in currentOverlays.value if (resolvedScene == currentSceneKey && forceSettleToTargetScene) { logger.logSceneChangeCancellation(scene = resolvedScene, sceneState = sceneState) @@ -265,6 +280,12 @@ constructor( loggingReason = loggingReason, ) ) { + if (bouncerShowing) { + hideOverlay( + Overlays.Bouncer, + "Scene change cancelled but hiding bouncer for: ($loggingReason)", + ) + } return } @@ -278,14 +299,20 @@ constructor( isInstant = false, ) - repository.changeScene(resolvedScene, transitionKey) + if (bouncerShowing) { + repository.snapToScene(resolvedScene) + hideOverlay(Overlays.Bouncer, "Hiding on changeScene for: ($loggingReason)") + } else { + repository.changeScene(resolvedScene, transitionKey) + } } /** * Requests a scene change to the given scene. * * The change is instantaneous and not animated; it will be observable in the next frame and - * there will be no transition animation. + * there will be no transition animation. If [Overlays.Bouncer] is showing, it will instantly be + * hidden. */ fun snapToScene(toScene: SceneKey, loggingReason: String) { val currentSceneKey = currentScene.value @@ -316,6 +343,7 @@ constructor( ) repository.snapToScene(resolvedScene) + instantlyHideOverlay(Overlays.Bouncer, "Hiding on snapToScene for: ($loggingReason)") } /** @@ -662,6 +690,16 @@ constructor( false } + to == Overlays.Bouncer && currentScene.value == Scenes.Gone -> { + logger.logSceneChangeRejection( + from = from, + to = to, + originalChangeReason = loggingReason, + rejectionReason = "Cannot show Bouncer over Gone scene", + ) + false + } + else -> true } } @@ -767,4 +805,41 @@ constructor( ) } } + + /** + * Based off of the ordering of [allContentKeys], returns the key of the highest z-order content + * out of [content]. + */ + private fun determineTopmostContent(content: Set<ContentKey>): ContentKey { + // Assuming allContentKeys is sorted by ascending z-order. + return checkNotNull(allContentKeys.findLast { it in content }) { + "Could not find unknown content $content in allContentKeys $allContentKeys" + } + } + + /** Optimization for common case where overlays is empty. */ + private fun determineTopmostContent(scene: SceneKey, overlays: Set<OverlayKey>): ContentKey { + return if (overlays.isEmpty()) { + scene + } else { + determineTopmostContent(overlays) + } + } + + /** + * The current content that has the highest z-order out of all currently shown scenes and + * overlays. + * + * Note that during a transition between content, a different content may have the highest z- + * order. Only the one provided by this flow is considered the current logical topmost content. + */ + @Deprecated("Only to be used for compatibility with KeyguardTransitionFramework") + val topmostContent: StateFlow<ContentKey> = + combine(currentScene, currentOverlays, ::determineTopmostContent) + .stateInTraced( + name = "topmostContent", + scope = applicationScope, + started = SharingStarted.Eagerly, + initialValue = determineTopmostContent(currentScene.value, currentOverlays.value), + ) } diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt index 79226138d07a..68ce26954aa3 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt @@ -87,7 +87,7 @@ constructor( ) is ObservableTransitionState.Transition -> if ( - state.fromContent == Scenes.Bouncer && + state.fromContent == Overlays.Bouncer && state.toContent == Scenes.Lockscreen ) { // Lockscreen is not visible during preview stage of predictive back diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt index 218ad477c45e..4753b9ac0457 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt @@ -18,6 +18,7 @@ package com.android.systemui.scene.domain.startable import android.app.StatusBarManager import com.android.compose.animation.scene.ObservableTransitionState +import com.android.compose.animation.scene.OverlayKey import com.android.compose.animation.scene.SceneKey import com.android.internal.logging.UiEventLogger import com.android.keyguard.AuthInteractionProperties @@ -45,7 +46,7 @@ import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.domain.interactor.KeyguardEnabledInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.TrustInteractor -import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor.Companion.keyguardScenes +import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor.Companion.keyguardContent import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.model.SceneContainerPlugin import com.android.systemui.model.SysUiState @@ -98,7 +99,6 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChangedBy import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterIsInstance -import kotlinx.coroutines.flow.filterNot import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf @@ -345,12 +345,10 @@ constructor( applicationScope.launch { // TODO (b/308001302): Move this to a bouncer specific interactor. bouncerInteractor.onImeHiddenByUser.collectLatest { - if (sceneInteractor.currentScene.value == Scenes.Bouncer) { - sceneInteractor.changeScene( - toScene = Scenes.Lockscreen, - loggingReason = "IME hidden", - ) - } + sceneInteractor.hideOverlay( + overlay = Overlays.Bouncer, + loggingReason = "IME hidden.", + ) } } } @@ -364,26 +362,28 @@ constructor( .collect { (isAnySimLocked, unlockStatus) -> when { isAnySimLocked -> { - switchToScene( - targetSceneKey = Scenes.Bouncer, + sceneInteractor.showOverlay( + overlay = Overlays.Bouncer, loggingReason = "Need to authenticate locked SIM card.", ) } unlockStatus.isUnlocked && deviceEntryInteractor.canSwipeToEnter.value == false -> { + val loggingReason = + "All SIM cards unlocked and device already unlocked and " + + "lockscreen doesn't require a swipe to dismiss." switchToScene( targetSceneKey = Scenes.Gone, - loggingReason = - "All SIM cards unlocked and device already unlocked and " + - "lockscreen doesn't require a swipe to dismiss.", + loggingReason = loggingReason, ) } else -> { + val loggingReason = + "All SIM cards unlocked and device still locked" + + " or lockscreen still requires a swipe to dismiss." switchToScene( targetSceneKey = Scenes.Lockscreen, - loggingReason = - "All SIM cards unlocked and device still locked" + - " or lockscreen still requires a swipe to dismiss.", + loggingReason = loggingReason, ) } } @@ -393,26 +393,40 @@ constructor( private fun handleDeviceUnlockStatus() { applicationScope.launch { - // Track the previous scene (sans Bouncer), so that we know where to go when the device - // is unlocked whilst on the bouncer. + // Track the previous scene, so that we know where to go when the device is unlocked + // whilst on the bouncer. val previousScene = - sceneBackInteractor.backScene - .filterNot { it == Scenes.Bouncer } - .stateIn(this, SharingStarted.Eagerly, initialValue = null) + sceneBackInteractor.backScene.stateIn( + this, + SharingStarted.Eagerly, + initialValue = null, + ) deviceUnlockedInteractor.deviceUnlockStatus .mapNotNull { deviceUnlockStatus -> - val renderedScenes = + val (renderedScenes: List<SceneKey>, renderedOverlays) = when (val transitionState = sceneInteractor.transitionState.value) { - is ObservableTransitionState.Idle -> setOf(transitionState.currentScene) - is ObservableTransitionState.Transition -> - setOf(transitionState.fromContent, transitionState.toContent) + is ObservableTransitionState.Idle -> + listOf(transitionState.currentScene) to + transitionState.currentOverlays + is ObservableTransitionState.Transition.ChangeScene -> + listOf(transitionState.fromScene, transitionState.toScene) to + transitionState.currentOverlays + is ObservableTransitionState.Transition.OverlayTransition -> + listOf(transitionState.currentScene) to + setOfNotNull( + transitionState.toContent.takeIf { it is OverlayKey }, + transitionState.fromContent.takeIf { it is OverlayKey }, + ) } val isOnLockscreen = renderedScenes.contains(Scenes.Lockscreen) val isAlternateBouncerVisible = alternateBouncerInteractor.isVisibleState() - val isOnPrimaryBouncer = renderedScenes.contains(Scenes.Bouncer) + val isOnPrimaryBouncer = Overlays.Bouncer in renderedOverlays if (!deviceUnlockStatus.isUnlocked) { - return@mapNotNull if (renderedScenes.any { it in keyguardScenes }) { - // Already on a keyguard scene, no need to change scenes. + return@mapNotNull if ( + renderedScenes.any { it in keyguardContent } || + Overlays.Bouncer in renderedOverlays + ) { + // Already on a keyguard scene or bouncer, no need to change scenes. null } else { // The device locked while on a scene that's not a keyguard scene, go @@ -450,24 +464,25 @@ constructor( } isOnPrimaryBouncer -> { // When the device becomes unlocked in primary Bouncer, - // notify dismiss succeeded and go to previous scene or Gone. + // notify dismiss succeeded and remain in current scene or switch to + // Gone. dismissCallbackRegistry.notifyDismissSucceeded() + // if transition is a scene change, take the destination scene + val targetScene = renderedScenes.last() if ( - previousScene.value == Scenes.Lockscreen || + targetScene == Scenes.Lockscreen || !statusBarStateController.leaveOpenOnKeyguardHide() ) { Scenes.Gone to "device was unlocked with bouncer showing and shade" + " didn't need to be left open" } else { - val prevScene = previousScene.value - val targetScene = prevScene ?: Scenes.Gone - if (targetScene != Scenes.Gone) { + if (previousScene.value != Scenes.Gone) { replaceLockscreenSceneOnBackStack() } targetScene to "device was unlocked with primary bouncer showing," + - " from sceneKey=$prevScene" + " from sceneKey=$targetScene" } } isOnLockscreen -> @@ -575,8 +590,8 @@ constructor( authenticationInteractor.get().getAuthenticationMethod() == AuthenticationMethodModel.Sim ) { - switchToScene( - targetSceneKey = Scenes.Bouncer, + sceneInteractor.showOverlay( + overlay = Overlays.Bouncer, loggingReason = "device is starting to wake up with a locked sim", ) } @@ -791,11 +806,11 @@ constructor( } applicationScope.launch { - sceneInteractor.currentScene - .map { it == Scenes.Bouncer } + sceneInteractor.currentOverlays + .map { Overlays.Bouncer in it } .distinctUntilChanged() - .collect { switchedToBouncerScene -> - if (switchedToBouncerScene) { + .collect { switchedToBouncerOverlay -> + if (switchedToBouncerOverlay) { falsingCollector.onBouncerShown() } else { falsingCollector.onBouncerHidden() @@ -812,7 +827,10 @@ constructor( falsingManager.addFalsingBeliefListener(listener) awaitClose { falsingManager.removeFalsingBeliefListener(listener) } } - .collect { switchToScene(Scenes.Lockscreen, "Falsing detected.") } + .collect { + val loggingReason = "Falsing detected." + switchToScene(Scenes.Lockscreen, loggingReason) + } } } @@ -827,7 +845,7 @@ constructor( .mapNotNull { it as? ObservableTransitionState.Idle } .map { it.currentScene to it.currentOverlays } .distinctUntilChanged() - .map { (sceneKey, currentOverlays) -> + .map { (currentScene, currentOverlays) -> when { // When locked, showing the lockscreen scene should be reported // as "interacting" while showing other scenes should report as @@ -837,9 +855,9 @@ constructor( // implementation. The real reason why is lost to lore and myth. Overlays.NotificationsShade in currentOverlays -> false Overlays.QuickSettingsShade in currentOverlays -> null - sceneKey == Scenes.Lockscreen -> true - sceneKey == Scenes.Bouncer -> false - sceneKey == Scenes.Shade -> false + Overlays.Bouncer in currentOverlays -> false + currentScene == Scenes.Lockscreen -> true + currentScene == Scenes.Shade -> false else -> null } } @@ -865,7 +883,7 @@ constructor( .filterIsInstance<ObservableTransitionState.Transition>() // Only consider user-initiated (e.g. drags) that go from bouncer to lockscreen. .filter { transition -> - transition.fromContent == Scenes.Bouncer && + transition.fromContent == Overlays.Bouncer && transition.toContent == Scenes.Lockscreen && transition.isInitiatedByUserInput } @@ -954,14 +972,13 @@ constructor( private fun notifyKeyguardDismissCancelledCallbacks() { applicationScope.launch { - combine(deviceEntryInteractor.isUnlocked, sceneInteractor.currentScene.pairwise()) { + combine(deviceEntryInteractor.isUnlocked, sceneInteractor.currentOverlays.pairwise()) { isUnlocked, - (from, to) -> - when { - from != Scenes.Bouncer -> false - to != Scenes.Gone && !isUnlocked -> true - else -> false - } + overlayChange -> + val difference = overlayChange.previousValue - overlayChange.newValue + !isUnlocked && + sceneInteractor.currentScene.value != Scenes.Gone && + Overlays.Bouncer in difference } .collect { notifyKeyguardDismissCancelled -> if (notifyKeyguardDismissCancelled) { diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt index 522dfabb8b61..305dcfb6a528 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt @@ -17,6 +17,7 @@ package com.android.systemui.scene.domain.startable import androidx.annotation.VisibleForTesting +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.compose.animation.scene.ContentKey import com.android.compose.animation.scene.ObservableTransitionState import com.android.compose.animation.scene.OverlayKey @@ -49,7 +50,6 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach -import com.android.app.tracing.coroutines.launchTraced as launch @SysUISingleton class ScrimStartable @@ -108,12 +108,12 @@ constructor( // This is true when the lockscreen scene is either the current scene or somewhere // in the navigation back stack of scenes. val isOnKeyguard = !isDeviceEntered - val isCurrentSceneBouncer = currentScene == Scenes.Bouncer - // This is true when moving away from one of the keyguard scenes to the gone scene. + val isOnBouncer = Overlays.Bouncer in currentOverlays + // This is true when moving away from the lockscreen scene to the gone scene. // It happens only when unlocking or when dismissing a dismissible lockscreen. val isTransitioningAwayFromKeyguard = transitionState is ObservableTransitionState.Transition.ChangeScene && - transitionState.fromScene.isKeyguard() && + transitionState.fromScene == Scenes.Lockscreen && transitionState.toScene == Scenes.Gone // This is true when any of the shade scenes or overlays is the current content. @@ -144,7 +144,7 @@ constructor( // Assume scrim state for shade is already correct and do nothing null } - } else if (isCurrentSceneBouncer && !unlocking) { + } else if (isOnBouncer && !unlocking) { // Bouncer needs the front scrim when it's on top of an activity, tapping on a // notification, editing QS or being dismissed by // FLAG_DISMISS_KEYGUARD_ACTIVITY. @@ -215,10 +215,6 @@ constructor( } } - private fun SceneKey.isKeyguard(): Boolean { - return this == Scenes.Lockscreen || this == Scenes.Bouncer - } - private fun ContentKey.isShade(): Boolean { return this == Scenes.Shade || this == Scenes.QuickSettings || diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/StatusBarStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/StatusBarStartable.kt index 7d09c457e8c1..4c8926a7bad0 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/StatusBarStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/StatusBarStartable.kt @@ -24,6 +24,8 @@ import android.os.IBinder import android.os.RemoteException import android.provider.DeviceConfig import android.util.Log +import com.android.app.tracing.coroutines.launchTraced as launch +import com.android.compose.animation.scene.OverlayKey import com.android.compose.animation.scene.SceneKey import com.android.internal.config.sysui.SystemUiDeviceConfigFlags import com.android.internal.statusbar.IStatusBarService @@ -43,14 +45,13 @@ import com.android.systemui.power.shared.model.WakefulnessModel import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag -import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.user.domain.interactor.SelectedUserInteractor import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged -import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext @SysUISingleton @@ -93,6 +94,7 @@ constructor( navigationInteractor.isGesturalMode, authenticationInteractor.authenticationMethod, powerInteractor.detailedWakefulness, + sceneInteractor.currentOverlays, ) { values -> val selectedUserId = values[0] as Int val currentScene = values[1] as SceneKey @@ -102,8 +104,9 @@ constructor( val isGesturalMode = values[5] as Boolean val authenticationMethod = values[6] as AuthenticationMethodModel val wakefulnessModel = values[7] as WakefulnessModel + val overlays = values[8] as Set<OverlayKey> - val isForceHideHomeAndRecents = currentScene == Scenes.Bouncer + val isForceHideHomeAndRecents = Overlays.Bouncer in overlays val isKeyguardShowing = !isDeviceEntered val isPowerGestureIntercepted = with(wakefulnessModel) { diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Overlays.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Overlays.kt index c47a85082032..238de4af0d1e 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Overlays.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Overlays.kt @@ -25,6 +25,12 @@ import com.android.compose.animation.scene.OverlayKey */ object Overlays { /** + * The bouncer is the overlay that displays authentication challenges like PIN, password, or + * pattern. + */ + @JvmField val Bouncer = OverlayKey("bouncer") + + /** * The notifications shade overlay primarily shows a scrollable list of notifications. * * It's used only in the dual shade configuration, where there are two separate shades: one for diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scenes.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scenes.kt index 16492efa658a..8332522dd129 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scenes.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scenes.kt @@ -24,12 +24,6 @@ import com.android.compose.animation.scene.SceneKey * PLEASE KEEP THE KEYS SORTED ALPHABETICALLY. */ object Scenes { - /** - * The bouncer is the scene that displays authentication challenges like PIN, password, or - * pattern. - */ - @JvmField val Bouncer = SceneKey("bouncer") - /** The communal scene shows the glanceable hub when device is locked and docked. */ @JvmField val Communal = SceneKey("communal") diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneJankMonitor.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneJankMonitor.kt index 48a49c60d8a2..48a3dede5def 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneJankMonitor.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneJankMonitor.kt @@ -27,7 +27,7 @@ import com.android.systemui.authentication.shared.model.AuthenticationMethodMode import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor import com.android.systemui.lifecycle.ExclusiveActivatable import com.android.systemui.lifecycle.Hydrator -import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.scene.shared.model.Overlays import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -95,7 +95,7 @@ constructor( private fun calculatedCuj(from: ContentKey, to: ContentKey): Int? { val isDeviceUnlocked = deviceUnlockedInteractor.deviceUnlockStatus.value.isUnlocked return when { - to == Scenes.Bouncer -> + to == Overlays.Bouncer -> when (authMethod) { is AuthenticationMethodModel.Pin, is AuthenticationMethodModel.Sim -> Cuj.CUJ_LOCKSCREEN_PIN_APPEAR @@ -104,7 +104,7 @@ constructor( is AuthenticationMethodModel.None -> null null -> null } - from == Scenes.Bouncer && isDeviceUnlocked -> + from == Overlays.Bouncer && isDeviceUnlocked -> when (authMethod) { is AuthenticationMethodModel.Pin, is AuthenticationMethodModel.Sim -> Cuj.CUJ_LOCKSCREEN_PIN_DISAPPEAR diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt index efdf5bee2c7b..a81fcec94989 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt @@ -318,7 +318,7 @@ constructor( private fun isInteractionAllowedByFalsing(content: ContentKey): Boolean { val interactionTypeOrNull = when (content) { - Scenes.Bouncer -> Classifier.BOUNCER_UNLOCK + Overlays.Bouncer -> Classifier.BOUNCER_UNLOCK Scenes.Gone -> Classifier.UNLOCK Scenes.Shade, Overlays.NotificationsShade -> Classifier.NOTIFICATION_DRAG_DOWN diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java index 34b3324f81da..d05837261b89 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java @@ -105,14 +105,15 @@ import com.android.systemui.util.kotlin.JavaAdapter; import dalvik.annotation.optimization.NeverCompile; +import dagger.Lazy; + +import kotlin.Unit; + import java.io.PrintWriter; import javax.inject.Inject; import javax.inject.Provider; -import dagger.Lazy; -import kotlin.Unit; - /** Handles QuickSettings touch handling, expansion and animation state. */ @SysUISingleton public class QuickSettingsControllerImpl implements QuickSettingsController, Dumpable { @@ -2365,16 +2366,8 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum return; } if (startTracing) { - if (mQs != null) { - mQs.setQSExpandingOrCollapsing(true); - } - monitor.begin(mPanelView, Cuj.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE); } else { - if (mQs != null) { - mQs.setQSExpandingOrCollapsing(false); - } - if (wasCancelled) { monitor.cancel(Cuj.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE); } else { diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModel.kt index 9655d928a9b9..a3bfbbc8eb1a 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModel.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModel.kt @@ -28,6 +28,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.util.kotlin.BooleanFlowOperators.any import com.android.systemui.util.kotlin.sample @@ -92,7 +93,7 @@ constructor( val isBouncerShowing: Flow<Boolean> = when { SceneContainerFlag.isEnabled -> { - sceneInteractor.get().transitionState.map { it.isIdle(Scenes.Bouncer) } + sceneInteractor.get().transitionState.map { it.isIdle(Overlays.Bouncer) } } ComposeBouncerFlags.isOnlyComposeBouncerEnabled() -> primaryBouncerInteractor.isShowing else -> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt index 3db004848d22..84266e805773 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt @@ -38,13 +38,13 @@ import com.android.systemui.Flags import com.android.systemui.Flags.spatialModelAppPushback import com.android.systemui.animation.ShadeInterpolation import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.shade.ShadeDisplayAware import com.android.systemui.shade.ShadeExpansionChangeEvent import com.android.systemui.shade.ShadeExpansionListener -import com.android.systemui.shared.Flags.ambientAod import com.android.systemui.statusbar.phone.BiometricUnlockController import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK import com.android.systemui.statusbar.phone.DozeParameters @@ -53,8 +53,11 @@ import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.statusbar.policy.SplitShadeStateController import com.android.systemui.util.WallpaperController +import com.android.systemui.wallpapers.domain.interactor.WallpaperInteractor import com.android.systemui.window.domain.interactor.WindowRootViewBlurInteractor import com.android.wm.shell.appzoomout.AppZoomOut +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch import java.io.PrintWriter import java.util.Optional import javax.inject.Inject @@ -76,12 +79,14 @@ constructor( private val keyguardInteractor: KeyguardInteractor, private val choreographer: Choreographer, private val wallpaperController: WallpaperController, + private val wallpaperInteractor: WallpaperInteractor, private val notificationShadeWindowController: NotificationShadeWindowController, private val dozeParameters: DozeParameters, @ShadeDisplayAware private val context: Context, private val splitShadeStateController: SplitShadeStateController, private val windowRootViewBlurInteractor: WindowRootViewBlurInteractor, private val appZoomOutOptional: Optional<AppZoomOut>, + @Application private val applicationScope: CoroutineScope, dumpManager: DumpManager, configurationController: ConfigurationController, ) : ShadeExpansionListener, Dumpable { @@ -110,6 +115,8 @@ constructor( private var prevTimestamp: Long = -1 private var prevShadeDirection = 0 private var prevShadeVelocity = 0f + private var prevDozeAmount: Float = 0f + @VisibleForTesting var wallpaperSupportsAmbientMode: Boolean = false // tracks whether app launch transition is in progress. This involves two independent factors // that control blur, shade expansion and app launch animation from outside sysui. // They can complete out of order, this flag will be reset by the animation that finishes later. @@ -219,7 +226,15 @@ constructor( } /** Blur radius of the wake-up animation on this frame. */ - private var wakeAndUnlockBlurRadius = 0f + private var wakeBlurRadius = 0f + set(value) { + if (field == value) return + field = value + scheduleUpdate() + } + + /** Blur radius of the unlock animation on this frame. */ + private var unlockBlurRadius = 0f set(value) { if (field == value) return field = value @@ -246,14 +261,16 @@ constructor( ShadeInterpolation.getNotificationScrimAlpha(qsPanelExpansion) * shadeExpansion combinedBlur = max(combinedBlur, blurUtils.blurRadiusOfRatio(qsExpandedRatio)) combinedBlur = max(combinedBlur, blurUtils.blurRadiusOfRatio(transitionToFullShadeProgress)) - var shadeRadius = max(combinedBlur, wakeAndUnlockBlurRadius) + var shadeRadius = max(combinedBlur, max(wakeBlurRadius, unlockBlurRadius)) if (areBlursDisabledForAppLaunch || blursDisabledForUnlock) { shadeRadius = 0f } var blur = shadeRadius.toInt() - val zoomOut = blurRadiusToZoomOut(blurRadius = shadeRadius) + // If the blur comes from waking up, we don't want to zoom out the background + val zoomOut = + if (shadeRadius != wakeBlurRadius) blurRadiusToZoomOut(blurRadius = shadeRadius) else 0f // Make blur be 0 if it is necessary to stop blur effect. if (scrimsVisible) { if (!Flags.notificationShadeBlur()) { @@ -338,14 +355,14 @@ constructor( startDelay = keyguardStateController.keyguardFadingAwayDelay interpolator = Interpolators.FAST_OUT_SLOW_IN addUpdateListener { animation: ValueAnimator -> - wakeAndUnlockBlurRadius = + unlockBlurRadius = blurUtils.blurRadiusOfRatio(animation.animatedValue as Float) } addListener( object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { keyguardAnimator = null - wakeAndUnlockBlurRadius = 0f + unlockBlurRadius = 0f } } ) @@ -381,15 +398,20 @@ constructor( } override fun onDozeAmountChanged(linear: Float, eased: Float) { - wakeAndUnlockBlurRadius = - if (ambientAod()) { - 0f - } else { - blurUtils.blurRadiusOfRatio(eased) - } + prevDozeAmount = eased + updateWakeBlurRadius(prevDozeAmount) } } + private fun updateWakeBlurRadius(ratio: Float) { + wakeBlurRadius = + if (!wallpaperSupportsAmbientMode) { + 0f + } else { + blurUtils.blurRadiusOfRatio(ratio) + } + } + init { dumpManager.registerCriticalDumpable(javaClass.name, this) if (WAKE_UP_ANIMATION_ENABLED) { @@ -411,6 +433,12 @@ constructor( } } ) + applicationScope.launch { + wallpaperInteractor.wallpaperSupportsAmbientMode.collect { supported -> + wallpaperSupportsAmbientMode = supported + updateWakeBlurRadius(prevDozeAmount) + } + } initBlurListeners() } @@ -585,7 +613,8 @@ constructor( it.println("shouldApplyShadeBlur: ${shouldApplyShadeBlur()}") it.println("shadeAnimation: ${shadeAnimation.radius}") it.println("brightnessMirrorRadius: ${brightnessMirrorSpring.radius}") - it.println("wakeAndUnlockBlur: $wakeAndUnlockBlurRadius") + it.println("wakeBlur: $wakeBlurRadius") + it.println("unlockBlur: $wakeBlurRadius") it.println("blursDisabledForAppLaunch: $blursDisabledForAppLaunch") it.println("appLaunchTransitionIsInProgress: $appLaunchTransitionIsInProgress") it.println("qsPanelExpansion: $qsPanelExpansion") diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java index 0dfc63ea8619..08ecc64bda02 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java @@ -627,7 +627,6 @@ public class StatusBarStateControllerImpl implements boolean alternateBouncerIsVisible) { SceneContainerFlag.isUnexpectedlyInLegacyMode(); - final boolean onBouncer = currentScene.equals(Scenes.Bouncer); final boolean onCommunal = currentScene.equals(Scenes.Communal); final boolean onGone = currentScene.equals(Scenes.Gone); final boolean onDream = currentScene.equals(Scenes.Dream); @@ -635,8 +634,8 @@ public class StatusBarStateControllerImpl implements final boolean onQuickSettings = currentScene.equals(Scenes.QuickSettings); final boolean onShade = currentScene.equals(Scenes.Shade); + final boolean overlaidBouncer = currentOverlays.contains(Overlays.Bouncer); final boolean overCommunal = SceneStackKt.contains(backStack, Scenes.Communal); - final boolean overLockscreen = SceneStackKt.contains(backStack, Scenes.Lockscreen); final boolean overShade = SceneStackKt.contains(backStack, Scenes.Shade); final boolean overlaidShade = currentOverlays.contains(Overlays.NotificationsShade); @@ -669,14 +668,13 @@ public class StatusBarStateControllerImpl implements // 3. backStack contains a keyguardish scene (Lockscreen or Communal). // 4. the alternate bouncer is visible. - final boolean onKeyguardish = onLockscreen || onBouncer || onCommunal; - final boolean overKeyguardish = overLockscreen || overCommunal; + final boolean onKeyguardish = onLockscreen || overlaidBouncer || onCommunal; if (isOccluded) { // Occlusion is special; even though the device is still technically on the lockscreen, // the UI behaves as if it is unlocked. newState = StatusBarState.SHADE; - } else if (onKeyguardish || overKeyguardish || alternateBouncerIsVisible) { + } else if (onKeyguardish || overCommunal || alternateBouncerIsVisible) { // We get here if we are on or over a keyguardish scene, even if isUnlocked is true; we // want to return SHADE_LOCKED or KEYGUARD until we are also neither on nor over a // keyguardish scene. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/StatusBarKeyguardViewManagerInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/StatusBarKeyguardViewManagerInteractor.kt index 9ca110e8c561..352d660eb29a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/StatusBarKeyguardViewManagerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/StatusBarKeyguardViewManagerInteractor.kt @@ -37,10 +37,7 @@ import kotlinx.coroutines.flow.merge /** * Whether to set the status bar keyguard view occluded or not, and whether to animate that change. */ -data class OccludedState( - val occluded: Boolean, - val animate: Boolean = false, -) +data class OccludedState(val occluded: Boolean, val animate: Boolean = false) /** Handles logic around calls to [StatusBarKeyguardViewManager] in legacy code. */ @Deprecated("Will be removed once all of SBKVM's responsibilies are refactored.") @@ -93,12 +90,12 @@ constructor( private val occlusionStateFromFinishedStep = combine( keyguardTransitionInteractor.isFinishedIn( - scene = Scenes.Gone, + content = Scenes.Gone, stateWithoutSceneContainer = KeyguardState.GONE, ), keyguardTransitionInteractor.isFinishedIn(KeyguardState.OCCLUDED), keyguardOcclusionInteractor.isShowWhenLockedActivityOnTop, - ::Triple + ::Triple, ) .map { (isOnGone, isOnOccluded, showWhenLockedOnTop) -> // If we're FINISHED in OCCLUDED, we want to render as occluded. We also need to diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/media/ui/compose/MediaControlPopup.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/media/ui/compose/MediaControlPopup.kt new file mode 100644 index 000000000000..80bdb7f8a05f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/media/ui/compose/MediaControlPopup.kt @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.featurepods.media.ui.compose + +import android.widget.FrameLayout +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.unit.dp +import androidx.compose.ui.viewinterop.AndroidView +import com.android.systemui.media.controls.ui.view.MediaHost +import com.android.systemui.res.R + +/** Displays a popup containing media controls. Embeds the MediaCarousel within a Compose popup. */ +@Composable +fun MediaControlPopup(mediaHost: MediaHost, modifier: Modifier = Modifier) { + AndroidView( + modifier = + modifier + .width(400.dp) + .height(200.dp) + .clip( + shape = + RoundedCornerShape(dimensionResource(R.dimen.notification_corner_radius)) + ), + factory = { _ -> + mediaHost.hostView.apply { + layoutParams = + FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.MATCH_PARENT, + ) + } + mediaHost.hostView + }, + onReset = {}, + ) +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopup.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopup.kt index 8a66904ea59b..ead51482b561 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopup.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopup.kt @@ -28,7 +28,9 @@ import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Popup import androidx.compose.ui.window.PopupProperties +import com.android.systemui.media.controls.ui.view.MediaHost import com.android.systemui.res.R +import com.android.systemui.statusbar.featurepods.media.ui.compose.MediaControlPopup import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipId import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipModel @@ -37,7 +39,7 @@ import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipM * status bar. */ @Composable -fun StatusBarPopup(viewModel: PopupChipModel.Shown) { +fun StatusBarPopup(viewModel: PopupChipModel.Shown, mediaHost: MediaHost) { val density = Density(LocalContext.current) Popup( properties = @@ -56,7 +58,7 @@ fun StatusBarPopup(viewModel: PopupChipModel.Shown) { Box(modifier = Modifier.padding(8.dp).wrapContentSize()) { when (viewModel.chipId) { is PopupChipId.MediaControl -> { - // TODO(b/385202114): Populate MediaControlPopup contents. + MediaControlPopup(mediaHost = mediaHost) } } // Future popup types will be handled here. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopupChipsContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopupChipsContainer.kt index 16538c93cf35..f5f1f2028aa2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopupChipsContainer.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopupChipsContainer.kt @@ -20,14 +20,37 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import com.android.systemui.media.controls.ui.view.MediaHost +import com.android.systemui.scene.shared.flag.SceneContainerFlag +import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipId import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipModel /** Container view that holds all right hand side chips in the status bar. */ @Composable -fun StatusBarPopupChipsContainer(chips: List<PopupChipModel.Shown>, modifier: Modifier = Modifier) { +fun StatusBarPopupChipsContainer( + chips: List<PopupChipModel.Shown>, + mediaHost: MediaHost, + onMediaControlPopupVisibilityChanged: (Boolean) -> Unit, + modifier: Modifier = Modifier, +) { + if (!SceneContainerFlag.isEnabled) { + val isMediaControlPopupShown = + remember(chips) { + chips.any { it.chipId == PopupChipId.MediaControl && it.isPopupShown } + } + + LaunchedEffect(isMediaControlPopupShown) { + onMediaControlPopupVisibilityChanged(isMediaControlPopupShown) + } + } + // TODO(b/385353140): Add padding and spacing for this container according to UX specs. Box { Row( @@ -37,7 +60,7 @@ fun StatusBarPopupChipsContainer(chips: List<PopupChipModel.Shown>, modifier: Mo chips.forEach { chip -> StatusBarPopupChip(chip) if (chip.isPopupShown) { - StatusBarPopup(chip) + StatusBarPopup(viewModel = chip, mediaHost = mediaHost) } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java index 26bc5b14d357..d1063d95a305 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java @@ -117,6 +117,7 @@ public class RankingCoordinator implements Coordinator { @Override public boolean isInSection(PipelineEntry entry) { return !mHighPriorityProvider.isHighPriority(entry) + && entry.getRepresentativeEntry() != null && !entry.getRepresentativeEntry().isAmbient(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/ConnectedDisplaysStatusBarNotificationIconViewStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/ConnectedDisplaysStatusBarNotificationIconViewStore.kt index eb55856d994b..5760b5f79c74 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/ConnectedDisplaysStatusBarNotificationIconViewStore.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/ConnectedDisplaysStatusBarNotificationIconViewStore.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification.icon.ui.viewbinder +import android.util.Log import com.android.systemui.display.domain.interactor.DisplayWindowPropertiesInteractor import com.android.systemui.lifecycle.Activatable import com.android.systemui.statusbar.StatusBarIconView @@ -61,9 +62,16 @@ constructor( } override fun iconView(key: String): StatusBarIconView? { - val entry = notifCollection.getEntry(key) ?: return null - val displayWindowProperties = - displayWindowPropertiesInteractor.getForStatusBar(displayId) ?: return null + val entry = notifCollection.getEntry(key) + if (entry == null) { + Log.w(TAG, "No notification entry found for key: $key") + return null + } + val displayWindowProperties = displayWindowPropertiesInteractor.getForStatusBar(displayId) + if (displayWindowProperties == null) { + Log.w(TAG, "No display properties found for display id: $displayId") + return null + } return cachedIcons.computeIfAbsent(key) { val context = displayWindowProperties.context iconManager.createSbIconView(context, entry) @@ -93,4 +101,8 @@ constructor( interface Factory { fun create(displayId: Int): ConnectedDisplaysStatusBarNotificationIconViewStore } + + companion object { + private const val TAG = "ConnectedDisplaysNotifIconViewStore" + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java index b0773276a3e9..4ed9dcee072e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java @@ -16,8 +16,10 @@ package com.android.systemui.statusbar.notification.row; +import static com.android.systemui.Flags.notificationAppearNonlinear; import static com.android.systemui.Flags.notificationBackgroundTintOptimization; import static com.android.systemui.Flags.notificationRowTransparency; +import static com.android.systemui.Flags.physicalNotificationMovement; import static com.android.systemui.statusbar.notification.row.ExpandableView.ClipSide.BOTTOM; import static com.android.systemui.statusbar.notification.row.ExpandableView.ClipSide.TOP; @@ -71,7 +73,8 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView * #ALPHA_APPEAR_START_FRACTION. */ - private static final float ALPHA_APPEAR_START_FRACTION = .7f; + private static final float ALPHA_APPEAR_START_FRACTION = + notificationAppearNonlinear() ? .55f : .7f; /** * The content should show fully with progress at #ALPHA_APPEAR_END_FRACTION * The start of the animation is at #ALPHA_APPEAR_START_FRACTION @@ -111,6 +114,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView private float mOverrideAmount; private boolean mShadowHidden; private boolean mIsHeadsUpAnimation; + private boolean mIsHeadsUpCycling; /* In order to track headsup longpress coorindate. */ protected Point mTargetPoint; private boolean mDismissed; @@ -349,10 +353,12 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView @Override public long performRemoveAnimation(long duration, long delay, float translationDirection, - boolean isHeadsUpAnimation, Runnable onStartedRunnable, Runnable onFinishedRunnable, - AnimatorListenerAdapter animationListener, ClipSide clipSide) { + boolean isHeadsUpAnimation, boolean isHeadsUpCycling, Runnable onStartedRunnable, + Runnable onFinishedRunnable, AnimatorListenerAdapter animationListener, + ClipSide clipSide) { enableAppearDrawing(true); mIsHeadsUpAnimation = isHeadsUpAnimation; + mIsHeadsUpCycling = isHeadsUpCycling; if (mDrawingAppearAnimation) { startAppearAnimation(false /* isAppearing */, translationDirection, delay, duration, onStartedRunnable, onFinishedRunnable, animationListener, @@ -370,9 +376,10 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView @Override public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear, - Runnable onFinishRunnable) { + boolean isHeadsUpCycling, Runnable onFinishRunnable) { enableAppearDrawing(true); mIsHeadsUpAnimation = isHeadsUpAppear; + mIsHeadsUpCycling = isHeadsUpCycling; if (mDrawingAppearAnimation) { startAppearAnimation(true /* isAppearing */, isHeadsUpAppear ? 0.0f : -1.0f, delay, duration, null, null, null, ClipSide.BOTTOM); @@ -404,14 +411,14 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView targetValue = 0.0f; } - if (NotificationHeadsUpCycling.isEnabled()) { - // TODO(b/316404716): add avalanche filtering + if (NotificationHeadsUpCycling.isEnabled() && !useNonLinearAnimation()) { mCurrentAppearInterpolator = Interpolators.LINEAR; } mAppearAnimator = ValueAnimator.ofFloat(mAppearAnimationFraction, targetValue); - mAppearAnimator.setInterpolator(mCurrentAppearInterpolator); + mAppearAnimator.setInterpolator( + useNonLinearAnimation() ? Interpolators.LINEAR : mCurrentAppearInterpolator); mAppearAnimator.setDuration( (long) (duration * Math.abs(mAppearAnimationFraction - targetValue))); mAppearAnimator.addUpdateListener(animation -> { @@ -530,7 +537,13 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView * @param clipSide Which side if view we want to clip from */ private void updateAppearRect(ClipSide clipSide) { - float interpolatedFraction = mAppearAnimationFraction; + float interpolatedFraction; + if (useNonLinearAnimation()) { + interpolatedFraction = mCurrentAppearInterpolator.getInterpolation( + mAppearAnimationFraction); + } else { + interpolatedFraction = mAppearAnimationFraction; + } mAppearAnimationTranslation = (1.0f - interpolatedFraction) * mAnimationTranslationY; final int fullHeight = getActualHeight(); float height = fullHeight * interpolatedFraction; @@ -558,6 +571,11 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView } } + private boolean useNonLinearAnimation() { + return notificationAppearNonlinear() && (!mIsHeadsUpCycling + || physicalNotificationMovement()); + } + private void updateAppearRect() { updateAppearRect(ClipSide.BOTTOM); } @@ -567,7 +585,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView mAppearAnimationFraction, ALPHA_APPEAR_START_FRACTION, ALPHA_APPEAR_END_FRACTION, - Interpolators.ALPHA_IN + notificationAppearNonlinear() ? mCurrentAppearInterpolator : Interpolators.ALPHA_IN ); } @@ -813,6 +831,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView pw.print("mDrawingAppearAnimation", mDrawingAppearAnimation); pw.print("mAppearAnimationFraction", mAppearAnimationFraction); pw.print("mIsHeadsUpAnimation", mIsHeadsUpAnimation); + pw.print("mIsHeadsUpCycling", mIsHeadsUpCycling); pw.print("mTargetPoint", mTargetPoint); pw.println(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index d2800d7c1b71..4c744086fea4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -3494,20 +3494,17 @@ public class ExpandableNotificationRow extends ActivatableNotificationView @Override public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear, - Runnable onFinishRunnable) { + boolean isHeadsUpCycling, Runnable onFinishRunnable) { mLogger.logStartAppearAnimation(mLoggingKey, /* isAppear = */ true); - super.performAddAnimation(delay, duration, isHeadsUpAppear, onFinishRunnable); + super.performAddAnimation(delay, duration, isHeadsUpAppear, isHeadsUpCycling, + onFinishRunnable); } @Override - public long performRemoveAnimation( - long duration, - long delay, - float translationDirection, - boolean isHeadsUpAnimation, - Runnable onStartedRunnable, - Runnable onFinishedRunnable, - AnimatorListenerAdapter animationListener, ClipSide clipSide) { + public long performRemoveAnimation(long duration, long delay, float translationDirection, + boolean isHeadsUpAnimation, boolean isHeadsUpCycling, Runnable onStartedRunnable, + Runnable onFinishedRunnable, AnimatorListenerAdapter animationListener, + ClipSide clipSide) { mLogger.logStartAppearAnimation(mLoggingKey, /* isAppear = */ false); if (mMenuRow != null && mMenuRow.isMenuVisible()) { Animator anim = getTranslateViewAnimator(0f, null /* listener */); @@ -3522,9 +3519,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView @Override public void onAnimationEnd(Animator animation) { - ExpandableNotificationRow.super.performRemoveAnimation( - duration, delay, translationDirection, isHeadsUpAnimation, - null, onFinishedRunnable, animationListener, ClipSide.BOTTOM); + ExpandableNotificationRow.super.performRemoveAnimation(duration, delay, + translationDirection, isHeadsUpAnimation, isHeadsUpCycling, null, + onFinishedRunnable, animationListener, ClipSide.BOTTOM); } }); anim.start(); @@ -3532,8 +3529,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } } return super.performRemoveAnimation(duration, delay, translationDirection, - isHeadsUpAnimation, onStartedRunnable, onFinishedRunnable, animationListener, - clipSide); + isHeadsUpAnimation, isHeadsUpCycling, onStartedRunnable, onFinishedRunnable, + animationListener, clipSide); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java index da664f864f06..292f74a65554 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java @@ -467,7 +467,8 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable, Ro * remove animation should be performed upwards, * such that the child appears to be going away to the top. 1 * Should mean the opposite. - * @param isHeadsUpAnimation Is this a headsUp animation. + * @param isHeadsUpAnimation Is this a headsUp animation + * @param isHeadsUpCycling Is this the cycling heads up animation * @param onFinishedRunnable A runnable which should be run when the animation is finished. * @param animationListener An animation listener to add to the animation. * @return The additional delay, in milliseconds, that this view needs to add before the @@ -475,7 +476,7 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable, Ro */ public abstract long performRemoveAnimation(long duration, long delay, float translationDirection, boolean isHeadsUpAnimation, - Runnable onStartedRunnable, + boolean isHeadsUpCycling, Runnable onStartedRunnable, Runnable onFinishedRunnable, AnimatorListenerAdapter animationListener, ClipSide clipSide); @@ -485,11 +486,12 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable, Ro } public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear) { - performAddAnimation(delay, duration, isHeadsUpAppear, null); + performAddAnimation(delay, duration, isHeadsUpAppear, false /* isHeadsUpCycling */, + null); } public abstract void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear, - Runnable onEndRunnable); + boolean isHeadsUpCycling, Runnable onEndRunnable); public int getPinnedHeadsUpHeight() { return getIntrinsicHeight(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java index c31f4ad5c3f1..4b2b1688bde6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java @@ -998,6 +998,10 @@ public class NotificationContentInflater implements NotificationRowContentBinder entry.setPromotedNotificationContentModel(result.mPromotedContent); } + if (PromotedNotificationUiForceExpanded.isEnabled()) { + row.setPromotedOngoing(entry.isOngoingPromoted()); + } + boolean setRepliesAndActions = true; if ((reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0) { if (result.inflatedContentView != null) { @@ -1130,9 +1134,6 @@ public class NotificationContentInflater implements NotificationRowContentBinder entry.setHeadsUpStatusBarText(result.headsUpStatusBarText); entry.setHeadsUpStatusBarTextPublic(result.headsUpStatusBarTextPublic); - if (PromotedNotificationUiForceExpanded.isEnabled()) { - row.setPromotedOngoing(entry.isOngoingPromoted()); - } Trace.endAsyncSection(APPLY_TRACE_METHOD, System.identityHashCode(row)); if (endListener != null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java index cd228e7872c1..a064d1c5b701 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java @@ -260,7 +260,7 @@ public abstract class StackScrollerDecorView extends ExpandableView { @Override public long performRemoveAnimation(long duration, long delay, float translationDirection, boolean isHeadsUpAnimation, - Runnable onStartedRunnable, + boolean isHeadsUpCycling, Runnable onStartedRunnable, Runnable onFinishedRunnable, AnimatorListenerAdapter animationListener, ClipSide clipSide) { // TODO: Use duration @@ -279,7 +279,7 @@ public abstract class StackScrollerDecorView extends ExpandableView { @Override public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear, - Runnable endRunnable) { + boolean isHeadsUpCycling, Runnable endRunnable) { // TODO: use delay and duration setContentVisibleAnimated(true); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java index e8affaa4b60f..e9eecdd8a26f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java @@ -31,7 +31,6 @@ import android.view.ViewGroup; import android.view.animation.Interpolator; import android.view.animation.PathInterpolator; import android.widget.DateTimeView; -import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.TextView; @@ -42,7 +41,6 @@ import com.android.app.animation.Interpolators; import com.android.internal.widget.CachingIconView; import com.android.internal.widget.NotificationCloseButton; import com.android.internal.widget.NotificationExpandButton; -import com.android.systemui.Flags; import com.android.systemui.res.R; import com.android.systemui.statusbar.TransformableView; import com.android.systemui.statusbar.ViewTransformationHelper; @@ -69,7 +67,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper imple private CachingIconView mIcon; private NotificationCloseButton mCloseButton; private NotificationExpandButton mExpandButton; - private FrameLayout mExpandButtonSpacer; private View mAltExpandTarget; private View mIconContainer; protected NotificationHeaderView mNotificationHeader; @@ -157,10 +154,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper imple mHeaderText = mView.findViewById(com.android.internal.R.id.header_text); mAppNameText = mView.findViewById(com.android.internal.R.id.app_name_text); mExpandButton = mView.findViewById(com.android.internal.R.id.expand_button); - if (Flags.uiRichOngoingForceExpanded()) { - mExpandButtonSpacer = - mView.findViewById(com.android.internal.R.id.expand_button_spacer); - } mAltExpandTarget = mView.findViewById(com.android.internal.R.id.alternate_expand_target); mIconContainer = mView.findViewById(com.android.internal.R.id.conversation_icon_container); mWorkProfileImage = mView.findViewById(com.android.internal.R.id.profile_badge); @@ -302,9 +295,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper imple boolean expandable, View.OnClickListener onClickListener, boolean requestLayout) { - if (Flags.uiRichOngoingForceExpanded() && mExpandButtonSpacer != null) { - mExpandButtonSpacer.setVisibility(expandable ? GONE : VISIBLE); - } mExpandButton.setVisibility(expandable ? VISIBLE : GONE); mExpandButton.setOnClickListener(expandable ? onClickListener : null); if (mAltExpandTarget != null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java index 3ccf5063cd68..b9aa57145c7c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java @@ -33,6 +33,7 @@ import android.graphics.drawable.Icon; import android.service.notification.StatusBarNotification; import android.util.ArraySet; import android.view.View; +import android.view.ViewGroup; import android.widget.Button; import android.widget.ImageView; import android.widget.ProgressBar; @@ -42,6 +43,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ContrastColorUtil; import com.android.internal.widget.NotificationActionListLayout; import com.android.systemui.Dependency; +import com.android.systemui.Flags; import com.android.systemui.UiOffloadThread; import com.android.systemui.res.R; import com.android.systemui.statusbar.CrossFadeHelper; @@ -51,6 +53,7 @@ import com.android.systemui.statusbar.notification.ImageTransformState; import com.android.systemui.statusbar.notification.TransformState; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.HybridNotificationView; +import com.android.systemui.util.DimensionKt; import java.util.function.Consumer; @@ -186,9 +189,43 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp mActions = mView.findViewById(com.android.internal.R.id.actions); mRemoteInputHistory = mView.findViewById( com.android.internal.R.id.notification_material_reply_container); + + adjustTitleAndRightIconForPromotedOngoing(); updatePendingIntentCancellations(); } + private void adjustTitleAndRightIconForPromotedOngoing() { + if (Flags.uiRichOngoingForceExpanded() && mRow.isPromotedOngoing() && mRightIcon != null) { + final int horizontalMargin; + if (notificationsRedesignTemplates()) { + horizontalMargin = mView.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.notification_2025_margin); + } else { + horizontalMargin = (int) DimensionKt.dpToPx(16, mView.getContext()); + } + + // position right icon to the right available space from expander. + final ViewGroup.MarginLayoutParams rightIconLP = + (ViewGroup.MarginLayoutParams) mRightIcon.getLayoutParams(); + rightIconLP.setMarginEnd(horizontalMargin); + mRightIcon.setLayoutParams(rightIconLP); + + // align top line view to start of the right icon. + final int iconSize = mView.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.notification_right_icon_size); + final int marginEnd = 2 * horizontalMargin + iconSize; + mNotificationTopLine.setHeaderTextMarginEnd(marginEnd); + + // title has too much margin on the right, so we need to reduce it + if (mTitle != null) { + final ViewGroup.MarginLayoutParams titleLP = + (ViewGroup.MarginLayoutParams) mTitle.getLayoutParams(); + titleLP.setMarginEnd(marginEnd); + mTitle.setLayoutParams(titleLP); + } + } + } + @Nullable protected final Icon getLargeIcon(Notification n) { Icon modernLargeIcon = n.getLargeIcon(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt index bd7bd596438a..4d10a527d53b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt @@ -70,14 +70,15 @@ class MediaContainerView(context: Context, attrs: AttributeSet?) : ExpandableVie } override fun performRemoveAnimation( - duration: Long, - delay: Long, - translationDirection: Float, - isHeadsUpAnimation: Boolean, - onStartedRunnable: Runnable?, - onFinishedRunnable: Runnable?, - animationListener: AnimatorListenerAdapter?, - clipSide: ClipSide + duration: Long, + delay: Long, + translationDirection: Float, + isHeadsUpAnimation: Boolean, + isHeadsUpCycling: Boolean, + onStartedRunnable: Runnable?, + onFinishedRunnable: Runnable?, + animationListener: AnimatorListenerAdapter?, + clipSide: ClipSide, ): Long { return 0 } @@ -86,7 +87,8 @@ class MediaContainerView(context: Context, attrs: AttributeSet?) : ExpandableVie delay: Long, duration: Long, isHeadsUpAppear: Boolean, - onEnd: Runnable? + isHeadsUpCycling: Boolean, + onEnd: Runnable?, ) { // No animation, it doesn't need it, this would be local } @@ -103,9 +105,7 @@ class MediaContainerView(context: Context, attrs: AttributeSet?) : ExpandableVie assertMediaContainerVisibility(visibility) } - /** - * visibility should be aligned with MediaContainerView visibility on the keyguard. - */ + /** visibility should be aligned with MediaContainerView visibility on the keyguard. */ private fun isVisibilityValid(visibility: Int): Boolean { val currentViewState = viewState as? MediaContainerViewState ?: return true val shouldBeGone = !currentViewState.shouldBeVisible @@ -113,8 +113,7 @@ class MediaContainerView(context: Context, attrs: AttributeSet?) : ExpandableVie } /** - * b/298213983 - * MediaContainerView's visibility is changed to VISIBLE when it should be GONE. + * b/298213983 MediaContainerView's visibility is changed to VISIBLE when it should be GONE. * This method check this state and logs. */ private fun assertMediaContainerVisibility(visibility: Int) { @@ -122,8 +121,10 @@ class MediaContainerView(context: Context, attrs: AttributeSet?) : ExpandableVie if (currentViewState is MediaContainerViewState) { if (!currentViewState.shouldBeVisible && visibility == VISIBLE) { - Log.wtf("MediaContainerView", "MediaContainerView should be GONE " + - "but its visibility changed to VISIBLE") + Log.wtf( + "MediaContainerView", + "MediaContainerView should be GONE " + "but its visibility changed to VISIBLE", + ) } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java index 5e0d57ebb3fe..2b052236a921 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java @@ -506,8 +506,8 @@ public class StackStateAnimator { } changingView.performRemoveAnimation(ANIMATION_DURATION_APPEAR_DISAPPEAR, 0 /* delay */, translationDirection, false /* isHeadsUpAppear */, - startAnimation, postAnimation, getGlobalAnimationFinishedListener(), - ExpandableView.ClipSide.BOTTOM); + false /* isHeadsUpCycling */, startAnimation, postAnimation, + getGlobalAnimationFinishedListener(), ExpandableView.ClipSide.BOTTOM); needsCustomAnimation = true; } else if (event.animationType == NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) { @@ -538,7 +538,7 @@ public class StackStateAnimator { }; } changingView.performAddAnimation(0, ANIMATION_DURATION_HEADS_UP_CYCLING, - /* isHeadsUpAppear= */ true, onAnimationEnd); + /* isHeadsUpAppear= */ true, /* isHeadsUpCycling= */ true, onAnimationEnd); } else if (event.animationType == ANIMATION_TYPE_HEADS_UP_APPEAR) { mHeadsUpAppearChildren.add(changingView); @@ -559,7 +559,7 @@ public class StackStateAnimator { onAnimationEnd = () -> mLogger.appearAnimationEnded(finalKey); } changingView.performAddAnimation(0, ANIMATION_DURATION_HEADS_UP_APPEAR, - /* isHeadsUpAppear= */ true, onAnimationEnd); + /* isHeadsUpAppear= */ true, /* isHeadsUpCycling= */ false, onAnimationEnd); } else if (event.animationType == ANIMATION_TYPE_HEADS_UP_CYCLING_OUT) { mHeadsUpDisappearChildren.add(changingView); Runnable endRunnable = null; @@ -629,6 +629,7 @@ public class StackStateAnimator { // translation, the actual translation is in StackScrollAlgorithm. /* translationDirection= */ 0.0f, /* isHeadsUpAnimation= */ true, + /* isHeadsUpCycling= */ true, startAnimation, postAnimation, getGlobalAnimationFinishedListener(), ExpandableView.ClipSide.TOP); mAnimationProperties.delay += removeAnimationDelay; @@ -706,6 +707,7 @@ public class StackStateAnimator { long removeAnimationDelay = changingView.performRemoveAnimation( ANIMATION_DURATION_HEADS_UP_DISAPPEAR, 0, 0.0f, true /* isHeadsUpAppear */, + false /* isHeadsUpCycling */, startAnimation, postAnimation, getGlobalAnimationFinishedListener(), ExpandableView.ClipSide.BOTTOM); mAnimationProperties.delay += removeAnimationDelay; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt index 08d98a1d53e5..1dbaf2f0f401 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt @@ -147,7 +147,7 @@ constructor( sceneInteractor.transitionState.map { transition: ObservableTransitionState -> transition is Transition && transition.fromContent == Scenes.Lockscreen && - (transition.toContent == Scenes.Bouncer || transition.toContent == Scenes.Gone) + (transition.toContent == Overlays.Bouncer || transition.toContent == Scenes.Gone) } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt index 34b65603542c..877aa76590e9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt @@ -76,6 +76,7 @@ import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToLockscreenTran import com.android.systemui.keyguard.ui.viewmodel.ViewStateAccessor import com.android.systemui.res.R import com.android.systemui.scene.shared.flag.SceneContainerFlag +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.LargeScreenHeaderHelper import com.android.systemui.shade.ShadeDisplayAware @@ -307,7 +308,7 @@ constructor( keyguardTransitionInteractor.transitionValue(ALTERNATE_BOUNCER).map { it > 0f }, keyguardTransitionInteractor .transitionValue( - scene = Scenes.Bouncer, + content = Overlays.Bouncer, stateWithoutSceneContainer = PRIMARY_BOUNCER, ) .map { it > 0f }, @@ -361,7 +362,7 @@ constructor( private val isOnGlanceableHub: Flow<Boolean> = combine( keyguardTransitionInteractor.isFinishedIn( - scene = Scenes.Communal, + content = Scenes.Communal, stateWithoutSceneContainer = GLANCEABLE_HUB, ), anyOf( @@ -638,7 +639,7 @@ constructor( anyOf( isKeyguardOccluded, keyguardTransitionInteractor - .transitionValue(scene = Scenes.Gone, stateWithoutSceneContainer = GONE) + .transitionValue(content = Scenes.Gone, stateWithoutSceneContainer = GONE) .map { it == 1f }, ) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index bc297699c41a..8c44fe56d269 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -84,7 +84,7 @@ import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.scene.domain.interactor.SceneInteractor; import com.android.systemui.scene.shared.flag.SceneContainerFlag; -import com.android.systemui.scene.shared.model.Scenes; +import com.android.systemui.scene.shared.model.Overlays; import com.android.systemui.shade.ShadeController; import com.android.systemui.shade.ShadeExpansionChangeEvent; import com.android.systemui.shade.ShadeExpansionListener; @@ -724,8 +724,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb if (!primaryBouncerIsShowing()) { if (SceneContainerFlag.isEnabled()) { mCentralSurfaces.hideKeyguard(); - mSceneInteractorLazy.get().changeScene( - Scenes.Bouncer, "StatusBarKeyguardViewManager.showBouncerOrKeyguard"); + mSceneInteractorLazy.get().showOverlay( + Overlays.Bouncer, + "StatusBarKeyguardViewManager.showBouncerOrKeyguard" + ); } else { if (Flags.simPinRaceConditionOnRestart()) { if (mPrimaryBouncerInteractor.show(/* isScrimmed= */ true)) { @@ -816,8 +818,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb /* clearDismissAction= */ !SceneContainerFlag.isEnabled()); if (mKeyguardStateController.isShowing() && !isBouncerShowing()) { if (SceneContainerFlag.isEnabled()) { - mSceneInteractorLazy.get().changeScene( - Scenes.Bouncer, + mSceneInteractorLazy.get().showOverlay( + Overlays.Bouncer, "primary bouncer requested" ); } else { @@ -915,8 +917,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb // we'll handle the dismiss action after keyguard is gone, so just show the // bouncer if (SceneContainerFlag.isEnabled()) { - mSceneInteractorLazy.get().changeScene( - Scenes.Bouncer, "StatusBarKeyguardViewManager.dismissWithAction"); + mSceneInteractorLazy.get().showOverlay( + Overlays.Bouncer, + "StatusBarKeyguardViewManager.dismissWithAction" + ); } else { mPrimaryBouncerInteractor.show(/* isScrimmed= */ true); } @@ -926,8 +930,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mPrimaryBouncerInteractor.setDismissAction( mAfterKeyguardGoneAction, mKeyguardGoneCancelAction); if (SceneContainerFlag.isEnabled()) { - mSceneInteractorLazy.get().changeScene( - Scenes.Bouncer, "StatusBarKeyguardViewManager.dismissWithAction"); + mSceneInteractorLazy.get().showOverlay( + Overlays.Bouncer, + "StatusBarKeyguardViewManager.dismissWithAction" + ); } else { mPrimaryBouncerInteractor.show(/* isScrimmed= */ true); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt index 39a1b46292b0..4189221d8a83 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt @@ -37,6 +37,10 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.theme.PlatformTheme import com.android.keyguard.AlphaOptimizedLinearLayout import com.android.systemui.lifecycle.rememberViewModel +import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager +import com.android.systemui.media.controls.ui.view.MediaHost +import com.android.systemui.media.controls.ui.view.MediaHostState +import com.android.systemui.media.dagger.MediaModule.POPUP import com.android.systemui.plugins.DarkIconDispatcher import com.android.systemui.res.R import com.android.systemui.statusbar.chips.ui.compose.OngoingActivityChips @@ -67,6 +71,7 @@ import com.android.systemui.statusbar.pipeline.shared.ui.model.VisibilityModel import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel.HomeStatusBarViewModelFactory import javax.inject.Inject +import javax.inject.Named /** Factory to simplify the dependency management for [StatusBarRoot] */ class StatusBarRootFactory @@ -81,6 +86,8 @@ constructor( private val ongoingCallController: OngoingCallController, private val darkIconDispatcherStore: DarkIconDispatcherStore, private val eventAnimationInteractor: SystemStatusEventAnimationInteractor, + private val mediaHierarchyManager: MediaHierarchyManager, + @Named(POPUP) private val mediaHost: MediaHost, ) { fun create(root: ViewGroup, andThen: (ViewGroup) -> Unit): ComposeView { val composeView = ComposeView(root.context) @@ -99,6 +106,8 @@ constructor( ongoingCallController = ongoingCallController, darkIconDispatcher = darkIconDispatcher, eventAnimationInteractor = eventAnimationInteractor, + mediaHierarchyManager = mediaHierarchyManager, + mediaHost = mediaHost, onViewCreated = andThen, ) } @@ -130,6 +139,8 @@ fun StatusBarRoot( ongoingCallController: OngoingCallController, darkIconDispatcher: DarkIconDispatcher, eventAnimationInteractor: SystemStatusEventAnimationInteractor, + mediaHierarchyManager: MediaHierarchyManager, + mediaHost: MediaHost, onViewCreated: (ViewGroup) -> Unit, ) { val displayId = parent.context.displayId @@ -237,6 +248,15 @@ fun StatusBarRoot( // Add a composable container for `StatusBarPopupChip`s if (StatusBarPopupChips.isEnabled) { + with(mediaHost) { + expansion = MediaHostState.EXPANDED + expandedMatchesParentHeight = true + showsOnlyActiveMedia = true + falsingProtectionNeeded = false + disablePagination = true + init(MediaHierarchyManager.LOCATION_STATUS_BAR_POPUP) + } + val endSideContent = phoneStatusBarView.requireViewById<AlphaOptimizedLinearLayout>( R.id.status_bar_end_side_content @@ -256,7 +276,12 @@ fun StatusBarRoot( setContent { StatusBarPopupChipsContainer( - chips = statusBarViewModel.popupChips + chips = statusBarViewModel.popupChips, + mediaHost = mediaHost, + onMediaControlPopupVisibilityChanged = { popupShowing -> + mediaHierarchyManager.isMediaControlPopupShowing = + popupShowing + }, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt index 7d9a7d49cf7f..2dc17f40a380 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt @@ -78,6 +78,7 @@ constructor( currentScene == Scenes.Lockscreen && Overlays.NotificationsShade !in currentOverlays && Overlays.QuickSettingsShade !in currentOverlays && + Overlays.Bouncer !in currentOverlays && !isDozing && !showHeadsUpStatusBar } diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/TouchpadTutorialModule.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/TouchpadTutorialModule.kt index d8a9527b22ab..d2f1d3cb7b47 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/TouchpadTutorialModule.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/TouchpadTutorialModule.kt @@ -133,14 +133,14 @@ private class ScreensProvider( override fun BackGesture( onDoneButtonClicked: () -> Unit, onBack: () -> Unit, - isAutoProceed: Boolean, + onAutoProceed: (suspend () -> Unit)?, ) { BackGestureTutorialScreen( backGestureScreenViewModel, easterEggGestureViewModel, onDoneButtonClicked, onBack, - isAutoProceed, + onAutoProceed, ) } @@ -148,14 +148,14 @@ private class ScreensProvider( override fun HomeGesture( onDoneButtonClicked: () -> Unit, onBack: () -> Unit, - isAutoProceed: Boolean, + onAutoProceed: (suspend () -> Unit)?, ) { HomeGestureTutorialScreen( homeGestureScreenViewModel, easterEggGestureViewModel, onDoneButtonClicked, onBack, - isAutoProceed, + onAutoProceed, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt index c28483c55952..2e2a97d9702f 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt @@ -33,7 +33,7 @@ fun BackGestureTutorialScreen( easterEggGestureViewModel: EasterEggGestureViewModel, onDoneButtonClicked: () -> Unit, onBack: () -> Unit, - isAutoProceed: Boolean = false, + onAutoProceed: (suspend () -> Unit)? = null, ) { val screenConfig = TutorialScreenConfig( @@ -48,7 +48,6 @@ fun BackGestureTutorialScreen( bodyErrorResId = R.string.touchpad_back_gesture_error_body, ), animations = TutorialScreenConfig.Animations(educationResId = R.raw.trackpad_back_edu), - hasNextButton = isAutoProceed, ) GestureTutorialScreen( screenConfig = screenConfig, @@ -61,6 +60,7 @@ fun BackGestureTutorialScreen( onEasterEggFinished = easterEggGestureViewModel::onEasterEggFinished, onDoneButtonClicked = onDoneButtonClicked, onBack = onBack, + onAutoProceed = onAutoProceed, ) } diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt index 47c82e309d9b..db3e31ba2e61 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt @@ -49,6 +49,7 @@ fun GestureTutorialScreen( onEasterEggFinished: () -> Unit, onDoneButtonClicked: () -> Unit, onBack: () -> Unit, + onAutoProceed: (suspend () -> Unit)? = null, ) { BackHandler(onBack = onBack) var cachedTutorialState: TutorialActionState by @@ -64,7 +65,7 @@ fun GestureTutorialScreen( easterEggTriggered, onEasterEggFinished, ) { - ActionTutorialContent(tutorialState, onDoneButtonClicked, screenConfig) + ActionTutorialContent(tutorialState, onDoneButtonClicked, screenConfig, onAutoProceed) } } diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt index b238a8db31f8..3e27dfb07624 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt @@ -32,7 +32,7 @@ fun HomeGestureTutorialScreen( easterEggGestureViewModel: EasterEggGestureViewModel, onDoneButtonClicked: () -> Unit, onBack: () -> Unit, - isAutoProceed: Boolean = false, + onAutoProceed: (suspend () -> Unit)? = null, ) { val screenConfig = TutorialScreenConfig( @@ -47,7 +47,6 @@ fun HomeGestureTutorialScreen( bodyErrorResId = R.string.touchpad_home_gesture_error_body, ), animations = TutorialScreenConfig.Animations(educationResId = R.raw.trackpad_home_edu), - hasNextButton = isAutoProceed, ) GestureTutorialScreen( screenConfig = screenConfig, @@ -60,6 +59,7 @@ fun HomeGestureTutorialScreen( onEasterEggFinished = easterEggGestureViewModel::onEasterEggFinished, onDoneButtonClicked = onDoneButtonClicked, onBack = onBack, + onAutoProceed = onAutoProceed, ) } diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt index 0bdf99e49b1b..14eede6f6816 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt @@ -24,6 +24,7 @@ import android.widget.ImageButton import androidx.annotation.LayoutRes import androidx.compose.ui.util.fastForEachIndexed import androidx.constraintlayout.motion.widget.MotionLayout +import androidx.constraintlayout.motion.widget.MotionScene import androidx.dynamicanimation.animation.FloatValueHolder import androidx.dynamicanimation.animation.SpringAnimation import androidx.dynamicanimation.animation.SpringForce @@ -47,6 +48,7 @@ import com.android.systemui.volume.dialog.ui.viewmodel.VolumeDialogViewModel import javax.inject.Inject import kotlin.properties.Delegates import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.mapLatest @@ -55,6 +57,7 @@ private const val CLOSE_DRAWER_DELAY = 300L // Ensure roundness and color of button is updated when progress is changed by a minimum fraction. private const val BUTTON_MIN_VISIBLE_CHANGE = 0.05F +@OptIn(ExperimentalCoroutinesApi::class) @VolumeDialogScope class VolumeDialogRingerViewBinder @Inject @@ -208,6 +211,13 @@ constructor( ringerState.orientation, ringerBackgroundView, ) + drawerContainer + .getTransition(R.id.close_to_open_transition) + .setInterpolatorInfo( + MotionScene.Transition.INTERPOLATE_REFERENCE_ID, + null, + R.anim.volume_dialog_ringer_open, + ) drawerContainer.transitionToState( R.id.volume_dialog_ringer_drawer_open ) @@ -370,6 +380,12 @@ constructor( orientation: Int, ) { setTransition(R.id.close_to_open_transition) + getTransition(R.id.close_to_open_transition) + .setInterpolatorInfo( + MotionScene.Transition.INTERPOLATE_REFERENCE_ID, + null, + R.anim.volume_dialog_ringer_close, + ) updateCloseState(this, selectedIndex, orientation, ringerBackground) transitionToState(R.id.volume_dialog_ringer_drawer_close) } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java index e76401528ff6..b23efcea1c2c 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java @@ -23,6 +23,7 @@ import static android.service.notification.NotificationListenerService.REASON_AP import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL; import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED; import static android.service.notification.NotificationListenerService.REASON_PACKAGE_BANNED; +import static android.service.notification.NotificationListenerService.REASON_PACKAGE_CHANGED; import static android.service.notification.NotificationStats.DISMISSAL_BUBBLE; import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL; @@ -451,7 +452,8 @@ public class BubblesManager { public void onEntryRemoved(NotificationEntry entry, @NotifCollection.CancellationReason int reason) { if (reason == REASON_APP_CANCEL || reason == REASON_APP_CANCEL_ALL - || reason == REASON_PACKAGE_BANNED) { + || reason == REASON_PACKAGE_BANNED + || reason == REASON_PACKAGE_CHANGED) { BubblesManager.this.onEntryRemoved(entry); } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index e2cfbc6f764c..a42f5d3f67ea 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -65,6 +65,8 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static java.util.Collections.emptySet; + import android.app.Activity; import android.app.ActivityTaskManager; import android.app.IActivityTaskManager; @@ -155,6 +157,7 @@ import com.android.systemui.log.SessionTracker; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.scene.domain.interactor.SceneInteractor; import com.android.systemui.scene.shared.flag.SceneContainerFlag; +import com.android.systemui.scene.shared.model.Overlays; import com.android.systemui.scene.shared.model.Scenes; import com.android.systemui.settings.UserTracker; import com.android.systemui.shared.system.TaskStackChangeListener; @@ -190,6 +193,7 @@ import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.Random; +import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -2775,7 +2779,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { private void setPrimaryBouncerVisibility(boolean isVisible) { if (SceneContainerFlag.isEnabled()) { ObservableTransitionState transitionState = new ObservableTransitionState.Idle( - isVisible ? Scenes.Bouncer : Scenes.Lockscreen); + Scenes.Lockscreen, isVisible ? Set.of(Overlays.Bouncer) : emptySet()); when(mSceneInteractor.getTransitionState()).thenReturn( MutableStateFlow(transitionState)); onTransitionStateChanged(transitionState); diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerContentTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerContentTest.kt index 1320223cabf3..4e08596adc21 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerContentTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerContentTest.kt @@ -33,7 +33,7 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.bouncer.ui.BouncerDialogFactory -import com.android.systemui.bouncer.ui.viewmodel.bouncerSceneContentViewModelFactory +import com.android.systemui.bouncer.ui.viewmodel.bouncerOverlayContentViewModelFactory import com.android.systemui.flags.Flags import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.lifecycle.rememberViewModel @@ -98,9 +98,9 @@ class BouncerContentTest : SysuiTestCase() { BouncerContent( viewModel = rememberViewModel("test") { - kosmos.bouncerSceneContentViewModelFactory.create() + kosmos.bouncerOverlayContentViewModelFactory.create() }, - layout = BouncerSceneLayout.BESIDE_USER_SWITCHER, + layout = BouncerOverlayLayout.BESIDE_USER_SWITCHER, modifier = Modifier.fillMaxSize().testTag("BouncerContent"), dialogFactory = bouncerDialogFactory, ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt index aaf5559290df..58bae308bcdf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt @@ -881,8 +881,15 @@ class MediaCarouselControllerTest(flags: FlagsParameterization) : SysuiTestCase( var updatedVisibility = false mediaCarouselController.updateHostVisibility = { updatedVisibility = true } mediaCarouselController.mediaCarousel = mediaCarousel + kosmos.sceneInteractor.snapToScene(Scenes.Lockscreen, "") + kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( + SuccessFingerprintAuthenticationStatus(0, true) + ) + runCurrent() val job = mediaCarouselController.listenForAnyStateToGoneKeyguardTransition(this) + + kosmos.sceneInteractor.changeScene(Scenes.Gone, "") kosmos.setSceneTransition(Idle(Scenes.Gone)) verify(mediaCarousel).visibility = View.VISIBLE diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt index 6ca4ae2d2259..14b14126b7f6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.media.controls.ui.controller import android.graphics.Rect +import android.platform.test.annotations.EnableFlags import android.provider.Settings import android.testing.TestableLooper import android.view.ViewGroup @@ -36,7 +37,6 @@ import com.android.systemui.flags.DisableSceneContainer import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository -import com.android.systemui.keyguard.data.repository.keyguardRepository import com.android.systemui.keyguard.domain.interactor.keyguardInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.kosmos.testScope @@ -50,6 +50,7 @@ import com.android.systemui.res.R import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.SysuiStatusBarStateController +import com.android.systemui.statusbar.featurepods.popups.StatusBarPopupChips import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.policy.FakeConfigurationController import com.android.systemui.statusbar.policy.KeyguardStateController @@ -581,6 +582,36 @@ class MediaHierarchyManagerTest : SysuiTestCase() { } @Test + @EnableFlags(StatusBarPopupChips.FLAG_NAME) + fun testStatusBarPopupLocation() = + testScope.runTest { + mediaHierarchyManager.isMediaControlPopupShowing = true + runCurrent() + + verify(mediaCarouselController) + .onDesiredLocationChanged( + eq(MediaHierarchyManager.LOCATION_STATUS_BAR_POPUP), + nullable(), + eq(false), + anyLong(), + anyLong(), + ) + clearInvocations(mediaCarouselController) + + mediaHierarchyManager.isMediaControlPopupShowing = false + runCurrent() + + verify(mediaCarouselController) + .onDesiredLocationChanged( + eq(MediaHierarchyManager.LOCATION_QQS), + any<MediaHostState>(), + eq(false), + anyLong(), + anyLong(), + ) + } + + @Test fun testCommunalLocationVisibilityWithShadeShowing() = testScope.runTest { whenever(mediaDataManager.hasActiveMediaOrRecommendation()).thenReturn(true) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt index 99b99ee1860b..0c0ef9d5edfe 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt @@ -549,7 +549,7 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() { if (isVisible) { Scenes.Lockscreen } else { - Scenes.Bouncer + Scenes.Communal } sceneInteractor.changeScene(key, "test") sceneInteractor.setTransitionState( diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index 10886760b521..ffb861db182c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -113,7 +113,7 @@ import java.util.Map; @SmallTest // TODO(b/381263619) there are more changes and tweaks required to match the new bouncer/shade specs // Disabling for now but it will be fixed before the flag is fully ramped up. -@DisableFlags(Flags.FLAG_BOUNCER_UI_REVAMP) +@DisableFlags({Flags.FLAG_BOUNCER_UI_REVAMP, Flags.FLAG_NOTIFICATION_SHADE_BLUR}) public class ScrimControllerTest extends SysuiTestCase { @Rule public Expect mExpect = Expect.create(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index dde6e2ee1866..0d99c0e8cab8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -93,7 +93,7 @@ import com.android.systemui.navigationbar.TaskbarDelegate; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; import com.android.systemui.scene.domain.interactor.SceneInteractor; -import com.android.systemui.scene.shared.model.Scenes; +import com.android.systemui.scene.shared.model.Overlays; import com.android.systemui.shade.NotificationShadeWindowView; import com.android.systemui.shade.ShadeController; import com.android.systemui.shade.ShadeExpansionChangeEvent; @@ -927,7 +927,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { @EnableSceneContainer public void showPrimaryBouncer() { mStatusBarKeyguardViewManager.showPrimaryBouncer(false); - verify(mSceneInteractor).changeScene(eq(Scenes.Bouncer), anyString()); + verify(mSceneInteractor).showOverlay(eq(Overlays.Bouncer), anyString()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index a38d71b178e9..68d84ecaf4b1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -25,6 +25,7 @@ import static android.service.notification.NotificationListenerService.NOTIFICAT import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL; import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED; import static android.service.notification.NotificationListenerService.REASON_PACKAGE_BANNED; +import static android.service.notification.NotificationListenerService.REASON_PACKAGE_CHANGED; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.server.notification.Flags.FLAG_SCREENSHARE_NOTIFICATION_HIDING; @@ -1126,6 +1127,18 @@ public class BubblesTest extends SysuiTestCase { } @Test + public void testNotifsPackageChanged_entryListenerRemove() { + mEntryListener.onEntryAdded(mRow); + mBubbleController.updateBubble(mBubbleEntry); + + assertTrue(mBubbleController.hasBubbles()); + + // Removes the notification + mEntryListener.onEntryRemoved(mRow, REASON_PACKAGE_CHANGED); + assertFalse(mBubbleController.hasBubbles()); + } + + @Test public void removeBubble_intercepted() { mEntryListener.onEntryAdded(mRow); mBubbleController.updateBubble(mBubbleEntry); diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt index 3bfd95816cf0..4d1c271d6a41 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt @@ -34,9 +34,7 @@ import com.android.systemui.user.domain.interactor.selectedUserInteractor import com.android.systemui.user.ui.viewmodel.userSwitcherViewModel import kotlinx.coroutines.flow.StateFlow -val Kosmos.bouncerUserActionsViewModel by Fixture { - BouncerUserActionsViewModel(bouncerInteractor = bouncerInteractor) -} +val Kosmos.bouncerUserActionsViewModel by Fixture { BouncerUserActionsViewModel() } val Kosmos.bouncerUserActionsViewModelFactory by Fixture { object : BouncerUserActionsViewModel.Factory { @@ -46,8 +44,8 @@ val Kosmos.bouncerUserActionsViewModelFactory by Fixture { } } -val Kosmos.bouncerSceneContentViewModel by Fixture { - BouncerSceneContentViewModel( +val Kosmos.bouncerOverlayContentViewModel by Fixture { + BouncerOverlayContentViewModel( applicationContext = applicationContext, bouncerInteractor = bouncerInteractor, authenticationInteractor = authenticationInteractor, @@ -65,10 +63,10 @@ val Kosmos.bouncerSceneContentViewModel by Fixture { ) } -val Kosmos.bouncerSceneContentViewModelFactory by Fixture { - object : BouncerSceneContentViewModel.Factory { - override fun create(): BouncerSceneContentViewModel { - return bouncerSceneContentViewModel +val Kosmos.bouncerOverlayContentViewModelFactory by Fixture { + object : BouncerOverlayContentViewModel.Factory { + override fun create(): BouncerOverlayContentViewModel { + return bouncerOverlayContentViewModel } } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt index f0350acd83ca..3bd8729ab173 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt @@ -32,7 +32,6 @@ var Kosmos.sceneKeys by Fixture { Scenes.QuickSettings, Scenes.Shade, Scenes.Lockscreen, - Scenes.Bouncer, Scenes.Gone, Scenes.Communal, Scenes.Dream, @@ -42,7 +41,7 @@ var Kosmos.sceneKeys by Fixture { val Kosmos.initialSceneKey by Fixture { Scenes.Lockscreen } var Kosmos.overlayKeys by Fixture { - listOf(Overlays.NotificationsShade, Overlays.QuickSettingsShade) + listOf(Overlays.NotificationsShade, Overlays.QuickSettingsShade, Overlays.Bouncer) } val Kosmos.fakeOverlaysByKeys by Fixture { overlayKeys.associateWith { FakeOverlay(it) } } @@ -62,7 +61,6 @@ var Kosmos.sceneContainerConfig by Fixture { Scenes.Dream to 2, Scenes.Shade to 3, Scenes.QuickSettings to 4, - Scenes.Bouncer to 5, ) SceneContainerConfig( diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryUtil.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryUtil.kt index c95b2dcb08b6..025608a87eff 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryUtil.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryUtil.kt @@ -17,6 +17,7 @@ package com.android.systemui.scene.data.repository import com.android.compose.animation.scene.ObservableTransitionState +import com.android.compose.animation.scene.OverlayKey import com.android.compose.animation.scene.SceneKey import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState @@ -38,7 +39,7 @@ suspend fun Kosmos.setTransition( stateTransition: TransitionStep? = null, fillInStateSteps: Boolean = true, scope: TestScope = testScope, - repository: SceneContainerRepository = sceneContainerRepository + repository: SceneContainerRepository = sceneContainerRepository, ) { var state: TransitionStep? = stateTransition if (SceneContainerFlag.isEnabled) { @@ -61,7 +62,7 @@ suspend fun Kosmos.setTransition( fun Kosmos.setSceneTransition( transition: ObservableTransitionState, scope: TestScope = testScope, - repository: SceneContainerRepository = sceneContainerRepository + repository: SceneContainerRepository = sceneContainerRepository, ) { repository.setTransitionState(mutableTransitionState) mutableTransitionState.value = transition @@ -76,7 +77,7 @@ fun Transition( isInitiatedByUserInput: Boolean = false, isUserInputOngoing: Flow<Boolean> = flowOf(false), previewProgress: Flow<Float> = flowOf(0f), - isInPreviewStage: Flow<Boolean> = flowOf(false) + isInPreviewStage: Flow<Boolean> = flowOf(false), ): ObservableTransitionState.Transition { return ObservableTransitionState.Transition( fromScene = from, @@ -86,17 +87,64 @@ fun Transition( isInitiatedByUserInput = isInitiatedByUserInput, isUserInputOngoing = isUserInputOngoing, previewProgress = previewProgress, - isInPreviewStage = isInPreviewStage + isInPreviewStage = isInPreviewStage, ) } -fun Idle(currentScene: SceneKey): ObservableTransitionState.Idle { - return ObservableTransitionState.Idle(currentScene) +fun ShowOverlay( + overlay: OverlayKey, + fromScene: SceneKey, + currentOverlays: Flow<Set<OverlayKey>> = flowOf(setOf(overlay)), + progress: Flow<Float> = flowOf(0f), + isInitiatedByUserInput: Boolean = false, + isUserInputOngoing: Flow<Boolean> = flowOf(false), + previewProgress: Flow<Float> = flowOf(0f), + isInPreviewStage: Flow<Boolean> = flowOf(false), +): ObservableTransitionState.Transition { + return ObservableTransitionState.Transition.showOverlay( + overlay = overlay, + fromScene = fromScene, + currentOverlays = currentOverlays, + progress = progress, + isInitiatedByUserInput = isInitiatedByUserInput, + isUserInputOngoing = isUserInputOngoing, + previewProgress = previewProgress, + isInPreviewStage = isInPreviewStage, + ) +} + +fun HideOverlay( + overlay: OverlayKey, + toScene: SceneKey, + currentOverlays: Flow<Set<OverlayKey>> = flowOf(setOf(overlay)), + progress: Flow<Float> = flowOf(0f), + isInitiatedByUserInput: Boolean = false, + isUserInputOngoing: Flow<Boolean> = flowOf(false), + previewProgress: Flow<Float> = flowOf(0f), + isInPreviewStage: Flow<Boolean> = flowOf(false), +): ObservableTransitionState.Transition { + return ObservableTransitionState.Transition.hideOverlay( + overlay = overlay, + toScene = toScene, + currentOverlays = currentOverlays, + progress = progress, + isInitiatedByUserInput = isInitiatedByUserInput, + isUserInputOngoing = isUserInputOngoing, + previewProgress = previewProgress, + isInPreviewStage = isInPreviewStage, + ) +} + +fun Idle( + currentScene: SceneKey, + currentOverlays: Set<OverlayKey> = emptySet(), +): ObservableTransitionState.Idle { + return ObservableTransitionState.Idle(currentScene, currentOverlays) } private fun getStateWithUndefined( sceneTransition: ObservableTransitionState, - state: TransitionStep + state: TransitionStep, ): TransitionStep { return when (sceneTransition) { is ObservableTransitionState.Idle -> { @@ -109,7 +157,7 @@ private fun getStateWithUndefined( state.to }, value = state.value, - transitionState = state.transitionState + transitionState = state.transitionState, ) } is ObservableTransitionState.Transition -> { @@ -127,7 +175,7 @@ private fun getStateWithUndefined( state.from }, value = state.value, - transitionState = state.transitionState + transitionState = state.transitionState, ) } else -> state diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeSceneDataSource.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeSceneDataSource.kt index f9917ac680e0..66a190c0bb99 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeSceneDataSource.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeSceneDataSource.kt @@ -115,8 +115,14 @@ class FakeSceneDataSource(initialSceneKey: SceneKey, val testScope: TestScope) : * If [force] is `true`, there will be no check that [isPaused] is true. * * If [expectedScene] is provided, will assert that it's indeed the latest called. + * + * If [expectedOverlays] is provided, will assert they are indeed present. */ - fun unpause(force: Boolean = false, expectedScene: SceneKey? = null) { + fun unpause( + force: Boolean = false, + expectedScene: SceneKey? = null, + expectedOverlays: Set<OverlayKey>? = null, + ) { check(force || _isPaused) { "Can't unpause what's already not paused!" } _isPaused = false @@ -128,9 +134,12 @@ class FakeSceneDataSource(initialSceneKey: SceneKey, val testScope: TestScope) : check(expectedScene == null || currentScene.value == expectedScene) { """ Unexpected scene while unpausing. - Expected $expectedScene but was $currentScene. + Expected $expectedScene but was ${currentScene.value}. """ .trimIndent() } + check(expectedOverlays == null || expectedOverlays == currentOverlays.value) { + "Expected $expectedOverlays, but instead found overlays ${currentOverlays.value}." + } } } diff --git a/services/core/java/com/android/server/om/OverlayManagerSettings.java b/services/core/java/com/android/server/om/OverlayManagerSettings.java index cb01727524da..1ac971104486 100644 --- a/services/core/java/com/android/server/om/OverlayManagerSettings.java +++ b/services/core/java/com/android/server/om/OverlayManagerSettings.java @@ -544,6 +544,7 @@ final class OverlayManagerSettings { // and overwritten. throw new XmlPullParserException("old version " + oldVersion + "; ignoring"); case 3: + case 4: // Upgrading from version 3 to 5 is not a breaking change so do not ignore the // overlay file. return; diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java index 6bec34ef7063..38aa57f785e5 100644 --- a/services/core/java/com/android/server/pm/DeletePackageHelper.java +++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java @@ -24,6 +24,7 @@ import static android.content.pm.PackageManager.DELETE_KEEP_DATA; import static android.content.pm.PackageManager.DELETE_SUCCEEDED; import static android.content.pm.PackageManager.MATCH_KNOWN_PACKAGES; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.os.UserHandle.USER_ALL; import static com.android.server.pm.InstructionSets.getAppDexInstructionSets; import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION; @@ -122,7 +123,7 @@ final class DeletePackageHelper { final boolean res; final int removeUser = (deleteFlags & PackageManager.DELETE_ALL_USERS) != 0 - ? UserHandle.USER_ALL : userId; + ? USER_ALL : userId; final PackageSetting uninstalledPs; final PackageSetting disabledSystemPs; @@ -183,7 +184,7 @@ final class DeletePackageHelper { if (libraryInfo != null) { boolean flagSdkLibIndependence = Flags.sdkLibIndependence(); for (int currUserId : allUsers) { - if (removeUser != UserHandle.USER_ALL && removeUser != currUserId) { + if (removeUser != USER_ALL && removeUser != currUserId) { continue; } var libClientPackagesPair = computer.getPackagesUsingSharedLibrary( @@ -227,7 +228,7 @@ final class DeletePackageHelper { && ((deleteFlags & PackageManager.DELETE_SYSTEM_APP) == 0)) { // We're downgrading a system app, which will apply to all users, so // freeze them all during the downgrade - freezeUser = UserHandle.USER_ALL; + freezeUser = USER_ALL; priorUserStates = new SparseArray<>(); for (int i = 0; i < allUsers.length; i++) { PackageUserState userState = uninstalledPs.readUserState(allUsers[i]); @@ -421,7 +422,7 @@ final class DeletePackageHelper { if (PackageManagerServiceUtils.isSystemApp(ps)) { final boolean deleteSystem = (flags & PackageManager.DELETE_SYSTEM_APP) != 0; final boolean deleteAllUsers = - user == null || user.getIdentifier() == UserHandle.USER_ALL; + user == null || user.getIdentifier() == USER_ALL; if ((!deleteSystem || deleteAllUsers) && disabledPs == null) { Slog.w(TAG, "Attempt to delete unknown system package " + ps.getPkg().getPackageName()); @@ -464,9 +465,9 @@ final class DeletePackageHelper { Manifest.permission.SUSPEND_APPS, packageName, userId) == PERMISSION_GRANTED); } - final int userId = user == null ? UserHandle.USER_ALL : user.getIdentifier(); + final int userId = user == null ? USER_ALL : user.getIdentifier(); // Remember which users are affected, before the installed states are modified - outInfo.mRemovedUsers = userId == UserHandle.USER_ALL + outInfo.mRemovedUsers = userId == USER_ALL ? ps.queryUsersInstalledOrHasData(allUserHandles) : new int[]{userId}; outInfo.populateBroadcastUsers(ps); @@ -479,7 +480,7 @@ final class DeletePackageHelper { outInfo.mRemovedPackageVersionCode = ps.getVersionCode(); if ((!systemApp || (flags & PackageManager.DELETE_SYSTEM_APP) != 0) - && userId != UserHandle.USER_ALL) { + && userId != USER_ALL) { // The caller is asking that the package only be deleted for a single // user. To do this, we just mark its uninstalled state and delete // its data. If this is a system app, we only allow this to happen if @@ -552,7 +553,7 @@ final class DeletePackageHelper { for (final int affectedUserId : outInfo.mRemovedUsers) { if (hadSuspendAppsPermission.get(affectedUserId)) { mPm.unsuspendForSuspendingPackage(snapshot, packageName, - affectedUserId /*suspendingUserId*/, true /*inAllUsers*/); + affectedUserId /*suspendingUserId*/, USER_ALL); mPm.removeAllDistractingPackageRestrictions(snapshot, affectedUserId); } } @@ -590,7 +591,7 @@ final class DeletePackageHelper { @GuardedBy("mPm.mLock") private void markPackageUninstalledForUserLPw(PackageSetting ps, UserHandle user, int flags) { - final int[] userIds = (user == null || user.getIdentifier() == UserHandle.USER_ALL) + final int[] userIds = (user == null || user.getIdentifier() == USER_ALL) ? mUserManagerInternal.getUserIds() : new int[] {user.getIdentifier()}; for (int nextUserId : userIds) { @@ -687,7 +688,7 @@ final class DeletePackageHelper { flags |= PackageManager.DELETE_KEEP_DATA; } try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) { - deleteInstalledPackageLIF(deletedPs, UserHandle.USER_ALL, true, flags, allUserHandles, + deleteInstalledPackageLIF(deletedPs, USER_ALL, true, flags, allUserHandles, outInfo, writeSettings); } } diff --git a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java index ed568b823159..f5230c57c597 100644 --- a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java +++ b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java @@ -689,7 +689,7 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal { final int suspendingUserId = crossUserSuspensionEnabledRo() ? UserHandle.USER_SYSTEM : affectedUser; mService.unsuspendForSuspendingPackage( - snapshot(), PLATFORM_PACKAGE_NAME, suspendingUserId, /* inAllUsers= */ false); + snapshot(), PLATFORM_PACKAGE_NAME, suspendingUserId, affectedUser); } @Override diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 91a1c9c12cb8..021da6f27deb 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -34,6 +34,7 @@ import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET; import static android.crashrecovery.flags.Flags.refactorCrashrecovery; import static android.os.Process.INVALID_UID; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; +import static android.os.UserHandle.USER_ALL; import static android.os.storage.StorageManager.FLAG_STORAGE_CE; import static android.os.storage.StorageManager.FLAG_STORAGE_DE; import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL; @@ -1587,13 +1588,13 @@ public class PackageManagerService implements PackageSender, TestUtilityService } void scheduleWritePackageRestrictions(UserHandle user) { - final int userId = user == null ? UserHandle.USER_ALL : user.getIdentifier(); + final int userId = user == null ? USER_ALL : user.getIdentifier(); scheduleWritePackageRestrictions(userId); } void scheduleWritePackageRestrictions(@CanBeALL @UserIdInt int userId) { invalidatePackageInfoCache(); - if (userId == UserHandle.USER_ALL) { + if (userId == USER_ALL) { synchronized (mDirtyUsers) { for (int aUserId : mUserManager.getUserIds()) { mDirtyUsers.add(aUserId); @@ -1806,7 +1807,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService private void installAllowlistedSystemPackages() { if (mUserManager.installWhitelistedSystemPackages(isFirstBoot(), isDeviceUpgrading(), mExistingPackages)) { - scheduleWritePackageRestrictions(UserHandle.USER_ALL); + scheduleWritePackageRestrictions(USER_ALL); scheduleWriteSettings(); } } @@ -2393,7 +2394,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService final PackageSetting ps = packageSettings.valueAt(i); if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, ps.getVolumeUuid())) { // No apps are running this early, so no need to freeze - mAppDataHelper.clearAppDataLIF(ps.getPkg(), UserHandle.USER_ALL, + mAppDataHelper.clearAppDataLIF(ps.getPkg(), USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY | Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES); @@ -3076,7 +3077,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService @NonNull int[] resolveUserIds(@CanBeALL @UserIdInt int userId) { - return (userId == UserHandle.USER_ALL) ? mUserManager.getUserIds() : new int[] { userId }; + return (userId == USER_ALL) ? mUserManager.getUserIds() : new int[]{userId}; } private void setUpInstantAppInstallerActivityLP(ActivityInfo installerActivity) { @@ -3109,7 +3110,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService } void killApplication(String pkgName, @AppIdInt int appId, String reason, int exitInfoReason) { - killApplication(pkgName, appId, UserHandle.USER_ALL, reason, exitInfoReason); + killApplication(pkgName, appId, USER_ALL, reason, exitInfoReason); } void killApplication(String pkgName, @AppIdInt int appId, @@ -3229,23 +3230,24 @@ public class PackageManagerService implements PackageSender, TestUtilityService } /** - * @param inAllUsers Whether to unsuspend packages suspended by the given package in other - * users. This flag is only used when cross-user suspension is enabled. + * @param suspendingUserId The user that has suspended apps using the suspending package. + * @param targetUserId The user whose apps should be unsuspended. Pass {@code USER_ALL} to + * unsuspend for all users. */ void unsuspendForSuspendingPackage(@NonNull Computer computer, String suspendingPackage, - @UserIdInt int suspendingUserId, boolean inAllUsers) { + @UserIdInt int suspendingUserId, @CanBeALL @UserIdInt int targetUserId) { // TODO: This can be replaced by a special parameter to iterate all packages, rather than // this weird pre-collect of all packages. final String[] allPackages = computer.getPackageStates().keySet().toArray(new String[0]); final Predicate<UserPackage> suspenderPredicate = UserPackage.of(suspendingUserId, suspendingPackage)::equals; - if (!crossUserSuspensionEnabledRo() || !inAllUsers) { + if (!crossUserSuspensionEnabledRo() || targetUserId != USER_ALL) { mSuspendPackageHelper.removeSuspensionsBySuspendingPackage(computer, - allPackages, suspenderPredicate, suspendingUserId); + allPackages, suspenderPredicate, targetUserId); } else { - for (int targetUserId: mUserManager.getUserIds()) { + for (int user : mUserManager.getUserIds()) { mSuspendPackageHelper.removeSuspensionsBySuspendingPackage( - computer, allPackages, suspenderPredicate, targetUserId); + computer, allPackages, suspenderPredicate, user); } } } @@ -3382,7 +3384,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService && !snapshot.isCallerSameApp(packageName, callingUid)) { return false; } - return isPackageDeviceAdmin(packageName, UserHandle.USER_ALL); + return isPackageDeviceAdmin(packageName, USER_ALL); } // TODO(b/261957226): centralise this logic in DPM @@ -3406,7 +3408,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService // Does it contain a device admin for any user? int[] allUsers = mUserManager.getUserIds(); int[] targetUsers; - if (userId == UserHandle.USER_ALL) { + if (userId == USER_ALL) { targetUsers = allUsers; } else { targetUsers = new int[]{userId}; @@ -4153,7 +4155,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService // This app should not generally be allowed to get disabled by the UI, but // if it ever does, we don't want to end up with some of the user's apps // permanently suspended. - unsuspendForSuspendingPackage(computer, packageName, userId, true /* inAllUsers */); + unsuspendForSuspendingPackage(computer, packageName, userId, USER_ALL); removeAllDistractingPackageRestrictions(computer, userId); } success = true; @@ -4244,9 +4246,9 @@ public class PackageManagerService implements PackageSender, TestUtilityService }; mContext.getContentResolver().registerContentObserver(android.provider.Settings.Global .getUriFor(Global.ENABLE_EPHEMERAL_FEATURE), - false, co, UserHandle.USER_ALL); + false, co, USER_ALL); mContext.getContentResolver().registerContentObserver(android.provider.Settings.Secure - .getUriFor(Secure.INSTANT_APPS_ENABLED), false, co, UserHandle.USER_ALL); + .getUriFor(Secure.INSTANT_APPS_ENABLED), false, co, USER_ALL); co.onChange(true); mAppsFilter.onSystemReady(LocalServices.getService(PackageManagerInternal.class)); @@ -4774,7 +4776,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService final Computer snapshot = snapshotComputer(); final AndroidPackage pkg = snapshot.getPackage(packageName); try (PackageFreezer ignored = - freezePackage(packageName, UserHandle.USER_ALL, + freezePackage(packageName, USER_ALL, "clearApplicationProfileData", ApplicationExitInfo.REASON_OTHER, null /* request */)) { try (PackageManagerTracedLock installLock = mInstallLock.acquireLock()) { @@ -4820,7 +4822,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService public void run() { mHandler.removeCallbacks(this); final boolean succeeded; - try (PackageFreezer freezer = freezePackage(packageName, UserHandle.USER_ALL, + try (PackageFreezer freezer = freezePackage(packageName, USER_ALL, "clearApplicationUserData", ApplicationExitInfo.REASON_USER_REQUESTED, null /* request */, /* waitAppKilled= */ true)) { @@ -4847,7 +4849,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService == PERMISSION_GRANTED) { final Computer snapshot = snapshotComputer(); unsuspendForSuspendingPackage( - snapshot, packageName, userId, true /* inAllUsers */); + snapshot, packageName, userId, USER_ALL); removeAllDistractingPackageRestrictions(snapshot, userId); synchronized (mLock) { flushPackageRestrictionsAsUserInternalLocked(userId); @@ -6372,13 +6374,13 @@ public class PackageManagerService implements PackageSender, TestUtilityService if (mComponentResolver.updateMimeGroup(snapshotComputer(), packageName, mimeGroup)) { Binder.withCleanCallingIdentity(() -> { mPreferredActivityHelper.clearPackagePreferredActivities(packageName, - UserHandle.USER_ALL); + USER_ALL); // Send the ACTION_PACKAGE_CHANGED when the mimeGroup has changes final Computer snapShot = snapshotComputer(); final ArrayList<String> components = new ArrayList<>( Collections.singletonList(packageName)); final int appId = packageState.getAppId(); - final int[] userIds = resolveUserIds(UserHandle.USER_ALL); + final int[] userIds = resolveUserIds(USER_ALL); final String reason = "The mimeGroup is changed"; for (int i = 0; i < userIds.length; i++) { final PackageUserStateInternal pkgUserState = diff --git a/services/tests/mockingservicestests/res/xml/expectedUserWakeupList_1.xml b/services/tests/mockingservicestests/res/xml/expectedUserWakeupList_1.xml new file mode 100644 index 000000000000..21e6dab518fc --- /dev/null +++ b/services/tests/mockingservicestests/res/xml/expectedUserWakeupList_1.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright 2025 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<users version="1"> + <user user_id="10" /> + <user user_id="11" /> +</users>
\ No newline at end of file diff --git a/services/tests/mockingservicestests/res/xml/expectedUserWakeupList_2.xml b/services/tests/mockingservicestests/res/xml/expectedUserWakeupList_2.xml new file mode 100644 index 000000000000..d0b371f060da --- /dev/null +++ b/services/tests/mockingservicestests/res/xml/expectedUserWakeupList_2.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright 2025 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<users version="1"> + <user user_id="10" /> +</users>
\ No newline at end of file diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/UserWakeupStoreTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/UserWakeupStoreTest.java index 72883e269a65..240284a42406 100644 --- a/services/tests/mockingservicestests/src/com/android/server/alarm/UserWakeupStoreTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/UserWakeupStoreTest.java @@ -23,15 +23,21 @@ import static com.android.server.alarm.UserWakeupStore.USER_START_TIME_DEVIATION import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import android.content.res.AssetManager; +import android.content.res.XmlResourceParser; import android.os.Environment; import android.os.FileUtils; import android.os.SystemClock; +import android.util.Xml; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import com.android.internal.os.BackgroundThread; +import com.android.internal.util.XmlUtils; +import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.testing.ExtendedMockitoRule; import org.junit.After; @@ -40,8 +46,11 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; +import org.xmlpull.v1.XmlPullParserException; import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.concurrent.ExecutorService; @@ -55,6 +64,7 @@ public class UserWakeupStoreTest { private static final File TEST_SYSTEM_DIR = new File(InstrumentationRegistry .getInstrumentation().getContext().getDataDir(), "alarmsTestDir"); private static final File ROOT_DIR = new File(TEST_SYSTEM_DIR, UserWakeupStore.ROOT_DIR_NAME); + private static final String USERS_FILE_NAME = "usersWithAlarmClocks.xml"; private ExecutorService mMockExecutorService = null; UserWakeupStore mUserWakeupStore; @@ -105,7 +115,7 @@ public class UserWakeupStoreTest { Collections.sort(userWakeups); assertEquals(userIds, userWakeups); - final File file = new File(ROOT_DIR , "usersWithAlarmClocks.xml"); + final File file = new File(ROOT_DIR, USERS_FILE_NAME); assertTrue(file.exists()); } @@ -178,5 +188,47 @@ public class UserWakeupStoreTest { assertTrue(mUserWakeupStore.getWakeupTimeForUser(USER_ID_2) - realtime < 3 * BUFFER_TIME_MS + USER_START_TIME_DEVIATION_LIMIT_MS); } - //TODO: b/330264023 - Add tests for I/O in usersWithAlarmClocks.xml. + + @Test + public void testWriteWakeups_xmlIsOrdered() { + mUserWakeupStore.addUserWakeup(USER_ID_1, TEST_TIMESTAMP - 19_000); + mUserWakeupStore.addUserWakeup(USER_ID_2, TEST_TIMESTAMP - 7_000); + assertFileContentsMatchExpectedXml("res/xml/expectedUserWakeupList_1.xml"); + } + + @Test + public void testWriteWakeups_containsOneEntryPerUser() { + mUserWakeupStore.addUserWakeup(USER_ID_1, TEST_TIMESTAMP - 19_000); + mUserWakeupStore.addUserWakeup(USER_ID_1, TEST_TIMESTAMP - 7_000); + assertFileContentsMatchExpectedXml("res/xml/expectedUserWakeupList_2.xml"); + } + + private static void assertFileContentsMatchExpectedXml(String expectedContentsFile) { + final File actual = new File(ROOT_DIR, USERS_FILE_NAME); + AssetManager assetManager = + InstrumentationRegistry.getInstrumentation().getContext().getAssets(); + try (FileInputStream actualFis = new FileInputStream(actual)) { + final TypedXmlPullParser actualParser = Xml.resolvePullParser(actualFis); + final XmlResourceParser expectedParser = assetManager.openXmlResourceParser( + expectedContentsFile); + for (XmlUtils.nextElement(expectedParser), XmlUtils.nextElement(actualParser); + actualParser.getEventType() != XmlResourceParser.END_DOCUMENT + && expectedParser.getEventType() != XmlResourceParser.END_DOCUMENT; + XmlUtils.nextElement(actualParser), XmlUtils.nextElement(expectedParser)) { + assertEquals("Event types differ ", expectedParser.getEventType(), + actualParser.getEventType()); + for (int i = 0; i < expectedParser.getAttributeCount(); i++) { + assertEquals("Attribute names differ at index " + i, + expectedParser.getAttributeName(i), actualParser.getAttributeName(i)); + assertEquals("Attribute values differ at index " + i, + expectedParser.getAttributeValue(i), actualParser.getAttributeValue(i)); + } + } + // Ensure they are both at the end of document + assertEquals("One of the parsers has not reached the EOF", + expectedParser.getEventType(), actualParser.getEventType()); + } catch (IOException | XmlPullParserException e) { + fail(e.getLocalizedMessage()); + } + } } diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java index ad3855f4c28f..b1cf9059fdc4 100644 --- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java @@ -37,10 +37,12 @@ import android.text.TextUtils; import android.util.Xml; import androidx.annotation.NonNull; -import androidx.test.runner.AndroidJUnit4; import com.android.modules.utils.TypedXmlPullParser; +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -56,7 +58,7 @@ import java.util.Set; import javax.annotation.Nullable; -@RunWith(AndroidJUnit4.class) +@RunWith(JUnitParamsRunner.class) public class OverlayManagerSettingsTests { private OverlayManagerSettings mSettings; private static final int USER_0 = 0; @@ -439,6 +441,39 @@ public class OverlayManagerSettingsTests { } @Test + @Parameters(method = "getPreviousVersions") + public void testRestoreWithPreviousVersion(int version) throws Exception { + final String xml = + "<?xml version='1.0' encoding='utf-8' standalone='yes'?>\n" + + "<overlays version='" + version + "'>\n" + + "<item packageName='com.test.overlay'\n" + + " overlayName='test'\n" + + " userId='1234'\n" + + " targetPackageName='com.test.target'\n" + + " baseCodePath='/data/app/com.test.overlay-1/base.apk'\n" + + " state='" + STATE_DISABLED + "'\n" + + " isEnabled='false'\n" + + " category='test-category'\n" + + " isStatic='false'\n" + + " priority='0' />\n" + + "</overlays>\n"; + ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes(UTF_8)); + + mSettings.restore(is); + final OverlayIdentifier identifier = new OverlayIdentifier("com.test.overlay", "test"); + OverlayInfo oi = mSettings.getOverlayInfo(identifier, 1234); + assertNotNull(oi); + assertEquals("com.test.overlay", oi.packageName); + assertEquals("test", oi.overlayName); + assertEquals("com.test.target", oi.targetPackageName); + assertEquals("/data/app/com.test.overlay-1/base.apk", oi.baseCodePath); + assertEquals(1234, oi.userId); + assertEquals(STATE_DISABLED, oi.state); + assertFalse(mSettings.getEnabled(identifier, 1234)); + assertTrue(oi.constraints.isEmpty()); + } + + @Test public void testPersistAndRestore() throws Exception { insertSetting(OVERLAY_A_USER0); insertSetting(OVERLAY_B_USER1); @@ -585,4 +620,11 @@ public class OverlayManagerSettingsTests { TextUtils.join(",", expected), TextUtils.join(",", actual))); } } + + private static Integer[] getPreviousVersions() { + return new Integer[]{ + 3, + 4, + }; + } } diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java index 42279e40fa33..04335df8c454 100644 --- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java +++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java @@ -20,14 +20,14 @@ import static android.os.VibrationAttributes.USAGE_RINGTONE; import static android.os.VibrationEffect.Composition.PRIMITIVE_CLICK; import static android.os.VibrationEffect.Composition.PRIMITIVE_SPIN; import static android.os.VibrationEffect.Composition.PRIMITIVE_TICK; +import static android.os.VibrationEffect.EFFECT_CLICK; +import static android.os.VibrationEffect.EFFECT_TICK; import static android.os.VibrationEffect.VibrationParameter.targetAmplitude; import static android.os.VibrationEffect.VibrationParameter.targetFrequency; import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; @@ -189,7 +189,7 @@ public class VibrationThreadTest { public void vibrate_noVibrator_ignoresVibration() { mVibratorProviders.clear(); CombinedVibration effect = CombinedVibration.createParallel( - VibrationEffect.get(VibrationEffect.EFFECT_CLICK)); + VibrationEffect.get(EFFECT_CLICK)); HalVibration vibration = startThreadAndDispatcher(effect); waitForCompletion(); @@ -200,8 +200,8 @@ public class VibrationThreadTest { @Test public void vibrate_missingVibrators_ignoresVibration() { CombinedVibration effect = CombinedVibration.startSequential() - .addNext(2, VibrationEffect.get(VibrationEffect.EFFECT_CLICK)) - .addNext(3, VibrationEffect.get(VibrationEffect.EFFECT_TICK)) + .addNext(2, VibrationEffect.get(EFFECT_CLICK)) + .addNext(3, VibrationEffect.get(EFFECT_TICK)) .combine(); HalVibration vibration = startThreadAndDispatcher(effect); waitForCompletion(); @@ -222,11 +222,12 @@ public class VibrationThreadTest { verify(mManagerHooks).noteVibratorOff(eq(UID)); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong()); verifyCallbacksTriggered(vibration, Status.FINISHED); - assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); + assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse(); - assertEquals(Arrays.asList(expectedOneShot(10)), - mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id)); - assertEquals(expectedAmplitudes(100), mVibratorProviders.get(VIBRATOR_ID).getAmplitudes()); + assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id)) + .containsExactly(expectedOneShot(10)).inOrder(); + assertThat(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes()) + .containsExactlyElementsIn(expectedAmplitudes(100)).inOrder(); } @Test @@ -239,11 +240,11 @@ public class VibrationThreadTest { verify(mManagerHooks).noteVibratorOff(eq(UID)); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong()); verifyCallbacksTriggered(vibration, Status.FINISHED); - assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); + assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse(); - assertEquals(Arrays.asList(expectedOneShot(10)), - mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id)); - assertTrue(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes().isEmpty()); + assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id)) + .containsExactly(expectedOneShot(10)).inOrder(); + assertThat(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes()).isEmpty(); } @Test @@ -259,12 +260,12 @@ public class VibrationThreadTest { verify(mManagerHooks).noteVibratorOff(eq(UID)); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong()); verifyCallbacksTriggered(vibration, Status.FINISHED); - assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); + assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse(); - assertEquals(Arrays.asList(expectedOneShot(15)), - mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id)); - assertEquals(expectedAmplitudes(1, 2, 3), - mVibratorProviders.get(VIBRATOR_ID).getAmplitudes()); + assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id)) + .containsExactly(expectedOneShot(15)).inOrder(); + assertThat(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes()) + .containsExactlyElementsIn(expectedAmplitudes(1, 2, 3)).inOrder(); } @Test @@ -285,11 +286,12 @@ public class VibrationThreadTest { waitForCompletion(); verify(mStatsLoggerMock, never()).logVibrationParamRequestTimeout(UID); - assertEquals(Arrays.asList(expectedOneShot(15)), - mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id)); + assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id)) + .containsExactly(expectedOneShot(15)).inOrder(); List<Float> amplitudes = mVibratorProviders.get(VIBRATOR_ID).getAmplitudes(); for (int i = 0; i < amplitudes.size(); i++) { - assertTrue(amplitudes.get(i) < 1 / 255f); + assertWithMessage("For amplitude index %s", i) + .that(amplitudes.get(i)).isLessThan(1 / 255f); } } @@ -309,10 +311,10 @@ public class VibrationThreadTest { waitForCompletion(); verify(mStatsLoggerMock).logVibrationParamRequestTimeout(UID); - assertEquals(Arrays.asList(expectedOneShot(15)), - mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id)); - assertEquals(expectedAmplitudes(1, 1, 1), - mVibratorProviders.get(VIBRATOR_ID).getAmplitudes()); + assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id)) + .containsExactly(expectedOneShot(15)).inOrder(); + assertThat(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes()) + .containsExactlyElementsIn(expectedAmplitudes(1, 1, 1)).inOrder(); } @Test @@ -325,31 +327,33 @@ public class VibrationThreadTest { VibrationEffect effect = VibrationEffect.createWaveform(new long[]{5, 5, 5}, amplitudes, 0); HalVibration vibration = startThreadAndDispatcher(effect); - assertTrue( + assertThat( waitUntil(() -> fakeVibrator.getAmplitudes().size() > 2 * amplitudes.length, - TEST_TIMEOUT_MILLIS)); + TEST_TIMEOUT_MILLIS)).isTrue(); // Vibration still running after 2 cycles. - assertTrue(mThread.isRunningVibrationId(vibration.id)); - assertTrue(mControllers.get(VIBRATOR_ID).isVibrating()); + assertThat(mThread.isRunningVibrationId(vibration.id)).isTrue(); + assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isTrue(); Vibration.EndInfo cancelVibrationInfo = new Vibration.EndInfo(Status.CANCELLED_SUPERSEDED, new CallerInfo(VibrationAttributes.createForUsage(VibrationAttributes.USAGE_ALARM), /* uid= */ 1, /* deviceId= */ -1, /* opPkg= */ null, /* reason= */ null)); mVibrationConductor.notifyCancelled(cancelVibrationInfo, /* immediate= */ false); waitForCompletion(); - assertFalse(mThread.isRunningVibrationId(vibration.id)); + assertThat(mThread.isRunningVibrationId(vibration.id)).isFalse(); verify(mManagerHooks).noteVibratorOn(eq(UID), anyLong()); verify(mManagerHooks).noteVibratorOff(eq(UID)); verifyCallbacksTriggered(vibration, Status.CANCELLED_SUPERSEDED); - assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); + assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse(); List<Float> playedAmplitudes = fakeVibrator.getAmplitudes(); - assertFalse(fakeVibrator.getEffectSegments(vibration.id).isEmpty()); - assertFalse(playedAmplitudes.isEmpty()); + assertThat(fakeVibrator.getEffectSegments(vibration.id)).isNotEmpty(); + assertThat(playedAmplitudes).isNotEmpty(); for (int i = 0; i < playedAmplitudes.size(); i++) { - assertEquals(amplitudes[i % amplitudes.length] / 255f, playedAmplitudes.get(i), 1e-5); + assertWithMessage("For amplitude index %s", i) + .that(amplitudes[i % amplitudes.length] / 255f) + .isWithin(1e-5f).of(playedAmplitudes.get(i)); } } @@ -364,15 +368,16 @@ public class VibrationThreadTest { new long[]{1, 10, 100}, amplitudes, 0); HalVibration vibration = startThreadAndDispatcher(effect); - assertTrue(waitUntil(() -> !fakeVibrator.getAmplitudes().isEmpty(), TEST_TIMEOUT_MILLIS)); + assertThat(waitUntil(() -> !fakeVibrator.getAmplitudes().isEmpty(), TEST_TIMEOUT_MILLIS)) + .isTrue(); mVibrationConductor.notifyCancelled( new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false); waitForCompletion(); verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_USER); - assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); - assertEquals(Arrays.asList(expectedOneShot(5000)), - fakeVibrator.getEffectSegments(vibration.id)); + assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse(); + assertThat(fakeVibrator.getEffectSegments(vibration.id)) + .containsExactly(expectedOneShot(5000)).inOrder(); } @Test @@ -391,7 +396,7 @@ public class VibrationThreadTest { assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse(); assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id)) - .isEqualTo(expectedOneShots(100L, 150L)); + .containsExactlyElementsIn(expectedOneShots(100L, 150L)).inOrder(); } @Test @@ -412,7 +417,7 @@ public class VibrationThreadTest { assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse(); assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id)) - .isEqualTo(expectedOneShots(200L, 50L)); + .containsExactlyElementsIn(expectedOneShots(200L, 50L)).inOrder(); } @EnableFlags(Flags.FLAG_FIX_VIBRATION_THREAD_CALLBACK_HANDLING) @@ -433,8 +438,8 @@ public class VibrationThreadTest { // 300ms ON (100ms + 200ms looping to the start and skipping first 0ms) // 150ms ON (100ms + 50ms, skips 0ms) // 300ms ON (100ms + 200ms looping to the start and skipping first 0ms) - assertTrue(waitUntil(() -> fakeVibrator.getEffectSegments(vibration.id).size() >= 5, - 5000L + TEST_TIMEOUT_MILLIS)); + assertThat(waitUntil(() -> fakeVibrator.getEffectSegments(vibration.id).size() >= 5, + 5000L + TEST_TIMEOUT_MILLIS)).isTrue(); mVibrationConductor.notifyCancelled( new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false); waitForCompletion(); @@ -444,7 +449,8 @@ public class VibrationThreadTest { assertThat( mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id).subList(0, 5)) - .isEqualTo(expectedOneShots(200L, 150L, 300L, 150L, 300L)); + .containsExactlyElementsIn(expectedOneShots(200L, 150L, 300L, 150L, 300L)) + .inOrder(); } @EnableFlags(Flags.FLAG_FIX_VIBRATION_THREAD_CALLBACK_HANDLING) @@ -468,7 +474,7 @@ public class VibrationThreadTest { // First callback ignored, did not cause the vibrator to turn back on during the 400ms step. assertThat(fakeVibrator.getEffectSegments(vibration.id)) - .isEqualTo(expectedOneShots(200L, 400L)); + .containsExactlyElementsIn(expectedOneShots(200L, 400L)).inOrder(); } @Test @@ -490,16 +496,16 @@ public class VibrationThreadTest { .compose(); HalVibration vibration = startThreadAndDispatcher(repeatingEffect); - assertTrue(waitUntil(() -> !fakeVibrator.getEffectSegments(vibration.id).isEmpty(), - TEST_TIMEOUT_MILLIS)); + assertThat(waitUntil(() -> !fakeVibrator.getEffectSegments(vibration.id).isEmpty(), + TEST_TIMEOUT_MILLIS)).isTrue(); mVibrationConductor.notifyCancelled( new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false); waitForCompletion(); // PWLE size max was used to generate a single vibrate call with 10 segments. verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_USER); - assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); - assertEquals(10, fakeVibrator.getEffectSegments(vibration.id).size()); + assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse(); + assertThat(fakeVibrator.getEffectSegments(vibration.id)).hasSize(10); } @Test @@ -519,16 +525,16 @@ public class VibrationThreadTest { .compose(); HalVibration vibration = startThreadAndDispatcher(repeatingEffect); - assertTrue(waitUntil(() -> !fakeVibrator.getEffectSegments(vibration.id).isEmpty(), - TEST_TIMEOUT_MILLIS)); + assertThat(waitUntil(() -> !fakeVibrator.getEffectSegments(vibration.id).isEmpty(), + TEST_TIMEOUT_MILLIS)).isTrue(); mVibrationConductor.notifyCancelled( new Vibration.EndInfo(Status.CANCELLED_BY_SCREEN_OFF), /* immediate= */ false); waitForCompletion(); // Composition size max was used to generate a single vibrate call with 10 primitives. verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_SCREEN_OFF); - assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); - assertEquals(10, fakeVibrator.getEffectSegments(vibration.id).size()); + assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse(); + assertThat(fakeVibrator.getEffectSegments(vibration.id)).hasSize(10); } @Test @@ -542,15 +548,16 @@ public class VibrationThreadTest { new long[]{5000, 500, 50}, amplitudes, 0); HalVibration vibration = startThreadAndDispatcher(effect); - assertTrue(waitUntil(() -> !fakeVibrator.getAmplitudes().isEmpty(), TEST_TIMEOUT_MILLIS)); + assertThat(waitUntil(() -> !fakeVibrator.getAmplitudes().isEmpty(), TEST_TIMEOUT_MILLIS)) + .isTrue(); mVibrationConductor.notifyCancelled( new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false); waitForCompletion(); verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_USER); - assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); - assertEquals(Arrays.asList(expectedOneShot(5550)), - fakeVibrator.getEffectSegments(vibration.id)); + assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse(); + assertThat(fakeVibrator.getEffectSegments(vibration.id)) + .containsExactly(expectedOneShot(5550)).inOrder(); } @LargeTest @@ -566,23 +573,24 @@ public class VibrationThreadTest { /* amplitudes= */ new int[]{1, 2}, /* repeat= */ 0); HalVibration vibration = startThreadAndDispatcher(effect); - assertTrue(waitUntil(() -> fakeVibrator.getEffectSegments(vibration.id).size() > 1, - expectedOnDuration + TEST_TIMEOUT_MILLIS)); + assertThat(waitUntil(() -> fakeVibrator.getEffectSegments(vibration.id).size() > 1, + expectedOnDuration + TEST_TIMEOUT_MILLIS)).isTrue(); mVibrationConductor.notifyCancelled( new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false); waitForCompletion(); verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_USER); - assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); + assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse(); List<VibrationEffectSegment> effectSegments = fakeVibrator.getEffectSegments(vibration.id); // First time, turn vibrator ON for the expected fixed duration. - assertEquals(expectedOnDuration, effectSegments.get(0).getDuration()); + assertThat(effectSegments.get(0).getDuration()).isEqualTo(expectedOnDuration); // Vibrator turns off in the middle of the second execution of the first step. Expect it to // be turned back ON at least for the fixed duration + the remaining duration of the step. - assertTrue(expectedOnDuration < effectSegments.get(1).getDuration()); + assertThat(effectSegments.get(1).getDuration()).isGreaterThan(expectedOnDuration); // Set amplitudes for a cycle {1, 2}, start second loop then turn it back on to same value. - assertEquals(expectedAmplitudes(1, 2, 1, 1), - mVibratorProviders.get(VIBRATOR_ID).getAmplitudes().subList(0, 4)); + assertThat(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes().subList(0, 4)) + .containsExactlyElementsIn(expectedAmplitudes(1, 2, 1, 1)) + .inOrder(); } @Test @@ -598,9 +606,9 @@ public class VibrationThreadTest { .compose(); HalVibration vibration = startThreadAndDispatcher(effect); - assertTrue(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(), - TEST_TIMEOUT_MILLIS)); - assertTrue(mThread.isRunningVibrationId(vibration.id)); + assertThat(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(), + TEST_TIMEOUT_MILLIS)).isTrue(); + assertThat(mThread.isRunningVibrationId(vibration.id)).isTrue(); // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately. @@ -614,7 +622,7 @@ public class VibrationThreadTest { cancellingThread.join(); verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_SETTINGS_UPDATE); - assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); + assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse(); } @Test @@ -628,9 +636,9 @@ public class VibrationThreadTest { VibrationEffect effect = VibrationEffect.createVendorEffect(createTestVendorData()); HalVibration vibration = startThreadAndDispatcher(effect); - assertTrue(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(), - TEST_TIMEOUT_MILLIS)); - assertTrue(mThread.isRunningVibrationId(vibration.id)); + assertThat(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(), + TEST_TIMEOUT_MILLIS)).isTrue(); + assertThat(mThread.isRunningVibrationId(vibration.id)).isTrue(); // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately. @@ -644,7 +652,7 @@ public class VibrationThreadTest { cancellingThread.join(); verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_SETTINGS_UPDATE); - assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); + assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse(); } @Test @@ -655,9 +663,9 @@ public class VibrationThreadTest { VibrationEffect effect = VibrationEffect.createWaveform(new long[]{100}, new int[]{100}, 0); HalVibration vibration = startThreadAndDispatcher(effect); - assertTrue(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(), - TEST_TIMEOUT_MILLIS)); - assertTrue(mThread.isRunningVibrationId(vibration.id)); + assertThat(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(), + TEST_TIMEOUT_MILLIS)).isTrue(); + assertThat(mThread.isRunningVibrationId(vibration.id)).isTrue(); // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately. @@ -671,7 +679,7 @@ public class VibrationThreadTest { cancellingThread.join(); verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_SCREEN_OFF); - assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); + assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse(); } @Test @@ -686,10 +694,10 @@ public class VibrationThreadTest { verify(mManagerHooks).noteVibratorOff(eq(UID)); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong()); verifyCallbacksTriggered(vibration, Status.FINISHED); - assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); + assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse(); - assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_THUD)), - mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id)); + assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id)) + .containsExactly(expectedPrebaked(VibrationEffect.EFFECT_THUD)).inOrder(); } @Test @@ -698,7 +706,7 @@ public class VibrationThreadTest { VibrationEffect fallback = VibrationEffect.createOneShot(10, 100); HalVibration vibration = createVibration(CombinedVibration.createParallel( - VibrationEffect.get(VibrationEffect.EFFECT_CLICK))); + VibrationEffect.get(EFFECT_CLICK))); vibration.fillFallbacks(unused -> fallback); startThreadAndDispatcher(vibration); waitForCompletion(); @@ -707,16 +715,17 @@ public class VibrationThreadTest { verify(mManagerHooks).noteVibratorOff(eq(UID)); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong()); verifyCallbacksTriggered(vibration, Status.FINISHED); - assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); + assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse(); - assertEquals(Arrays.asList(expectedOneShot(10)), - mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id)); - assertEquals(expectedAmplitudes(100), mVibratorProviders.get(VIBRATOR_ID).getAmplitudes()); + assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id)) + .containsExactly(expectedOneShot(10)).inOrder(); + assertThat(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes()) + .containsExactlyElementsIn(expectedAmplitudes(100)).inOrder(); } @Test public void vibrate_singleVibratorPrebakedAndUnsupportedEffect_ignoresVibration() { - VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); + VibrationEffect effect = VibrationEffect.get(EFFECT_CLICK); HalVibration vibration = startThreadAndDispatcher(effect); waitForCompletion(); @@ -725,7 +734,7 @@ public class VibrationThreadTest { verify(mControllerCallbacks, never()) .onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong()); verifyCallbacksTriggered(vibration, Status.IGNORED_UNSUPPORTED); - assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id).isEmpty()); + assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id)).isEmpty(); } @Test @@ -745,8 +754,7 @@ public class VibrationThreadTest { assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse(); assertThat(mVibratorProviders.get(VIBRATOR_ID).getVendorEffects(vibration.id)) - .containsExactly(effect) - .inOrder(); + .containsExactly(effect).inOrder(); } @Test @@ -766,11 +774,12 @@ public class VibrationThreadTest { verify(mManagerHooks).noteVibratorOff(eq(UID)); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong()); verifyCallbacksTriggered(vibration, Status.FINISHED); - assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); - assertEquals(Arrays.asList( - expectedPrimitive(PRIMITIVE_CLICK, 1, 0), - expectedPrimitive(PRIMITIVE_TICK, 0.5f, 0)), - fakeVibrator.getEffectSegments(vibration.id)); + assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse(); + assertThat(fakeVibrator.getEffectSegments(vibration.id)) + .containsExactly( + expectedPrimitive(PRIMITIVE_CLICK, 1, 0), + expectedPrimitive(PRIMITIVE_TICK, 0.5f, 0)) + .inOrder(); } @Test @@ -787,7 +796,7 @@ public class VibrationThreadTest { verify(mControllerCallbacks, never()) .onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong()); verifyCallbacksTriggered(vibration, Status.IGNORED_UNSUPPORTED); - assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id).isEmpty()); + assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id)).isEmpty(); } @Test @@ -804,7 +813,7 @@ public class VibrationThreadTest { verify(mControllerCallbacks, never()) .onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong()); verifyCallbacksTriggered(vibration, Status.IGNORED_UNSUPPORTED); - assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id).isEmpty()); + assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id)).isEmpty(); } @Test @@ -826,14 +835,14 @@ public class VibrationThreadTest { // Vibrator compose called twice. verify(mControllerCallbacks, times(2)) .onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong()); - assertEquals(3, fakeVibrator.getEffectSegments(vibration.id).size()); + assertThat(fakeVibrator.getEffectSegments(vibration.id)).hasSize(3); } @Test @DisableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) public void vibrate_singleVibratorComposedEffects_runsDifferentVibrations() { FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID); - fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK); + fakeVibrator.setSupportedEffects(EFFECT_CLICK); fakeVibrator.setSupportedPrimitives(PRIMITIVE_CLICK, PRIMITIVE_TICK); fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS, IVibrator.CAP_COMPOSE_PWLE_EFFECTS, IVibrator.CAP_AMPLITUDE_CONTROL); @@ -847,13 +856,13 @@ public class VibrationThreadTest { .addEffect(VibrationEffect.createOneShot(10, 100)) .addPrimitive(PRIMITIVE_CLICK, 1f) .addPrimitive(PRIMITIVE_TICK, 0.5f) - .addEffect(VibrationEffect.get(VibrationEffect.EFFECT_CLICK)) + .addEffect(VibrationEffect.get(EFFECT_CLICK)) .addEffect(VibrationEffect.startWaveform() .addTransition(Duration.ofMillis(10), targetAmplitude(1), targetFrequency(100)) .addTransition(Duration.ofMillis(20), targetFrequency(120)) .build()) - .addEffect(VibrationEffect.get(VibrationEffect.EFFECT_CLICK)) + .addEffect(VibrationEffect.get(EFFECT_CLICK)) .compose(); HalVibration vibration = startThreadAndDispatcher(effect); waitForCompletion(); @@ -864,33 +873,37 @@ public class VibrationThreadTest { verify(mControllerCallbacks, times(5)) .onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong()); verifyCallbacksTriggered(vibration, Status.FINISHED); - assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); - assertEquals(Arrays.asList( - expectedOneShot(10), - expectedPrimitive(PRIMITIVE_CLICK, 1, 0), - expectedPrimitive(PRIMITIVE_TICK, 0.5f, 0), - expectedPrebaked(VibrationEffect.EFFECT_CLICK), - expectedRamp(/* startAmplitude= */ 0, /* endAmplitude= */ 0.5f, - /* startFrequencyHz= */ 150, /* endFrequencyHz= */ 100, /* duration= */ 10), - expectedRamp(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.7f, - /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 120, /* duration= */ 20), - expectedPrebaked(VibrationEffect.EFFECT_CLICK)), - mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id)); - assertEquals(expectedAmplitudes(100), mVibratorProviders.get(VIBRATOR_ID).getAmplitudes()); + assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse(); + assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id)) + .containsExactly( + expectedOneShot(10), + expectedPrimitive(PRIMITIVE_CLICK, 1, 0), + expectedPrimitive(PRIMITIVE_TICK, 0.5f, 0), + expectedPrebaked(EFFECT_CLICK), + expectedRamp(/* startAmplitude= */ 0, /* endAmplitude= */ 0.5f, + /* startFrequencyHz= */ 150, /* endFrequencyHz= */ 100, + /* duration= */ 10), + expectedRamp(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.7f, + /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 120, + /* duration= */ 20), + expectedPrebaked(EFFECT_CLICK)) + .inOrder(); + assertThat(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes()) + .containsExactlyElementsIn(expectedAmplitudes(100)).inOrder(); } @Test public void vibrate_singleVibratorComposedWithFallback_replacedInTheMiddleOfComposition() { FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID); - fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK); + fakeVibrator.setSupportedEffects(EFFECT_CLICK); fakeVibrator.setSupportedPrimitives(PRIMITIVE_CLICK, PRIMITIVE_TICK); fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); VibrationEffect fallback = VibrationEffect.createOneShot(10, 100); VibrationEffect effect = VibrationEffect.startComposition() - .addEffect(VibrationEffect.get(VibrationEffect.EFFECT_CLICK)) + .addEffect(VibrationEffect.get(EFFECT_CLICK)) .addPrimitive(PRIMITIVE_CLICK, 1f) - .addEffect(VibrationEffect.get(VibrationEffect.EFFECT_TICK)) + .addEffect(VibrationEffect.get(EFFECT_TICK)) .addPrimitive(PRIMITIVE_TICK, 0.5f) .compose(); HalVibration vibration = createVibration(CombinedVibration.createParallel(effect)); @@ -904,18 +917,19 @@ public class VibrationThreadTest { verify(mControllerCallbacks, times(4)) .onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong()); verifyCallbacksTriggered(vibration, Status.FINISHED); - assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); + assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse(); List<VibrationEffectSegment> segments = mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id); - assertTrue("Wrong segments: " + segments, segments.size() >= 4); - assertTrue(segments.get(0) instanceof PrebakedSegment); - assertTrue(segments.get(1) instanceof PrimitiveSegment); + assertWithMessage("Wrong segments: %s", segments).that(segments.size()).isGreaterThan(3); + assertThat(segments.get(0)).isInstanceOf(PrebakedSegment.class); + assertThat(segments.get(1)).isInstanceOf(PrimitiveSegment.class); for (int i = 2; i < segments.size() - 1; i++) { // One or more step segments as fallback for the EFFECT_TICK. - assertTrue(segments.get(i) instanceof StepSegment); + assertWithMessage("For segment index %s", i) + .that(segments.get(i)).isInstanceOf(StepSegment.class); } - assertTrue(segments.get(segments.size() - 1) instanceof PrimitiveSegment); + assertThat(segments.get(segments.size() - 1)).isInstanceOf(PrimitiveSegment.class); } @Test @@ -942,14 +956,15 @@ public class VibrationThreadTest { verify(mManagerHooks).noteVibratorOff(eq(UID)); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong()); verifyCallbacksTriggered(vibration, Status.FINISHED); - assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); - assertEquals(Arrays.asList( - expectedPwle(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 0), - expectedPwle(/*amplitude=*/ 0.1f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20), - expectedPwle(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 30), - expectedPwle(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 20), - expectedPwle(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 30) - ), fakeVibrator.getEffectPwlePoints(vibration.id)); + assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse(); + assertThat(fakeVibrator.getEffectPwlePoints(vibration.id)) + .containsExactly( + expectedPwle(0.0f, 60f, 0), + expectedPwle(0.1f, 60f, 20), + expectedPwle(0.3f, 100f, 30), + expectedPwle(0.4f, 120f, 20), + expectedPwle(0.0f, 120f, 30)) + .inOrder(); } @@ -978,13 +993,14 @@ public class VibrationThreadTest { verify(mManagerHooks).noteVibratorOff(eq(UID)); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong()); verifyCallbacksTriggered(vibration, Status.FINISHED); - assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); - assertEquals(Arrays.asList( - expectedPwle(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 150f, /*timeMillis=*/ 0), - expectedPwle(/*amplitude=*/ 1.0f, /*frequencyHz=*/ 150f, /*timeMillis=*/ 20), - expectedPwle(/*amplitude=*/ 1.0f, /*frequencyHz=*/ 150f, /*timeMillis=*/ 100), - expectedPwle(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 150f, /*timeMillis=*/ 100) - ), fakeVibrator.getEffectPwlePoints(vibration.id)); + assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse(); + assertThat(fakeVibrator.getEffectPwlePoints(vibration.id)) + .containsExactly( + expectedPwle(0.0f, 150f, 0), + expectedPwle(1.0f, 150f, 20), + expectedPwle(1.0f, 150f, 100), + expectedPwle(0.0f, 150f, 100)) + .inOrder(); } @@ -1014,15 +1030,15 @@ public class VibrationThreadTest { verify(mManagerHooks).noteVibratorOff(eq(UID)); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong()); verifyCallbacksTriggered(vibration, Status.FINISHED); - assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); - assertEquals(Arrays.asList( - expectedPwle(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 30f, /*timeMillis=*/ 0), - expectedPwle(/*amplitude=*/ 0.1f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20), - expectedPwle(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 30), - expectedPwle(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 20), - expectedPwle(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 30) - ), fakeVibrator.getEffectPwlePoints(vibration.id)); - + assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse(); + assertThat(fakeVibrator.getEffectPwlePoints(vibration.id)) + .containsExactly( + expectedPwle(0.0f, 30f, 0), + expectedPwle(0.1f, 60f, 20), + expectedPwle(0.3f, 100f, 30), + expectedPwle(0.4f, 120f, 20), + expectedPwle(0.0f, 120f, 30)) + .inOrder(); } @Test @@ -1054,18 +1070,17 @@ public class VibrationThreadTest { // Using best split points instead of max-packing PWLEs. verify(mControllerCallbacks, times(3)) .onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong()); - assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); - - assertEquals(Arrays.asList( - expectedPwle(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 0), - expectedPwle(/*amplitude=*/ 0.8f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 30), - expectedPwle(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 30), - expectedPwle(/*amplitude=*/ 0.9f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 0), - expectedPwle(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 30), - expectedPwle(/*amplitude=*/ 0.6f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 0), - expectedPwle(/*amplitude=*/ 0.7f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 30) - ), fakeVibrator.getEffectPwlePoints(vibration.id)); - + assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse(); + assertThat(fakeVibrator.getEffectPwlePoints(vibration.id)) + .containsExactly( + expectedPwle(0.0f, 100f, 0), + expectedPwle(0.8f, 100f, 30), + expectedPwle(0.0f, 100f, 30), + expectedPwle(0.9f, 100f, 0), + expectedPwle(0.4f, 100f, 30), + expectedPwle(0.6f, 100f, 0), + expectedPwle(0.7f, 100f, 30)) + .inOrder(); } @Test @@ -1094,17 +1109,21 @@ public class VibrationThreadTest { verify(mManagerHooks).noteVibratorOff(eq(UID)); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong()); verifyCallbacksTriggered(vibration, Status.FINISHED); - assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); - assertEquals(Arrays.asList( - expectedRamp(/* amplitude= */ 1, /* frequencyHz= */ 150, /* duration= */ 10), - expectedRamp(/* startAmplitude= */ 1, /* endAmplitude= */ 0, - /* startFrequencyHz= */ 150, /* endFrequencyHz= */ 150, /* duration= */ 20), - expectedRamp(/* amplitude= */ 0.5f, /* frequencyHz= */ 100, /* duration= */ 30), - expectedRamp(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.6f, - /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 200, - /* duration= */ 40)), - fakeVibrator.getEffectSegments(vibration.id)); - assertEquals(Arrays.asList(Braking.CLAB), fakeVibrator.getBraking(vibration.id)); + assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse(); + assertThat(fakeVibrator.getEffectSegments(vibration.id)) + .containsExactly( + expectedRamp(/* amplitude= */ 1, /* frequencyHz= */ 150, + /* duration= */ 10), + expectedRamp(/* startAmplitude= */ 1, /* endAmplitude= */ 0, + /* startFrequencyHz= */ 150, /* endFrequencyHz= */ 150, + /* duration= */ 20), + expectedRamp(/* amplitude= */ 0.5f, /* frequencyHz= */ 100, + /* duration= */ 30), + expectedRamp(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.6f, + /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 200, + /* duration= */ 40)) + .inOrder(); + assertThat(fakeVibrator.getBraking(vibration.id)).containsExactly(Braking.CLAB).inOrder(); } @Test @@ -1137,7 +1156,7 @@ public class VibrationThreadTest { // Using best split points instead of max-packing PWLEs. verify(mControllerCallbacks, times(3)) .onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong()); - assertEquals(6, fakeVibrator.getEffectSegments(vibration.id).size()); + assertThat(fakeVibrator.getEffectSegments(vibration.id)).hasSize(6); } @Test @@ -1148,15 +1167,16 @@ public class VibrationThreadTest { VibrationEffect effect = VibrationEffect.createWaveform(new long[]{5}, new int[]{100}, 0); HalVibration vibration = startThreadAndDispatcher(effect); - assertTrue(waitUntil(() -> fakeVibrator.getAmplitudes().size() > 2, TEST_TIMEOUT_MILLIS)); + assertThat(waitUntil(() -> fakeVibrator.getAmplitudes().size() > 2, TEST_TIMEOUT_MILLIS)) + .isTrue(); // Vibration still running after 2 cycles. - assertTrue(mThread.isRunningVibrationId(vibration.id)); - assertTrue(mControllers.get(VIBRATOR_ID).isVibrating()); + assertThat(mThread.isRunningVibrationId(vibration.id)).isTrue(); + assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isTrue(); mVibrationConductor.notifyCancelled( new Vibration.EndInfo(Status.CANCELLED_BINDER_DIED), /* immediate= */ false); waitForCompletion(); - assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); + assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse(); verifyCallbacksTriggered(vibration, Status.CANCELLED_BINDER_DIED); } @@ -1176,11 +1196,11 @@ public class VibrationThreadTest { @Test public void vibrate_multipleExistingAndMissingVibrators_vibratesOnlyExistingOnes() { - mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_TICK); + mVibratorProviders.get(1).setSupportedEffects(EFFECT_TICK); CombinedVibration effect = CombinedVibration.startParallel() - .addVibrator(VIBRATOR_ID, VibrationEffect.get(VibrationEffect.EFFECT_TICK)) - .addVibrator(2, VibrationEffect.get(VibrationEffect.EFFECT_TICK)) + .addVibrator(VIBRATOR_ID, VibrationEffect.get(EFFECT_TICK)) + .addVibrator(2, VibrationEffect.get(EFFECT_TICK)) .combine(); HalVibration vibration = startThreadAndDispatcher(effect); waitForCompletion(); @@ -1190,21 +1210,21 @@ public class VibrationThreadTest { verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong()); verify(mControllerCallbacks, never()).onComplete(eq(2), eq(vibration.id), anyLong()); verifyCallbacksTriggered(vibration, Status.FINISHED); - assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); + assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse(); - assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_TICK)), - mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id)); + assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id)) + .containsExactly(expectedPrebaked(EFFECT_TICK)).inOrder(); } @Test public void vibrate_multipleMono_runsSameEffectInAllVibrators() { mockVibrators(1, 2, 3); - mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK); - mVibratorProviders.get(2).setSupportedEffects(VibrationEffect.EFFECT_CLICK); - mVibratorProviders.get(3).setSupportedEffects(VibrationEffect.EFFECT_CLICK); + mVibratorProviders.get(1).setSupportedEffects(EFFECT_CLICK); + mVibratorProviders.get(2).setSupportedEffects(EFFECT_CLICK); + mVibratorProviders.get(3).setSupportedEffects(EFFECT_CLICK); CombinedVibration effect = CombinedVibration.createParallel( - VibrationEffect.get(VibrationEffect.EFFECT_CLICK)); + VibrationEffect.get(EFFECT_CLICK)); HalVibration vibration = startThreadAndDispatcher(effect); waitForCompletion(); @@ -1214,23 +1234,23 @@ public class VibrationThreadTest { verify(mControllerCallbacks).onComplete(eq(2), eq(vibration.id), anyLong()); verify(mControllerCallbacks).onComplete(eq(3), eq(vibration.id), anyLong()); verifyCallbacksTriggered(vibration, Status.FINISHED); - assertFalse(mControllers.get(1).isVibrating()); - assertFalse(mControllers.get(2).isVibrating()); - assertFalse(mControllers.get(3).isVibrating()); + assertThat(mControllers.get(1).isVibrating()).isFalse(); + assertThat(mControllers.get(2).isVibrating()).isFalse(); + assertThat(mControllers.get(3).isVibrating()).isFalse(); - VibrationEffectSegment expected = expectedPrebaked(VibrationEffect.EFFECT_CLICK); - assertEquals(Arrays.asList(expected), - mVibratorProviders.get(1).getEffectSegments(vibration.id)); - assertEquals(Arrays.asList(expected), - mVibratorProviders.get(2).getEffectSegments(vibration.id)); - assertEquals(Arrays.asList(expected), - mVibratorProviders.get(3).getEffectSegments(vibration.id)); + VibrationEffectSegment expected = expectedPrebaked(EFFECT_CLICK); + assertThat(mVibratorProviders.get(1).getEffectSegments(vibration.id)) + .containsExactly(expected).inOrder(); + assertThat(mVibratorProviders.get(2).getEffectSegments(vibration.id)) + .containsExactly(expected).inOrder(); + assertThat(mVibratorProviders.get(3).getEffectSegments(vibration.id)) + .containsExactly(expected).inOrder(); } @Test public void vibrate_multipleStereo_runsVibrationOnRightVibrators() { mockVibrators(1, 2, 3, 4); - mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK); + mVibratorProviders.get(1).setSupportedEffects(EFFECT_CLICK); mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); mVibratorProviders.get(3).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); mVibratorProviders.get(4).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); @@ -1240,7 +1260,7 @@ public class VibrationThreadTest { .addPrimitive(PRIMITIVE_CLICK) .compose(); CombinedVibration effect = CombinedVibration.startParallel() - .addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK)) + .addVibrator(1, VibrationEffect.get(EFFECT_CLICK)) .addVibrator(2, VibrationEffect.createOneShot(10, 100)) .addVibrator(3, VibrationEffect.createWaveform( new long[]{10, 10}, new int[]{1, 2}, -1)) @@ -1256,21 +1276,23 @@ public class VibrationThreadTest { verify(mControllerCallbacks).onComplete(eq(3), eq(vibration.id), anyLong()); verify(mControllerCallbacks).onComplete(eq(4), eq(vibration.id), anyLong()); verifyCallbacksTriggered(vibration, Status.FINISHED); - assertFalse(mControllers.get(1).isVibrating()); - assertFalse(mControllers.get(2).isVibrating()); - assertFalse(mControllers.get(3).isVibrating()); - assertFalse(mControllers.get(4).isVibrating()); - - assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)), - mVibratorProviders.get(1).getEffectSegments(vibration.id)); - assertEquals(Arrays.asList(expectedOneShot(10)), - mVibratorProviders.get(2).getEffectSegments(vibration.id)); - assertEquals(expectedAmplitudes(100), mVibratorProviders.get(2).getAmplitudes()); - assertEquals(Arrays.asList(expectedOneShot(20)), - mVibratorProviders.get(3).getEffectSegments(vibration.id)); - assertEquals(expectedAmplitudes(1, 2), mVibratorProviders.get(3).getAmplitudes()); - assertEquals(Arrays.asList(expectedPrimitive(PRIMITIVE_CLICK, 1, 0)), - mVibratorProviders.get(4).getEffectSegments(vibration.id)); + assertThat(mControllers.get(1).isVibrating()).isFalse(); + assertThat(mControllers.get(2).isVibrating()).isFalse(); + assertThat(mControllers.get(3).isVibrating()).isFalse(); + assertThat(mControllers.get(4).isVibrating()).isFalse(); + + assertThat(mVibratorProviders.get(1).getEffectSegments(vibration.id)) + .containsExactly(expectedPrebaked(EFFECT_CLICK)).inOrder(); + assertThat(mVibratorProviders.get(2).getEffectSegments(vibration.id)) + .containsExactly(expectedOneShot(10)).inOrder(); + assertThat(mVibratorProviders.get(2).getAmplitudes()) + .containsExactlyElementsIn(expectedAmplitudes(100)).inOrder(); + assertThat(mVibratorProviders.get(3).getEffectSegments(vibration.id)) + .containsExactly(expectedOneShot(20)).inOrder(); + assertThat(mVibratorProviders.get(3).getAmplitudes()) + .containsExactlyElementsIn(expectedAmplitudes(1, 2)).inOrder(); + assertThat(mVibratorProviders.get(4).getEffectSegments(vibration.id)) + .containsExactly(expectedPrimitive(PRIMITIVE_CLICK, 1, 0)).inOrder(); } @Test @@ -1279,13 +1301,13 @@ public class VibrationThreadTest { mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); mVibratorProviders.get(2).setSupportedPrimitives(PRIMITIVE_CLICK); - mVibratorProviders.get(3).setSupportedEffects(VibrationEffect.EFFECT_CLICK); + mVibratorProviders.get(3).setSupportedEffects(EFFECT_CLICK); VibrationEffect composed = VibrationEffect.startComposition() .addPrimitive(PRIMITIVE_CLICK) .compose(); CombinedVibration effect = CombinedVibration.startSequential() - .addNext(3, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), /* delay= */ 50) + .addNext(3, VibrationEffect.get(EFFECT_CLICK), /* delay= */ 50) .addNext(1, VibrationEffect.createOneShot(10, 100), /* delay= */ 50) .addNext(2, composed, /* delay= */ 50) .combine(); @@ -1306,17 +1328,18 @@ public class VibrationThreadTest { batteryVerifier.verify(mManagerHooks).noteVibratorOff(eq(UID)); verifyCallbacksTriggered(vibration, Status.FINISHED); - assertFalse(mControllers.get(1).isVibrating()); - assertFalse(mControllers.get(2).isVibrating()); - assertFalse(mControllers.get(3).isVibrating()); + assertThat(mControllers.get(1).isVibrating()).isFalse(); + assertThat(mControllers.get(2).isVibrating()).isFalse(); + assertThat(mControllers.get(3).isVibrating()).isFalse(); - assertEquals(Arrays.asList(expectedOneShot(10)), - mVibratorProviders.get(1).getEffectSegments(vibration.id)); - assertEquals(expectedAmplitudes(100), mVibratorProviders.get(1).getAmplitudes()); - assertEquals(Arrays.asList(expectedPrimitive(PRIMITIVE_CLICK, 1, 0)), - mVibratorProviders.get(2).getEffectSegments(vibration.id)); - assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)), - mVibratorProviders.get(3).getEffectSegments(vibration.id)); + assertThat(mVibratorProviders.get(1).getEffectSegments(vibration.id)) + .containsExactly(expectedOneShot(10)).inOrder(); + assertThat(mVibratorProviders.get(1).getAmplitudes()) + .containsExactlyElementsIn(expectedAmplitudes(100)).inOrder(); + assertThat(mVibratorProviders.get(2).getEffectSegments(vibration.id)) + .containsExactly(expectedPrimitive(PRIMITIVE_CLICK, 1, 0)).inOrder(); + assertThat(mVibratorProviders.get(3).getEffectSegments(vibration.id)) + .containsExactly(expectedPrebaked(EFFECT_CLICK)).inOrder(); } @Test @@ -1339,10 +1362,10 @@ public class VibrationThreadTest { when(mManagerHooks.triggerSyncedVibration(eq(vibration.id))).thenReturn(true); startThreadAndDispatcher(vibration); - assertTrue(waitUntil( + assertThat(waitUntil( () -> !mVibratorProviders.get(1).getEffectSegments(vibration.id).isEmpty() && !mVibratorProviders.get(2).getEffectSegments(vibration.id).isEmpty(), - TEST_TIMEOUT_MILLIS)); + TEST_TIMEOUT_MILLIS)).isTrue(); mVibrationConductor.notifySyncedVibrationComplete(); waitForCompletion(); @@ -1353,17 +1376,17 @@ public class VibrationThreadTest { verifyCallbacksTriggered(vibration, Status.FINISHED); VibrationEffectSegment expected = expectedPrimitive(PRIMITIVE_CLICK, 1, 100); - assertEquals(Arrays.asList(expected), - mVibratorProviders.get(1).getEffectSegments(vibration.id)); - assertEquals(Arrays.asList(expected), - mVibratorProviders.get(2).getEffectSegments(vibration.id)); + assertThat(mVibratorProviders.get(1).getEffectSegments(vibration.id)) + .containsExactly(expected).inOrder(); + assertThat(mVibratorProviders.get(2).getEffectSegments(vibration.id)) + .containsExactly(expected).inOrder(); } @Test public void vibrate_multipleSynced_callsPrepareAndTriggerCallbacks() { int[] vibratorIds = new int[]{1, 2, 3, 4}; mockVibrators(vibratorIds); - mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK); + mVibratorProviders.get(1).setSupportedEffects(EFFECT_CLICK); mVibratorProviders.get(4).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); mVibratorProviders.get(4).setSupportedPrimitives(PRIMITIVE_CLICK); when(mManagerHooks.prepareSyncedVibration(anyLong(), any())).thenReturn(true); @@ -1372,7 +1395,7 @@ public class VibrationThreadTest { .addPrimitive(PRIMITIVE_CLICK) .compose(); CombinedVibration effect = CombinedVibration.startParallel() - .addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK)) + .addVibrator(1, VibrationEffect.get(EFFECT_CLICK)) .addVibrator(2, VibrationEffect.createOneShot(10, 100)) .addVibrator(3, VibrationEffect.createWaveform(new long[]{10}, new int[]{100}, -1)) .addVibrator(4, composed) @@ -1417,24 +1440,26 @@ public class VibrationThreadTest { verify(mManagerHooks, never()).triggerSyncedVibration(eq(vibration.id)); verify(mManagerHooks, never()).cancelSyncedVibration(); - assertEquals(Arrays.asList(expectedOneShot(10)), - mVibratorProviders.get(1).getEffectSegments(vibration.id)); - assertEquals(expectedAmplitudes(100), mVibratorProviders.get(1).getAmplitudes()); - assertEquals(Arrays.asList(expectedOneShot(5)), - mVibratorProviders.get(2).getEffectSegments(vibration.id)); - assertEquals(expectedAmplitudes(200), mVibratorProviders.get(2).getAmplitudes()); + assertThat(mVibratorProviders.get(1).getEffectSegments(vibration.id)) + .containsExactly(expectedOneShot(10)).inOrder(); + assertThat(mVibratorProviders.get(1).getAmplitudes()) + .containsExactlyElementsIn(expectedAmplitudes(100)).inOrder(); + assertThat(mVibratorProviders.get(2).getEffectSegments(vibration.id)) + .containsExactly(expectedOneShot(5)).inOrder(); + assertThat(mVibratorProviders.get(2).getAmplitudes()) + .containsExactlyElementsIn(expectedAmplitudes(200)).inOrder(); } @Test public void vibrate_multipleSyncedTriggerFailed_cancelPreparedVibrationAndSkipSetAmplitude() { int[] vibratorIds = new int[]{1, 2}; mockVibrators(vibratorIds); - mVibratorProviders.get(2).setSupportedEffects(VibrationEffect.EFFECT_CLICK); + mVibratorProviders.get(2).setSupportedEffects(EFFECT_CLICK); when(mManagerHooks.prepareSyncedVibration(anyLong(), any())).thenReturn(true); CombinedVibration effect = CombinedVibration.startParallel() .addVibrator(1, VibrationEffect.createOneShot(10, 100)) - .addVibrator(2, VibrationEffect.get(VibrationEffect.EFFECT_CLICK)) + .addVibrator(2, VibrationEffect.get(EFFECT_CLICK)) .combine(); // We create the HalVibration here to obtain the vibration id and use it to mock the // required response when calling triggerSyncedVibration. @@ -1451,7 +1476,7 @@ public class VibrationThreadTest { verify(mManagerHooks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds)); verify(mManagerHooks).triggerSyncedVibration(eq(vibration.id)); verify(mManagerHooks).cancelSyncedVibration(); - assertTrue(mVibratorProviders.get(1).getAmplitudes().isEmpty()); + assertThat(mVibratorProviders.get(1).getAmplitudes()).isEmpty(); } @Test @@ -1472,11 +1497,11 @@ public class VibrationThreadTest { HalVibration vibration = startThreadAndDispatcher(effect); // All vibrators are turned on in parallel. - assertTrue(waitUntil( + assertThat(waitUntil( () -> mControllers.get(1).isVibrating() && mControllers.get(2).isVibrating() && mControllers.get(3).isVibrating(), - TEST_TIMEOUT_MILLIS)); + TEST_TIMEOUT_MILLIS)).isTrue(); waitForCompletion(); @@ -1486,19 +1511,23 @@ public class VibrationThreadTest { verify(mControllerCallbacks).onComplete(eq(2), eq(vibration.id), anyLong()); verify(mControllerCallbacks).onComplete(eq(3), eq(vibration.id), anyLong()); verifyCallbacksTriggered(vibration, Status.FINISHED); - assertFalse(mControllers.get(1).isVibrating()); - assertFalse(mControllers.get(2).isVibrating()); - assertFalse(mControllers.get(3).isVibrating()); + assertThat(mControllers.get(1).isVibrating()).isFalse(); + assertThat(mControllers.get(2).isVibrating()).isFalse(); + assertThat(mControllers.get(3).isVibrating()).isFalse(); + + assertThat(mVibratorProviders.get(1).getEffectSegments(vibration.id)) + .containsExactly(expectedOneShot(25)).inOrder(); + assertThat(mVibratorProviders.get(2).getEffectSegments(vibration.id)) + .containsExactly(expectedOneShot(80)).inOrder(); + assertThat(mVibratorProviders.get(3).getEffectSegments(vibration.id)) + .containsExactly(expectedOneShot(60)).inOrder(); + assertThat(mVibratorProviders.get(1).getAmplitudes()) + .containsExactlyElementsIn(expectedAmplitudes(1, 2, 3)).inOrder(); + assertThat(mVibratorProviders.get(2).getAmplitudes()) + .containsExactlyElementsIn(expectedAmplitudes(4, 5)).inOrder(); + assertThat(mVibratorProviders.get(3).getAmplitudes()) + .containsExactlyElementsIn(expectedAmplitudes(6)).inOrder(); - assertEquals(Arrays.asList(expectedOneShot(25)), - mVibratorProviders.get(1).getEffectSegments(vibration.id)); - assertEquals(Arrays.asList(expectedOneShot(80)), - mVibratorProviders.get(2).getEffectSegments(vibration.id)); - assertEquals(Arrays.asList(expectedOneShot(60)), - mVibratorProviders.get(3).getEffectSegments(vibration.id)); - assertEquals(expectedAmplitudes(1, 2, 3), mVibratorProviders.get(1).getAmplitudes()); - assertEquals(expectedAmplitudes(4, 5), mVibratorProviders.get(2).getAmplitudes()); - assertEquals(expectedAmplitudes(6), mVibratorProviders.get(3).getAmplitudes()); } @Test @@ -1545,8 +1574,8 @@ public class VibrationThreadTest { VibrationEffect.createOneShot( expectedDuration, VibrationEffect.DEFAULT_AMPLITUDE))); - startThreadAndDispatcher(vibration); long startTime = SystemClock.elapsedRealtime(); + startThreadAndDispatcher(vibration); vibration.waitForEnd(); long vibrationEndTime = SystemClock.elapsedRealtime(); @@ -1616,26 +1645,24 @@ public class VibrationThreadTest { // Allow some delay for thread scheduling and callback triggering. int maxDelay = (int) (0.05 * totalDuration); // < 5% of total duration - assertTrue("Waveform with perceived delay of " + delay + "ms," - + " expected less than " + maxDelay + "ms", - delay < maxDelay); + assertThat(delay).isLessThan(maxDelay); } @LargeTest @Test public void vibrate_cancelSlowVibrator_cancelIsNotBlockedByVibrationThread() throws Exception { FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID); - fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK); + fakeVibrator.setSupportedEffects(EFFECT_CLICK); long latency = 5_000; // 5s fakeVibrator.setOnLatency(latency); - VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); + VibrationEffect effect = VibrationEffect.get(EFFECT_CLICK); HalVibration vibration = startThreadAndDispatcher(effect); - assertTrue(waitUntil(() -> !fakeVibrator.getEffectSegments(vibration.id).isEmpty(), - TEST_TIMEOUT_MILLIS)); - assertTrue(mThread.isRunningVibrationId(vibration.id)); + assertThat(waitUntil(() -> !fakeVibrator.getEffectSegments(vibration.id).isEmpty(), + TEST_TIMEOUT_MILLIS)).isTrue(); + assertThat(mThread.isRunningVibrationId(vibration.id)).isTrue(); // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should // fail at waitForCompletion(cancellingThread). @@ -1651,18 +1678,18 @@ public class VibrationThreadTest { // After the vibrator call ends the vibration is cancelled and the vibrator is turned off. waitForCompletion(/* timeout= */ latency + TEST_TIMEOUT_MILLIS); verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_USER); - assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); + assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse(); } @Test public void vibrate_multiplePredefinedCancel_cancelsVibrationImmediately() throws Exception { mockVibrators(1, 2); - mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK); + mVibratorProviders.get(1).setSupportedEffects(EFFECT_CLICK); mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); mVibratorProviders.get(2).setSupportedPrimitives(PRIMITIVE_CLICK); CombinedVibration effect = CombinedVibration.startParallel() - .addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK)) + .addVibrator(1, VibrationEffect.get(EFFECT_CLICK)) .addVibrator(2, VibrationEffect.startComposition() .addPrimitive(PRIMITIVE_CLICK, 1f, 100) .addPrimitive(PRIMITIVE_CLICK, 1f, 100) @@ -1671,8 +1698,9 @@ public class VibrationThreadTest { .combine(); HalVibration vibration = startThreadAndDispatcher(effect); - assertTrue(waitUntil(() -> mControllers.get(2).isVibrating(), TEST_TIMEOUT_MILLIS)); - assertTrue(mThread.isRunningVibrationId(vibration.id)); + assertThat(waitUntil(() -> mControllers.get(2).isVibrating(), TEST_TIMEOUT_MILLIS)) + .isTrue(); + assertThat(mThread.isRunningVibrationId(vibration.id)).isTrue(); // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately. @@ -1686,8 +1714,8 @@ public class VibrationThreadTest { cancellingThread.join(); verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_SCREEN_OFF); - assertFalse(mControllers.get(1).isVibrating()); - assertFalse(mControllers.get(2).isVibrating()); + assertThat(mControllers.get(1).isVibrating()).isFalse(); + assertThat(mControllers.get(2).isVibrating()).isFalse(); } @Test @@ -1705,8 +1733,9 @@ public class VibrationThreadTest { .combine(); HalVibration vibration = startThreadAndDispatcher(effect); - assertTrue(waitUntil(() -> mControllers.get(2).isVibrating(), TEST_TIMEOUT_MILLIS)); - assertTrue(mThread.isRunningVibrationId(vibration.id)); + assertThat(waitUntil(() -> mControllers.get(2).isVibrating(), TEST_TIMEOUT_MILLIS)) + .isTrue(); + assertThat(mThread.isRunningVibrationId(vibration.id)).isTrue(); // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately. @@ -1720,8 +1749,8 @@ public class VibrationThreadTest { cancellingThread.join(); verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_SCREEN_OFF); - assertFalse(mControllers.get(1).isVibrating()); - assertFalse(mControllers.get(2).isVibrating()); + assertThat(mControllers.get(1).isVibrating()).isFalse(); + assertThat(mControllers.get(2).isVibrating()).isFalse(); } @Test @@ -1737,10 +1766,10 @@ public class VibrationThreadTest { .combine(); HalVibration vibration = startThreadAndDispatcher(effect); - assertTrue(waitUntil(() -> mControllers.get(1).isVibrating() + assertThat(waitUntil(() -> mControllers.get(1).isVibrating() && mControllers.get(2).isVibrating(), - TEST_TIMEOUT_MILLIS)); - assertTrue(mThread.isRunningVibrationId(vibration.id)); + TEST_TIMEOUT_MILLIS)).isTrue(); + assertThat(mThread.isRunningVibrationId(vibration.id)).isTrue(); // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately. @@ -1754,8 +1783,8 @@ public class VibrationThreadTest { cancellingThread.join(); verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_SCREEN_OFF); - assertFalse(mControllers.get(1).isVibrating()); - assertFalse(mControllers.get(2).isVibrating()); + assertThat(mControllers.get(1).isVibrating()).isFalse(); + assertThat(mControllers.get(2).isVibrating()).isFalse(); } @Test @@ -1763,17 +1792,18 @@ public class VibrationThreadTest { VibrationEffect effect = VibrationEffect.createWaveform(new long[]{5}, new int[]{100}, 0); HalVibration vibration = startThreadAndDispatcher(effect); - assertTrue(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(), - TEST_TIMEOUT_MILLIS)); - assertTrue(mThread.isRunningVibrationId(vibration.id)); + assertThat(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(), + TEST_TIMEOUT_MILLIS)).isTrue(); + assertThat(mThread.isRunningVibrationId(vibration.id)).isTrue(); mVibrationConductor.notifyCancelled( new Vibration.EndInfo(Status.CANCELLED_BINDER_DIED), /* immediate= */ false); waitForCompletion(); verifyCallbacksTriggered(vibration, Status.CANCELLED_BINDER_DIED); - assertFalse(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id).isEmpty()); - assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); + assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id)) + .isNotEmpty(); + assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse(); } @Test @@ -1790,13 +1820,16 @@ public class VibrationThreadTest { verifyCallbacksTriggered(vibration, Status.FINISHED); // Duration extended for 5 + 5 + 5 + 15. - assertEquals(Arrays.asList(expectedOneShot(30)), - mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id)); + assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id)) + .containsExactly(expectedOneShot(30)).inOrder(); List<Float> amplitudes = mVibratorProviders.get(VIBRATOR_ID).getAmplitudes(); - assertTrue(amplitudes.size() > 3); - assertEquals(expectedAmplitudes(60, 120, 240), amplitudes.subList(0, 3)); + assertThat(amplitudes.size()).isGreaterThan(3); + assertThat(amplitudes.subList(0, 3)) + .containsExactlyElementsIn(expectedAmplitudes(60, 120, 240)) + .inOrder(); for (int i = 3; i < amplitudes.size(); i++) { - assertTrue(amplitudes.get(i) < amplitudes.get(i - 1)); + assertWithMessage("For amplitude index %s", i) + .that(amplitudes.get(i)).isLessThan(amplitudes.get(i - 1)); } } @@ -1814,11 +1847,11 @@ public class VibrationThreadTest { verify(mManagerHooks, never()).onVibrationThreadReleased(anyLong()); // Thread still running ramp down. - assertTrue(mThread.isRunningVibrationId(vibration.id)); + assertThat(mThread.isRunningVibrationId(vibration.id)).isTrue(); // Duration extended for 10 + 10000. - assertEquals(Arrays.asList(expectedOneShot(10_010)), - mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id)); + assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id)) + .containsExactly(expectedOneShot(10_010)).inOrder(); // Will stop the ramp down right away. mVibrationConductor.notifyCancelled( @@ -1838,8 +1871,8 @@ public class VibrationThreadTest { VibrationEffect effect = VibrationEffect.createOneShot(10_000, 240); HalVibration vibration = startThreadAndDispatcher(effect); - assertTrue(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(), - TEST_TIMEOUT_MILLIS)); + assertThat(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(), + TEST_TIMEOUT_MILLIS)).isTrue(); mVibrationConductor.notifyCancelled( new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false); waitForCompletion(); @@ -1847,12 +1880,13 @@ public class VibrationThreadTest { verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_USER); // Duration extended for 10000 + 15. - assertEquals(Arrays.asList(expectedOneShot(10_015)), - mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id)); + assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id)) + .containsExactly(expectedOneShot(10_015)).inOrder(); List<Float> amplitudes = mVibratorProviders.get(VIBRATOR_ID).getAmplitudes(); - assertTrue(amplitudes.size() > 1); + assertThat(amplitudes.size()).isGreaterThan(1); for (int i = 1; i < amplitudes.size(); i++) { - assertTrue(amplitudes.get(i) < amplitudes.get(i - 1)); + assertWithMessage("For amplitude index %s", i) + .that(amplitudes.get(i)).isLessThan(amplitudes.get(i - 1)); } } @@ -1860,18 +1894,18 @@ public class VibrationThreadTest { public void vibrate_predefinedWithRampDown_doesNotAddRampDown() { when(mVibrationConfigMock.getRampDownDurationMs()).thenReturn(15); mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); - mVibratorProviders.get(VIBRATOR_ID).setSupportedEffects(VibrationEffect.EFFECT_CLICK); + mVibratorProviders.get(VIBRATOR_ID).setSupportedEffects(EFFECT_CLICK); - VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); + VibrationEffect effect = VibrationEffect.get(EFFECT_CLICK); HalVibration vibration = startThreadAndDispatcher(effect); waitForCompletion(); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong()); verifyCallbacksTriggered(vibration, Status.FINISHED); - assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)), - mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id)); - assertTrue(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes().isEmpty()); + assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id)) + .containsExactly(expectedPrebaked(EFFECT_CLICK)).inOrder(); + assertThat(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes()).isEmpty(); } @Test @@ -1888,8 +1922,7 @@ public class VibrationThreadTest { verifyCallbacksTriggered(vibration, Status.FINISHED); assertThat(mVibratorProviders.get(VIBRATOR_ID).getVendorEffects(vibration.id)) - .containsExactly(effect) - .inOrder(); + .containsExactly(effect).inOrder(); assertThat(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes()).isEmpty(); } @@ -1909,9 +1942,9 @@ public class VibrationThreadTest { verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong()); verifyCallbacksTriggered(vibration, Status.FINISHED); - assertEquals(Arrays.asList(expectedPrimitive(PRIMITIVE_CLICK, 1, 0)), - mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id)); - assertTrue(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes().isEmpty()); + assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id)) + .containsExactly(expectedPrimitive(PRIMITIVE_CLICK, 1, 0)).inOrder(); + assertThat(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes()).isEmpty(); } @Test @@ -1936,30 +1969,29 @@ public class VibrationThreadTest { verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong()); verifyCallbacksTriggered(vibration, Status.FINISHED); - assertEquals(Arrays.asList(expectedRamp(0, 1, 150, 150, 1)), - fakeVibrator.getEffectSegments(vibration.id)); - assertTrue(fakeVibrator.getAmplitudes().isEmpty()); + assertThat(fakeVibrator.getEffectSegments(vibration.id)) + .containsExactly(expectedRamp(0, 1, 150, 150, 1)).inOrder(); + assertThat(fakeVibrator.getAmplitudes()).isEmpty(); } @Test public void vibrate_multipleVibrations_withCancel() throws Exception { - mVibratorProviders.get(VIBRATOR_ID).setSupportedEffects( - VibrationEffect.EFFECT_CLICK, VibrationEffect.EFFECT_TICK); + mVibratorProviders.get(VIBRATOR_ID).setSupportedEffects(EFFECT_CLICK, EFFECT_TICK); mVibratorProviders.get(VIBRATOR_ID).setSupportedPrimitives(PRIMITIVE_CLICK); mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL, IVibrator.CAP_COMPOSE_EFFECTS); // A simple effect, followed by a repeating effect that gets cancelled, followed by another // simple effect. - VibrationEffect effect1 = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); + VibrationEffect effect1 = VibrationEffect.get(EFFECT_CLICK); VibrationEffect effect2 = VibrationEffect.startComposition() - .repeatEffectIndefinitely(VibrationEffect.get(VibrationEffect.EFFECT_TICK)) + .repeatEffectIndefinitely(VibrationEffect.get(EFFECT_TICK)) .compose(); VibrationEffect effect3 = VibrationEffect.startComposition() .addPrimitive(PRIMITIVE_CLICK) .compose(); VibrationEffect effect4 = VibrationEffect.createOneShot(8000, 100); - VibrationEffect effect5 = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); + VibrationEffect effect5 = VibrationEffect.get(EFFECT_CLICK); HalVibration vibration1 = startThreadAndDispatcher(effect1); waitForCompletion(); @@ -1987,14 +2019,14 @@ public class VibrationThreadTest { waitForCompletion(); FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID); - assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); + assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse(); // Effect1 verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration1.id), anyLong()); verifyCallbacksTriggered(vibration1, Status.FINISHED); - assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)), - fakeVibrator.getEffectSegments(vibration1.id)); + assertThat(fakeVibrator.getEffectSegments(vibration1.id)) + .containsExactly(expectedPrebaked(EFFECT_CLICK)).inOrder(); // Effect2: repeating, cancelled. verify(mControllerCallbacks, atLeast(2)) @@ -2005,24 +2037,24 @@ public class VibrationThreadTest { // all elements are the same segment. List<VibrationEffectSegment> actualSegments2 = fakeVibrator.getEffectSegments(vibration2.id); - assertTrue(actualSegments2.size() + " > 2", actualSegments2.size() > 2); + assertThat(actualSegments2.size()).isGreaterThan(2); for (VibrationEffectSegment segment : actualSegments2) { - assertEquals(expectedPrebaked(VibrationEffect.EFFECT_TICK), segment); + assertThat(segment).isEqualTo(expectedPrebaked(EFFECT_TICK)); } // Effect3 verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration3.id), anyLong()); verifyCallbacksTriggered(vibration3, Status.FINISHED); - assertEquals(Arrays.asList(expectedPrimitive(PRIMITIVE_CLICK, 1, 0)), - fakeVibrator.getEffectSegments(vibration3.id)); + assertThat(fakeVibrator.getEffectSegments(vibration3.id)) + .containsExactly(expectedPrimitive(PRIMITIVE_CLICK, 1, 0)).inOrder(); // Effect4: cancelled quickly. verifyCallbacksTriggered(vibration4, Status.CANCELLED_BY_SCREEN_OFF); - assertTrue("Tested duration=" + duration4, duration4 < 2000); + assertThat(duration4).isLessThan(2000); // Effect5: played normally after effect4, which may or may not have played. - assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)), - fakeVibrator.getEffectSegments(vibration5.id)); + assertThat(fakeVibrator.getEffectSegments(vibration5.id)) + .containsExactly(expectedPrebaked(EFFECT_CLICK)).inOrder(); } @Test @@ -2031,16 +2063,13 @@ public class VibrationThreadTest { mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); mVibratorProviders.get(2).setSupportedPrimitives(PRIMITIVE_CLICK); - mVibratorProviders.get(3).setSupportedEffects(VibrationEffect.EFFECT_CLICK); + mVibratorProviders.get(3).setSupportedEffects(EFFECT_CLICK); CombinedVibration effect = CombinedVibration.startSequential() - .addNext(3, - VibrationEffect.get(VibrationEffect.EFFECT_CLICK), - /* delay= */ TEST_TIMEOUT_MILLIS) + .addNext(3, VibrationEffect.get(EFFECT_CLICK), /* delay= */ TEST_TIMEOUT_MILLIS) .addNext(1, VibrationEffect.createWaveform( - new long[] {TEST_TIMEOUT_MILLIS, TEST_TIMEOUT_MILLIS}, - /* repeat= */ -1), + new long[] {TEST_TIMEOUT_MILLIS, TEST_TIMEOUT_MILLIS}, -1), /* delay= */ TEST_TIMEOUT_MILLIS) .addNext(2, VibrationEffect.startComposition() @@ -2055,20 +2084,22 @@ public class VibrationThreadTest { // Vibrating state remains ON until session resets it. verifyCallbacksTriggered(vibration, Status.FINISHED); - assertTrue(mControllers.get(1).isVibrating()); - assertTrue(mControllers.get(2).isVibrating()); - assertTrue(mControllers.get(3).isVibrating()); - - assertEquals(0, mVibratorProviders.get(1).getOffCount()); - assertEquals(0, mVibratorProviders.get(2).getOffCount()); - assertEquals(0, mVibratorProviders.get(3).getOffCount()); - assertEquals(Arrays.asList(expectedOneShot(TEST_TIMEOUT_MILLIS)), - mVibratorProviders.get(1).getEffectSegments(vibration.id)); - assertEquals(expectedAmplitudes(255), mVibratorProviders.get(1).getAmplitudes()); - assertEquals(Arrays.asList(expectedPrimitive(PRIMITIVE_CLICK, 1, TEST_TIMEOUT_MILLIS)), - mVibratorProviders.get(2).getEffectSegments(vibration.id)); - assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)), - mVibratorProviders.get(3).getEffectSegments(vibration.id)); + assertThat(mControllers.get(1).isVibrating()).isTrue(); + assertThat(mControllers.get(2).isVibrating()).isTrue(); + assertThat(mControllers.get(3).isVibrating()).isTrue(); + + assertThat(mVibratorProviders.get(1).getOffCount()).isEqualTo(0); + assertThat(mVibratorProviders.get(2).getOffCount()).isEqualTo(0); + assertThat(mVibratorProviders.get(3).getOffCount()).isEqualTo(0); + assertThat(mVibratorProviders.get(1).getEffectSegments(vibration.id)) + .containsExactly(expectedOneShot(TEST_TIMEOUT_MILLIS)).inOrder(); + assertThat(mVibratorProviders.get(1).getAmplitudes()) + .containsExactlyElementsIn(expectedAmplitudes(255)).inOrder(); + assertThat(mVibratorProviders.get(2).getEffectSegments(vibration.id)) + .containsExactly(expectedPrimitive(PRIMITIVE_CLICK, 1, TEST_TIMEOUT_MILLIS)) + .inOrder(); + assertThat(mVibratorProviders.get(3).getEffectSegments(vibration.id)) + .containsExactly(expectedPrebaked(EFFECT_CLICK)).inOrder(); } private void mockVibrators(int... vibratorIds) { @@ -2122,7 +2153,7 @@ public class VibrationThreadTest { mVibrationConductor = new VibrationStepConductor(vib, isInSession, mVibrationSettings, deviceAdapter, mVibrationScaler, mStatsLoggerMock, requestVibrationParamsFuture, mManagerHooks); - assertTrue(mThread.runVibrationOnVibrationThread(mVibrationConductor)); + assertThat(mThread.runVibrationOnVibrationThread(mVibrationConductor)).isTrue(); return mVibrationConductor.getVibration(); } @@ -2142,8 +2173,8 @@ public class VibrationThreadTest { } private void waitForCompletion(long timeout) { - assertTrue("Timed out waiting for VibrationThread to become idle", - mThread.waitForThreadIdle(timeout)); + assertWithMessage("Timed out waiting for VibrationThread to become idle") + .that(mThread.waitForThreadIdle(timeout)).isTrue(); mTestLooper.dispatchAll(); // Flush callbacks } |