summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/current.txt11
-rw-r--r--core/java/android/companion/virtual/flags/flags.aconfig8
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java12
-rw-r--r--core/java/android/view/ViewRootImpl.java3
-rw-r--r--core/java/android/view/XrWindowProperties.java159
-rw-r--r--core/jni/platform/host/HostRuntime.cpp14
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DragZoneFactory.kt61
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt24
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt22
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt14
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt9
-rw-r--r--packages/SettingsLib/aconfig/settingslib.aconfig10
-rw-r--r--packages/SettingsLib/res/drawable/ic_news.xml19
-rw-r--r--packages/SettingsLib/res/drawable/ic_promotions.xml19
-rw-r--r--packages/SettingsLib/res/drawable/ic_recs.xml25
-rw-r--r--packages/SettingsLib/res/drawable/ic_social.xml19
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java1
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java1
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/PagerDots.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PagerDots.kt)4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt32
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt4
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt150
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/DefaultIntentCreatorTest.java (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/IntentCreatorTest.java)22
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt30
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/FakeCondition.java2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt59
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt32
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt36
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapterTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryTest.kt)47
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapterTest.kt370
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java339
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt61
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerTest.kt23
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt12
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java23
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java19
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.kt8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java20
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt2
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockController.kt6
-rw-r--r--packages/SystemUI/pods/com/android/systemui/dagger/qualifiers/DisplaySpecific.kt (renamed from packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/DisplaySpecific.kt)5
-rw-r--r--packages/SystemUI/pods/com/android/systemui/dagger/qualifiers/Main.java (renamed from packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/Main.java)0
-rw-r--r--packages/SystemUI/pods/com/android/systemui/dagger/qualifiers/UiBackground.java (renamed from packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/UiBackground.java)0
-rw-r--r--packages/SystemUI/res/layout/magic_action_button.xml2
-rw-r--r--packages/SystemUI/res/layout/volume_dialog.xml6
-rw-r--r--packages/SystemUI/res/layout/volume_dialog_top_section.xml2
-rw-r--r--packages/SystemUI/res/values/dimens.xml5
-rw-r--r--packages/SystemUI/shared/Android.bp1
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/condition/CombinedCondition.kt11
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/condition/Condition.java306
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/condition/Condition.kt270
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/condition/ConditionExtensions.kt18
-rw-r--r--packages/SystemUI/src/com/android/keyguard/ClockEventController.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt99
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/DefaultIntentCreator.java102
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java78
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/DeviceInactiveCondition.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepository.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/conditions/AssistantAttentionCondition.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/RefactorFlagUtils.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/lowlightclock/DirectBootCondition.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/lowlightclock/ForceLowLightCondition.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightCondition.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/lowlightclock/ScreenSaverEnabledCondition.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/modes/shared/ModesUi.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/process/condition/SystemProcessCondition.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/flags/QsDetailedView.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/LauncherProxyService.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeExpandsOnStatusBarLongPress.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/shared/flag/ShadeWindowGoesAround.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt57
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt54
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/shared/StatusBarNotifChips.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChips.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/core/NewStatusBarIcons.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarConnectedDisplays.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarRootModernization.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarConfigurationStateStore.kt120
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarPerDisplayConfigurationStateRepository.kt69
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/StatusBarPopupChips.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/headsup/shared/StatusBarNoHunBehavior.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java130
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt122
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapterFactory.kt (renamed from packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/Tracing.kt)10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapterFactoryImpl.kt56
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java136
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt145
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StabilizeHeadsUpGroup.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/shared/ModesEmptyShadeFix.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/shared/NotifRedesignFooter.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/NotificationsHunSharedAnimationValues.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationUi.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationUiAod.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationUiForceExpanded.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationBundleUi.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java53
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/StatusBarChipsModernization.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ethernet/shared/StatusBarSignalPolicyRefactorEthernet.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTileGrid.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/supervision/shared/DeprecateDpmSupervisionApis.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/VolumeDialogComponent.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/factory/VolumeDialogComponentFactory.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/module/VolumeDialogModule.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/module/VolumeDialogPluginModule.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shared/condition/CombinedConditionTest.kt50
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/NotificationActivityStarterKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerKosmos.kt22
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/people/NotificationPersonExtractorKosmos.kt22
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifierKosmos.kt25
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/EntryAdapterFactoryKosmos.kt36
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt15
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponentKosmos.kt5
-rw-r--r--services/core/java/com/android/server/UiModeManagerService.java2
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java21
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java7
-rw-r--r--services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java2
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java15
-rw-r--r--services/core/java/com/android/server/wm/BackgroundActivityStartController.java7
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java7
-rw-r--r--tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt14
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml27
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_bottom_half_pip.xml1
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_bubble.xml1
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_base_layout.xml1
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml1
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_secondary_activity_layout.xml3
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml1
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_launch_new.xml1
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_mail.xml1
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_non_resizeable.xml3
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml1
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_simple.xml1
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_splitscreen.xml1
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_splitscreen_secondary.xml1
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_start_media_projection.xml3
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_surfaceview.xml1
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_transparent.xml1
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_transparent_launch.xml1
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/notification_button.xml2
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/task_button.xml1
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml6
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivity.java5
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeEditorPopupDialogActivity.java2
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchNewActivity.java5
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchNewTaskActivity.java5
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java8
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java8
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PortraitOnlyActivity.java5
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SeamlessRotationActivity.java2
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ShowWhenLockedActivity.java5
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SimpleActivity.java5
185 files changed, 2788 insertions, 1640 deletions
diff --git a/core/api/current.txt b/core/api/current.txt
index 3da5a5cca861..7bc0fb220e1a 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -56108,6 +56108,17 @@ package android.view {
method @NonNull public android.view.WindowInsets getWindowInsets();
}
+ @FlaggedApi("android.xr.xr_manifest_entries") public final class XrWindowProperties {
+ field @FlaggedApi("android.xr.xr_manifest_entries") public static final String PROPERTY_XR_ACTIVITY_START_MODE = "android.window.PROPERTY_XR_ACTIVITY_START_MODE";
+ field @FlaggedApi("android.xr.xr_manifest_entries") public static final String PROPERTY_XR_BOUNDARY_TYPE_RECOMMENDED = "android.window.PROPERTY_XR_BOUNDARY_TYPE_RECOMMENDED";
+ field @FlaggedApi("android.xr.xr_manifest_entries") public static final String XR_ACTIVITY_START_MODE_FULL_SPACE_MANAGED = "XR_ACTIVITY_START_MODE_FULL_SPACE_MANAGED";
+ field @FlaggedApi("android.xr.xr_manifest_entries") public static final String XR_ACTIVITY_START_MODE_FULL_SPACE_UNMANAGED = "XR_ACTIVITY_START_MODE_FULL_SPACE_UNMANAGED";
+ field @FlaggedApi("android.xr.xr_manifest_entries") public static final String XR_ACTIVITY_START_MODE_HOME_SPACE = "XR_ACTIVITY_START_MODE_HOME_SPACE";
+ field @FlaggedApi("android.xr.xr_manifest_entries") public static final String XR_ACTIVITY_START_MODE_UNDEFINED = "XR_ACTIVITY_START_MODE_UNDEFINED";
+ field @FlaggedApi("android.xr.xr_manifest_entries") public static final String XR_BOUNDARY_TYPE_LARGE = "XR_BOUNDARY_TYPE_LARGE";
+ field @FlaggedApi("android.xr.xr_manifest_entries") public static final String XR_BOUNDARY_TYPE_NO_RECOMMENDATION = "XR_BOUNDARY_TYPE_NO_RECOMMENDATION";
+ }
+
}
package android.view.accessibility {
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index 0085e4f42397..4fb3982c3754 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -150,3 +150,11 @@ flag {
description: "Settings override for virtual devices"
bug: "371801645"
}
+
+flag {
+ namespace: "virtual_devices"
+ name: "viewconfiguration_apis"
+ description: "APIs for settings ViewConfiguration attributes on virtual devices"
+ bug: "370720522"
+ is_exported: true
+}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 7b47efd47008..894b068b1528 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -527,22 +527,13 @@ public class InputMethodService extends AbstractInputMethodService {
public static final int IME_ACTIVE = 1 << 0;
/**
- * The IME is perceptibly visible to the user.
+ * The IME is visible.
*
* @hide
*/
public static final int IME_VISIBLE = 1 << 1;
/**
- * The IME is visible, but not yet perceptible to the user (e.g. fading in)
- * by {@link android.view.WindowInsetsController}.
- *
- * @see InputMethodManager#reportPerceptible
- * @hide
- */
- public static final int IME_VISIBLE_IMPERCEPTIBLE = 1 << 2;
-
- /**
* The IME window visibility state.
*
* @hide
@@ -550,7 +541,6 @@ public class InputMethodService extends AbstractInputMethodService {
@IntDef(flag = true, prefix = { "IME_" }, value = {
IME_ACTIVE,
IME_VISIBLE,
- IME_VISIBLE_IMPERCEPTIBLE,
})
public @interface ImeWindowVisibility {}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 98e6cdde664a..7dc96f21b5ae 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1836,6 +1836,7 @@ public final class ViewRootImpl implements ViewParent,
eventsToBeRegistered,
mBasePackageName);
+ // LINT.IfChange(fi_cb)
if (forceInvertColor()) {
if (mForceInvertObserver == null) {
mForceInvertObserver = new ContentObserver(mHandler) {
@@ -1844,7 +1845,6 @@ public final class ViewRootImpl implements ViewParent,
updateForceDarkMode();
}
};
-
final Uri[] urisToObserve = {
Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED),
@@ -1859,6 +1859,7 @@ public final class ViewRootImpl implements ViewParent,
}
}
}
+ // LINT.ThenChange(/services/core/java/com/android/server/UiModeManagerService.java:fi_cb)
}
/**
diff --git a/core/java/android/view/XrWindowProperties.java b/core/java/android/view/XrWindowProperties.java
new file mode 100644
index 000000000000..23021a563393
--- /dev/null
+++ b/core/java/android/view/XrWindowProperties.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.annotation.FlaggedApi;
+
+/**
+ * Class for XR-specific window properties to put in application manifests.
+ */
+@FlaggedApi(android.xr.Flags.FLAG_XR_MANIFEST_ENTRIES)
+public final class XrWindowProperties {
+ /** @hide */
+ private XrWindowProperties() {}
+
+ /**
+ * Both Application and activity level
+ * {@link android.content.pm.PackageManager.Property PackageManager.Property} for an app to
+ * inform the system of the activity launch mode in XR. When it is declared at the application
+ * level, all activities are set to the defined value, unless it is overridden at the activity
+ * level.
+ *
+ * <p>The default value is {@link #XR_ACTIVITY_START_MODE_UNDEFINED}.
+ *
+ * <p>The available values are:
+ * <ul>
+ * <li>{@link #XR_ACTIVITY_START_MODE_FULL_SPACE_UNMANAGED}
+ * <li>{@link #XR_ACTIVITY_START_MODE_FULL_SPACE_MANAGED}
+ * <li>{@link #XR_ACTIVITY_START_MODE_HOME_SPACE}
+ * <li>{@link #XR_ACTIVITY_START_MODE_UNDEFINED}
+ * </ul>
+ *
+ * <p><b>Syntax:</b>
+ * <pre>
+ * &lt;application&gt;
+ * &lt;property
+ * android:name="android.window.PROPERTY_ACTIVITY_XR_START_MODE"
+ * android:value="XR_ACTIVITY_START_MODE_FULL_SPACE_UNMANAGED|
+ * XR_ACTIVITY_START_MODE_FULL_SPACE_MANAGED|
+ * XR_ACTIVITY_START_MODE_HOME_SPACE|
+ * XR_ACTIVITY_START_MODE_UNDEFINED"/&gt;
+ * &lt;/application&gt;
+ * </pre>
+ */
+ @FlaggedApi(android.xr.Flags.FLAG_XR_MANIFEST_ENTRIES)
+ public static final String PROPERTY_XR_ACTIVITY_START_MODE =
+ "android.window.PROPERTY_XR_ACTIVITY_START_MODE";
+
+ /**
+ * Defines the value to launch an activity in unmanaged full space mode in XR, where the
+ * activity itself is rendering the space and controls its own scene graph. This should be used
+ * for all activities that use OpenXR to render.
+ *
+ * @see #PROPERTY_XR_ACTIVITY_START_MODE
+ */
+ @FlaggedApi(android.xr.Flags.FLAG_XR_MANIFEST_ENTRIES)
+ public static final String XR_ACTIVITY_START_MODE_FULL_SPACE_UNMANAGED =
+ "XR_ACTIVITY_START_MODE_FULL_SPACE_UNMANAGED";
+
+ /**
+ * The default value if not specified. If used, the actual launching mode will be determined by
+ * the system based on the launching activity's current mode and the launching flags. When
+ * {@link #PROPERTY_XR_ACTIVITY_START_MODE} is used at the application level, apps can use this
+ * value to reset at individual activity level.
+ *
+ * @see #PROPERTY_XR_ACTIVITY_START_MODE
+ */
+ @FlaggedApi(android.xr.Flags.FLAG_XR_MANIFEST_ENTRIES)
+ public static final String XR_ACTIVITY_START_MODE_UNDEFINED =
+ "XR_ACTIVITY_START_MODE_UNDEFINED";
+
+ /**
+ * Defines the value to launch an activity in
+ * <a href="https://developer.android.com/develop/xr/jetpack-xr-sdk/transition-home-space-to-full-space">managed
+ * full space mode</a> in XR, where the system is rendering the activity from a scene graph.
+ *
+ * @see #PROPERTY_XR_ACTIVITY_START_MODE
+ */
+ @FlaggedApi(android.xr.Flags.FLAG_XR_MANIFEST_ENTRIES)
+ public static final String XR_ACTIVITY_START_MODE_FULL_SPACE_MANAGED =
+ "XR_ACTIVITY_START_MODE_FULL_SPACE_MANAGED";
+
+ /**
+ * Defines the value to launch an activity in
+ * <a href="https://developer.android.com/develop/xr/jetpack-xr-sdk/transition-home-space-to-full-space">home
+ * space mode</a> in XR.
+ *
+ * @see #PROPERTY_XR_ACTIVITY_START_MODE
+ */
+ @FlaggedApi(android.xr.Flags.FLAG_XR_MANIFEST_ENTRIES)
+ public static final String XR_ACTIVITY_START_MODE_HOME_SPACE =
+ "XR_ACTIVITY_START_MODE_HOME_SPACE";
+
+ /**
+ * Both Application and activity level
+ * {@link android.content.pm.PackageManager.Property PackageManager.Property} for an app to
+ * inform the system of the type of safety boundary recommended for the activity. When it is
+ * declared at the application level, all activities are set to the defined value, unless it is
+ * overridden at the activity level. When not declared, the system will not enforce any
+ * recommendations for a type of safety boundary and will continue to use the type that is
+ * currently in use.
+ *
+ * <p>The default value is {@link #XR_BOUNDARY_TYPE_NO_RECOMMENDATION}.
+ *
+ * <p>The available values are:
+ * <ul>
+ * <li>{@link #XR_BOUNDARY_TYPE_LARGE}
+ * <li>{@link #XR_BOUNDARY_TYPE_NO_RECOMMENDATION}
+ * </ul>
+ *
+ * <p><b>Syntax:</b>
+ * <pre>
+ * &lt;application&gt;
+ * &lt;property
+ * android:name="android.window.PROPERTY_XR_BOUNDARY_TYPE_RECOMMENDED"
+ * android:value="XR_BOUNDARY_TYPE_LARGE|
+ * XR_BOUNDARY_TYPE_NO_RECOMMENDATION"/&gt;
+ * &lt;/application&gt;
+ * </pre>
+ */
+ @FlaggedApi(android.xr.Flags.FLAG_XR_MANIFEST_ENTRIES)
+ public static final String PROPERTY_XR_BOUNDARY_TYPE_RECOMMENDED =
+ "android.window.PROPERTY_XR_BOUNDARY_TYPE_RECOMMENDED";
+
+ /**
+ * Defines the value to launch an activity with no recommendations for the type of safety
+ * boundary. The system will continue to use the type of safety boundary that is currently
+ * in use.
+ *
+ * @see #PROPERTY_XR_BOUNDARY_TYPE_RECOMMENDED
+ */
+ @FlaggedApi(android.xr.Flags.FLAG_XR_MANIFEST_ENTRIES)
+ public static final String XR_BOUNDARY_TYPE_NO_RECOMMENDATION =
+ "XR_BOUNDARY_TYPE_NO_RECOMMENDATION";
+
+ /**
+ * Defines the value to launch an activity with a large boundary recommended. This is useful for
+ * activities which expect users to be moving around. The system will ask the user to use a
+ * larger size for their safety boundary and check that their space is clear, if the larger
+ * size is not already in use. This larger size will be determined by the system.
+ *
+ * @see #PROPERTY_XR_BOUNDARY_TYPE_RECOMMENDED
+ */
+ @FlaggedApi(android.xr.Flags.FLAG_XR_MANIFEST_ENTRIES)
+ public static final String XR_BOUNDARY_TYPE_LARGE = "XR_BOUNDARY_TYPE_LARGE";
+}
diff --git a/core/jni/platform/host/HostRuntime.cpp b/core/jni/platform/host/HostRuntime.cpp
index 746740b0248b..1e8da730aff8 100644
--- a/core/jni/platform/host/HostRuntime.cpp
+++ b/core/jni/platform/host/HostRuntime.cpp
@@ -280,12 +280,18 @@ static string getJavaProperty(JNIEnv* env, const char* property_name,
return string(chars.c_str());
}
-static void loadIcuData(string icuPath) {
+static void loadIcuData(JNIEnv* env, string icuPath) {
void* addr = mmapFile(icuPath.c_str());
+ if (addr == nullptr) {
+ jniThrowRuntimeException(env, "Failed to map the ICU data file.");
+ }
UErrorCode err = U_ZERO_ERROR;
udata_setCommonData(addr, &err);
if (err != U_ZERO_ERROR) {
- ALOGE("Unable to load ICU data\n");
+ jniThrowRuntimeException(env,
+ format("udata_setCommonData failed with error code {}",
+ u_errorName(err))
+ .c_str());
}
}
@@ -296,12 +302,12 @@ static void loadIcuData() {
JNIEnv* env = AndroidRuntime::getJNIEnv();
string icuPath = base::GetProperty("ro.icu.data.path", "");
if (!icuPath.empty()) {
- loadIcuData(icuPath);
+ loadIcuData(env, icuPath);
} else {
// fallback to read from java.lang.System.getProperty
string icuPathFromJava = getJavaProperty(env, "icu.data.path");
if (!icuPathFromJava.empty()) {
- loadIcuData(icuPathFromJava);
+ loadIcuData(env, icuPathFromJava);
}
}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DragZoneFactory.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DragZoneFactory.kt
index 1a80b0f29aa9..362a5c5c3750 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DragZoneFactory.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DragZoneFactory.kt
@@ -111,47 +111,28 @@ class DragZoneFactory(
/** Updates all dimensions after a configuration change. */
fun onConfigurationUpdated() {
- dismissDragZoneSize =
- if (deviceConfig.isSmallTablet) {
- context.resolveDimension(R.dimen.drag_zone_dismiss_fold)
- } else {
- context.resolveDimension(R.dimen.drag_zone_dismiss_tablet)
- }
- bubbleDragZoneTabletSize = context.resolveDimension(R.dimen.drag_zone_bubble_tablet)
- bubbleDragZoneFoldableSize = context.resolveDimension(R.dimen.drag_zone_bubble_fold)
- fullScreenDragZoneWidth = context.resolveDimension(R.dimen.drag_zone_full_screen_width)
- fullScreenDragZoneHeight = context.resolveDimension(R.dimen.drag_zone_full_screen_height)
- desktopWindowDragZoneWidth =
- context.resolveDimension(R.dimen.drag_zone_desktop_window_width)
- desktopWindowDragZoneHeight =
- context.resolveDimension(R.dimen.drag_zone_desktop_window_height)
- desktopWindowFromExpandedViewDragZoneWidth =
- context.resolveDimension(R.dimen.drag_zone_desktop_window_expanded_view_width)
- desktopWindowFromExpandedViewDragZoneHeight =
- context.resolveDimension(R.dimen.drag_zone_desktop_window_expanded_view_height)
- splitFromBubbleDragZoneHeight =
- context.resolveDimension(R.dimen.drag_zone_split_from_bubble_height)
- splitFromBubbleDragZoneWidth =
- context.resolveDimension(R.dimen.drag_zone_split_from_bubble_width)
- hSplitFromExpandedViewDragZoneWidth =
- context.resolveDimension(R.dimen.drag_zone_h_split_from_expanded_view_width)
- vSplitFromExpandedViewDragZoneWidth =
- context.resolveDimension(R.dimen.drag_zone_v_split_from_expanded_view_width)
- vSplitFromExpandedViewDragZoneHeightTablet =
- context.resolveDimension(R.dimen.drag_zone_v_split_from_expanded_view_height_tablet)
- vSplitFromExpandedViewDragZoneHeightFoldTall =
- context.resolveDimension(R.dimen.drag_zone_v_split_from_expanded_view_height_fold_tall)
- vSplitFromExpandedViewDragZoneHeightFoldShort =
- context.resolveDimension(R.dimen.drag_zone_v_split_from_expanded_view_height_fold_short)
- fullScreenDropTargetPadding =
- context.resolveDimension(R.dimen.drop_target_full_screen_padding)
- desktopWindowDropTargetPaddingSmall =
- context.resolveDimension(R.dimen.drop_target_desktop_window_padding_small)
- desktopWindowDropTargetPaddingLarge =
- context.resolveDimension(R.dimen.drop_target_desktop_window_padding_large)
-
- // TODO b/393172431: Use the shared xml resources once we can easily access them from
+ // TODO b/396539130: Use the shared xml resources once we can easily access them from
// launcher
+ dismissDragZoneSize =
+ if (deviceConfig.isSmallTablet) 140.dpToPx() else 200.dpToPx()
+ bubbleDragZoneTabletSize = 200.dpToPx()
+ bubbleDragZoneFoldableSize = 140.dpToPx()
+ fullScreenDragZoneWidth = 512.dpToPx()
+ fullScreenDragZoneHeight = 44.dpToPx()
+ desktopWindowDragZoneWidth = 880.dpToPx()
+ desktopWindowDragZoneHeight = 300.dpToPx()
+ desktopWindowFromExpandedViewDragZoneWidth = 200.dpToPx()
+ desktopWindowFromExpandedViewDragZoneHeight = 350.dpToPx()
+ splitFromBubbleDragZoneHeight = 100.dpToPx()
+ splitFromBubbleDragZoneWidth = 60.dpToPx()
+ hSplitFromExpandedViewDragZoneWidth = 60.dpToPx()
+ vSplitFromExpandedViewDragZoneWidth = 200.dpToPx()
+ vSplitFromExpandedViewDragZoneHeightTablet = 285.dpToPx()
+ vSplitFromExpandedViewDragZoneHeightFoldTall = 150.dpToPx()
+ vSplitFromExpandedViewDragZoneHeightFoldShort = 100.dpToPx()
+ fullScreenDropTargetPadding = 20.dpToPx()
+ desktopWindowDropTargetPaddingSmall = 100.dpToPx()
+ desktopWindowDropTargetPaddingLarge = 130.dpToPx()
expandedViewDropTargetWidth = 364.dpToPx()
expandedViewDropTargetHeight = 578.dpToPx()
expandedViewDropTargetPaddingBottom = 108.dpToPx()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 2ded48a79d6e..43a19ef034c9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -877,8 +877,7 @@ public abstract class WMShellModule {
@ShellMainThread Handler handler) {
int maxTaskLimit = DesktopModeStatus.getMaxTaskLimit(context);
if (!DesktopModeStatus.canEnterDesktopMode(context)
- || !ENABLE_DESKTOP_WINDOWING_TASK_LIMIT.isTrue()
- || maxTaskLimit <= 0) {
+ || !ENABLE_DESKTOP_WINDOWING_TASK_LIMIT.isTrue()) {
return Optional.empty();
}
return Optional.of(
@@ -886,7 +885,7 @@ public abstract class WMShellModule {
transitions,
desktopUserRepositories,
shellTaskOrganizer,
- maxTaskLimit,
+ maxTaskLimit <= 0 ? null : maxTaskLimit,
interactionJankMonitor,
context,
handler));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt
index 70a648f57125..fcdf4af531ee 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt
@@ -109,7 +109,7 @@ class DesktopTaskChangeListener(private val desktopUserRepositories: DesktopUser
desktopUserRepositories.getProfile(taskInfo.userId)
if (!desktopRepository.isActiveTask(taskInfo.taskId)) return
logD("onTaskMovingToBack for taskId=%d, displayId=%d", taskInfo.taskId, taskInfo.displayId)
- // TODO: b/367268953 - Connect this with DesktopRepository.
+ desktopRepository.updateTask(taskInfo.displayId, taskInfo.taskId, /* isVisible= */ false)
}
override fun onTaskClosing(taskInfo: RunningTaskInfo) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
index b9b4d9e4bbd8..da369f094405 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
@@ -38,17 +38,17 @@ import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.transition.Transitions.TransitionObserver
/**
- * Limits the number of tasks shown in Desktop Mode.
+ * Keeps track of minimized tasks and limits the number of tasks shown in Desktop Mode.
*
- * This class should only be used if
- * [android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TASK_LIMIT] is enabled and
- * [maxTasksLimit] is strictly greater than 0.
+ * [maxTasksLimit] must be strictly greater than 0 if it's given.
+ *
+ * TODO(b/400634379): Separate two responsibilities of this class into two classes.
*/
class DesktopTasksLimiter(
transitions: Transitions,
private val desktopUserRepositories: DesktopUserRepositories,
private val shellTaskOrganizer: ShellTaskOrganizer,
- private val maxTasksLimit: Int,
+ private val maxTasksLimit: Int?,
private val interactionJankMonitor: InteractionJankMonitor,
private val context: Context,
@ShellMainThread private val handler: Handler,
@@ -59,13 +59,19 @@ class DesktopTasksLimiter(
private var userId: Int
init {
- require(maxTasksLimit > 0) {
- "DesktopTasksLimiter: maxTasksLimit should be greater than 0. Current value: $maxTasksLimit."
+ maxTasksLimit?.let {
+ require(it > 0) {
+ "DesktopTasksLimiter: maxTasksLimit should be greater than 0. Current value: $it."
+ }
}
transitions.registerObserver(minimizeTransitionObserver)
userId = ActivityManager.getCurrentUser()
desktopUserRepositories.current.addActiveTaskListener(leftoverMinimizedTasksRemover)
- logV("Starting limiter with a maximum of %d tasks", maxTasksLimit)
+ if (maxTasksLimit != null) {
+ logV("Starting limiter with a maximum of %d tasks", maxTasksLimit)
+ } else {
+ logV("Starting limiter without the task limit")
+ }
}
data class TaskDetails(
@@ -325,7 +331,7 @@ class DesktopTasksLimiter(
launchingNewIntent: Boolean,
): Int? {
val newTasksOpening = if (launchingNewIntent) 1 else 0
- if (visibleOrderedTasks.size + newTasksOpening <= maxTasksLimit) {
+ if (visibleOrderedTasks.size + newTasksOpening <= (maxTasksLimit ?: Int.MAX_VALUE)) {
logV("No need to minimize; tasks below limit")
// No need to minimize anything
return null
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt
index 6b0ee5b7ffd4..54360a8fd908 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt
@@ -180,6 +180,28 @@ class DesktopTaskChangeListenerTest : ShellTestCase() {
}
@Test
+ fun onTaskMovingToBack_activeTaskInRepo_updatesTask() {
+ val task = createFreeformTask().apply { isVisible = true }
+ whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
+
+ desktopTaskChangeListener.onTaskMovingToBack(task)
+
+ verify(desktopUserRepositories.current)
+ .updateTask(task.displayId, task.taskId, /* isVisible= */ false)
+ }
+
+ @Test
+ fun onTaskMovingToBack_nonActiveTaskInRepo_noop() {
+ val task = createFreeformTask().apply { isVisible = true }
+ whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(false)
+
+ desktopTaskChangeListener.onTaskMovingToBack(task)
+
+ verify(desktopUserRepositories.current, never())
+ .updateTask(task.displayId, task.taskId, /* isVisible= */ false)
+ }
+
+ @Test
@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
fun onTaskClosing_backNavEnabled_nonClosingTask_minimizesTaskInRepo() {
val task = createFreeformTask().apply { isVisible = true }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
index 62e3c544e390..76103640c029 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
@@ -172,6 +172,20 @@ class DesktopTasksLimiterTest : ShellTestCase() {
}
@Test
+ fun createDesktopTasksLimiter_withNoLimit_shouldSucceed() {
+ // Instantiation should succeed without an error.
+ DesktopTasksLimiter(
+ transitions,
+ userRepositories,
+ shellTaskOrganizer,
+ maxTasksLimit = null,
+ interactionJankMonitor,
+ mContext,
+ handler,
+ )
+ }
+
+ @Test
fun addPendingMinimizeTransition_taskIsNotMinimized() {
desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
index 9588a5c73385..0871d38ceb46 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
@@ -15,7 +15,6 @@ import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
-import android.view.Display.DEFAULT_DISPLAY
import android.view.SurfaceControl
import android.view.WindowManager.TRANSIT_OPEN
import android.window.TransitionInfo
@@ -785,7 +784,7 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
startTransaction: SurfaceControl.Transaction = mock(),
finishTransaction: SurfaceControl.Transaction = mock(),
homeChange: TransitionInfo.Change? = createHomeChange(),
- transitionRootLeash: SurfaceControl? = null,
+ transitionRootLeash: SurfaceControl = mock(),
): IBinder {
whenever(dragAnimator.position).thenReturn(PointF())
// Simulate transition is started and is ready to animate.
@@ -887,7 +886,7 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
type: Int,
draggedTask: RunningTaskInfo,
homeChange: TransitionInfo.Change? = createHomeChange(),
- rootLeash: SurfaceControl? = null,
+ rootLeash: SurfaceControl = mock(),
) =
TransitionInfo(type, /* flags= */ 0).apply {
homeChange?.let { addChange(it) }
@@ -904,9 +903,7 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
flags = flags or FLAG_IS_WALLPAPER
}
)
- if (rootLeash != null) {
- addRootLeash(DEFAULT_DISPLAY, rootLeash, /* offsetLeft= */ 0, /* offsetTop= */ 0)
- }
+ addRootLeash(draggedTask.displayId, rootLeash, /* offsetLeft= */ 0, /* offsetTop= */ 0)
}
private fun createHomeChange() =
diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
index a029f56cf1d7..418a76ffb534 100644
--- a/packages/SettingsLib/aconfig/settingslib.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -249,3 +249,13 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "audio_stream_media_service_by_receive_state"
+ namespace: "cross_device_experiences"
+ description: "Start or update audio stream media service by receive state"
+ bug: "398700619"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SettingsLib/res/drawable/ic_news.xml b/packages/SettingsLib/res/drawable/ic_news.xml
new file mode 100644
index 000000000000..90615ec1b8fd
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_news.xml
@@ -0,0 +1,19 @@
+<!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="14.933333dp" android:viewportHeight="14" android:viewportWidth="15" android:width="16dp">
+ <path android:fillColor="#ffffff" android:pathData="M3,10.8H11V9.6H3V10.8ZM3,8.4H5.4V4.8H3V8.4ZM12.2,13.6H1.8C1.467,13.6 1.183,13.483 0.95,13.25C0.717,13.017 0.6,12.733 0.6,12.4V3.6C0.6,3.267 0.717,2.983 0.95,2.75C1.183,2.517 1.467,2.4 1.8,2.4H6.683C6.528,2.711 6.406,3.033 6.317,3.367C6.239,3.7 6.2,4.044 6.2,4.4C6.2,4.933 6.283,5.439 6.45,5.917C6.628,6.383 6.878,6.811 7.2,7.2H6.6V8.4H8.767C9.056,8.522 9.35,8.622 9.65,8.7C9.961,8.767 10.278,8.8 10.6,8.8C11.111,8.8 11.606,8.717 12.083,8.55C12.572,8.372 13.011,8.122 13.4,7.8V12.4C13.4,12.733 13.283,13.017 13.05,13.25C12.817,13.483 12.533,13.6 12.2,13.6ZM10.6,8C10.6,7 10.25,6.15 9.55,5.45C8.85,4.75 8,4.4 7,4.4C8,4.4 8.85,4.05 9.55,3.35C10.25,2.65 10.6,1.8 10.6,0.8C10.6,1.8 10.95,2.65 11.65,3.35C12.35,4.05 13.2,4.4 14.2,4.4C13.2,4.4 12.35,4.75 11.65,5.45C10.95,6.15 10.6,7 10.6,8Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_promotions.xml b/packages/SettingsLib/res/drawable/ic_promotions.xml
new file mode 100644
index 000000000000..a597ecebd967
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_promotions.xml
@@ -0,0 +1,19 @@
+<!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="16dp" android:viewportHeight="15" android:viewportWidth="13" android:width="13.866667dp">
+ <path android:fillColor="#ffffff" android:pathData="M1.4,13.4C1.067,13.4 0.783,13.283 0.55,13.05C0.317,12.817 0.2,12.533 0.2,12.2V4.6C0.2,4.267 0.317,3.983 0.55,3.75C0.783,3.517 1.067,3.4 1.4,3.4H2.6V3.133C2.6,2.467 2.822,1.878 3.267,1.367C3.722,0.856 4.3,0.6 5,0.6C5.667,0.6 6.233,0.833 6.7,1.3C7.167,1.767 7.4,2.333 7.4,3V3.4H8.6C8.933,3.4 9.217,3.517 9.45,3.75C9.683,3.983 9.8,4.267 9.8,4.6V6.367C9.611,6.311 9.417,6.272 9.217,6.25C9.017,6.217 8.811,6.2 8.6,6.2C7.378,6.2 6.339,6.628 5.483,7.483C4.628,8.339 4.2,9.378 4.2,10.6C4.2,11.111 4.283,11.611 4.45,12.1C4.628,12.578 4.878,13.011 5.2,13.4H1.4ZM8.6,14.2C8.6,13.2 8.25,12.35 7.55,11.65C6.85,10.95 6,10.6 5,10.6C6,10.6 6.85,10.25 7.55,9.55C8.25,8.85 8.6,8 8.6,7C8.6,8 8.95,8.85 9.65,9.55C10.35,10.25 11.2,10.6 12.2,10.6C11.2,10.6 10.35,10.95 9.65,11.65C8.95,12.35 8.6,13.2 8.6,14.2ZM3.8,3.4H6.2V3C6.2,2.667 6.083,2.383 5.85,2.15C5.617,1.917 5.333,1.8 5,1.8C4.667,1.8 4.383,1.917 4.15,2.15C3.917,2.383 3.8,2.667 3.8,3V3.4ZM3.2,5.8C3.367,5.8 3.506,5.744 3.617,5.633C3.739,5.511 3.8,5.367 3.8,5.2V4.6H2.6V5.2C2.6,5.367 2.656,5.511 2.767,5.633C2.889,5.744 3.033,5.8 3.2,5.8ZM6.8,5.8C6.967,5.8 7.106,5.744 7.217,5.633C7.339,5.511 7.4,5.367 7.4,5.2V4.6H6.2V5.2C6.2,5.367 6.256,5.511 6.367,5.633C6.489,5.744 6.633,5.8 6.8,5.8Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_recs.xml b/packages/SettingsLib/res/drawable/ic_recs.xml
new file mode 100644
index 000000000000..034ff9e221b0
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_recs.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="14dp"
+ android:height="16dp"
+ android:viewportWidth="14"
+ android:viewportHeight="16">
+ <path
+ android:pathData="M6,15.2L3.6,12.8H1.6C1.278,12.8 0.994,12.683 0.75,12.45C0.517,12.206 0.4,11.922 0.4,11.6V2.8C0.4,2.478 0.517,2.2 0.75,1.967C0.994,1.722 1.278,1.6 1.6,1.6H6.2C5.878,1.989 5.628,2.428 5.45,2.917C5.283,3.394 5.2,3.889 5.2,4.4C5.2,5.622 5.628,6.661 6.483,7.517C7.339,8.372 8.378,8.8 9.6,8.8C9.956,8.8 10.3,8.761 10.633,8.683C10.967,8.594 11.289,8.472 11.6,8.317V11.6C11.6,11.922 11.483,12.206 11.25,12.45C11.017,12.683 10.733,12.8 10.4,12.8H8.4L6,15.2ZM9.6,8C9.6,7 9.25,6.15 8.55,5.45C7.85,4.75 7,4.4 6,4.4C7,4.4 7.85,4.05 8.55,3.35C9.25,2.65 9.6,1.8 9.6,0.8C9.6,1.8 9.95,2.65 10.65,3.35C11.35,4.05 12.2,4.4 13.2,4.4C12.2,4.4 11.35,4.75 10.65,5.45C9.95,6.15 9.6,7 9.6,8Z"
+ android:fillColor="#ffffff"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_social.xml b/packages/SettingsLib/res/drawable/ic_social.xml
new file mode 100644
index 000000000000..01974319b441
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_social.xml
@@ -0,0 +1,19 @@
+<!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="16dp" android:viewportHeight="15" android:viewportWidth="15" android:width="16dp">
+ <path android:fillColor="#ffffff" android:pathData="M0.6,14.4V12.733C0.6,12.411 0.678,12.117 0.833,11.85C0.989,11.583 1.211,11.378 1.5,11.233C2.056,10.956 2.628,10.75 3.217,10.617C3.817,10.472 4.428,10.4 5.05,10.4C5.661,10.4 6.256,10.472 6.833,10.617C7.422,10.75 7.989,10.956 8.533,11.233C8.811,11.378 9.028,11.583 9.183,11.85C9.339,12.117 9.417,12.411 9.417,12.733V14.4H0.6ZM10.6,14.4V12.617C10.6,12.183 10.494,11.778 10.283,11.4C10.072,11.022 9.772,10.733 9.383,10.533C9.772,10.6 10.144,10.7 10.5,10.833C10.856,10.956 11.206,11.106 11.55,11.283C11.828,11.439 12.072,11.628 12.283,11.85C12.494,12.061 12.6,12.317 12.6,12.617V14.4H10.6ZM5.2,9.6C4.589,9.6 4.067,9.389 3.633,8.967C3.211,8.533 3,8.011 3,7.4C3,6.789 3.211,6.272 3.633,5.85C4.067,5.417 4.589,5.2 5.2,5.2C5.811,5.2 6.328,5.417 6.75,5.85C7.183,6.272 7.4,6.789 7.4,7.4C7.4,8.011 7.183,8.533 6.75,8.967C6.328,9.389 5.811,9.6 5.2,9.6ZM10.6,7.4C10.6,8.011 10.383,8.533 9.95,8.967C9.528,9.389 9.011,9.6 8.4,9.6C8.311,9.6 8.217,9.594 8.117,9.583C8.017,9.572 7.922,9.55 7.833,9.517C8.089,9.206 8.278,8.872 8.4,8.517C8.533,8.161 8.6,7.789 8.6,7.4C8.6,7.011 8.533,6.639 8.4,6.283C8.278,5.928 8.089,5.594 7.833,5.283C7.922,5.25 8.017,5.228 8.117,5.217C8.217,5.206 8.311,5.2 8.4,5.2C9.011,5.2 9.528,5.417 9.95,5.85C10.383,6.272 10.6,6.789 10.6,7.4ZM11.4,6.4C11.4,5.622 11.128,4.961 10.583,4.417C10.039,3.872 9.378,3.6 8.6,3.6C9.378,3.6 10.039,3.328 10.583,2.783C11.128,2.239 11.4,1.578 11.4,0.8C11.4,1.578 11.672,2.239 12.217,2.783C12.761,3.328 13.422,3.6 14.2,3.6C13.422,3.6 12.761,3.872 12.217,4.417C11.672,4.961 11.4,5.622 11.4,6.4Z"/>
+</vector>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
index de7c450d8d39..7c975b750639 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
@@ -72,6 +72,7 @@ import java.util.regex.Pattern;
public final class DeviceConfigService extends Binder {
private static final List<String> sAconfigTextProtoFilesOnDevice = List.of(
"/system/etc/aconfig_flags.pb",
+ "/system_ext/etc/aconfig_flags.pb",
"/product/etc/aconfig_flags.pb",
"/vendor/etc/aconfig_flags.pb");
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 99c4e21c6053..17c13b78778c 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -158,6 +158,7 @@ public class SettingsState {
private static final List<String> sAconfigTextProtoFilesOnDevice = List.of(
"/system/etc/aconfig_flags.pb",
+ "/system_ext/etc/aconfig_flags.pb",
"/product/etc/aconfig_flags.pb",
"/vendor/etc/aconfig_flags.pb");
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PagerDots.kt b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/PagerDots.kt
index 91f1477d5325..172d88af4cc8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PagerDots.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/PagerDots.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.panels.ui.compose
+package com.android.systemui.common.ui.compose
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.foundation.Canvas
@@ -43,9 +43,9 @@ import androidx.compose.ui.semantics.stateDescription
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
+import com.android.app.tracing.coroutines.launchTraced as launch
import kotlin.math.absoluteValue
import kotlinx.coroutines.CoroutineScope
-import com.android.app.tracing.coroutines.launchTraced as launch
import platform.test.motion.compose.values.MotionTestValueKey
import platform.test.motion.compose.values.motionTestValues
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
index 547461e5faf2..a0216268308c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
@@ -49,6 +49,8 @@ import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
+import com.android.compose.animation.scene.content.state.TransitionState
+import com.android.compose.modifiers.thenIf
import com.android.systemui.brightness.ui.compose.BrightnessSliderContainer
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.dagger.SysUISingleton
@@ -258,7 +260,11 @@ fun ContentScope.QuickSettingsLayout(
BrightnessSliderContainer(
viewModel = viewModel.brightnessSliderViewModel,
containerColor = OverlayShade.Colors.PanelBackground,
- modifier = Modifier.systemGestureExclusionInShade().fillMaxWidth(),
+ modifier =
+ Modifier.systemGestureExclusionInShade(
+ enabled = { layoutState.transitionState is TransitionState.Idle }
+ )
+ .fillMaxWidth(),
)
Box {
@@ -289,18 +295,20 @@ object QuickSettingsShade {
* right.
*/
@Composable
- fun Modifier.systemGestureExclusionInShade(): Modifier {
+ fun Modifier.systemGestureExclusionInShade(enabled: () -> Boolean): Modifier {
val density = LocalDensity.current
- return systemGestureExclusion { layoutCoordinates ->
- val sidePadding = with(density) { Dimensions.Padding.toPx() }
- Rect(
- offset = Offset(x = -sidePadding, y = 0f),
- size =
- Size(
- width = layoutCoordinates.size.width.toFloat() + 2 * sidePadding,
- height = layoutCoordinates.size.height.toFloat(),
- ),
- )
+ return thenIf(enabled()) {
+ Modifier.systemGestureExclusion { layoutCoordinates ->
+ val sidePadding = with(density) { Dimensions.Padding.toPx() }
+ Rect(
+ offset = Offset(x = -sidePadding, y = 0f),
+ size =
+ Size(
+ width = layoutCoordinates.size.width.toFloat() + 2 * sidePadding,
+ height = layoutCoordinates.size.height.toFloat(),
+ ),
+ )
+ }
}
}
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index 9bb3bac824e9..365567b17ec0 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -17,7 +17,6 @@ import android.content.Context
import android.content.res.Resources
import android.graphics.Color
import android.graphics.Rect
-import android.graphics.RectF
import android.icu.text.NumberFormat
import android.util.TypedValue
import android.view.LayoutInflater
@@ -29,6 +28,7 @@ import com.android.systemui.plugins.clocks.AlarmData
import com.android.systemui.plugins.clocks.ClockAnimations
import com.android.systemui.plugins.clocks.ClockConfig
import com.android.systemui.plugins.clocks.ClockController
+import com.android.systemui.plugins.clocks.ClockEventListener
import com.android.systemui.plugins.clocks.ClockEvents
import com.android.systemui.plugins.clocks.ClockFaceConfig
import com.android.systemui.plugins.clocks.ClockFaceController
@@ -102,7 +102,7 @@ class DefaultClockController(
isDarkTheme: Boolean,
dozeFraction: Float,
foldFraction: Float,
- onBoundsChanged: (RectF) -> Unit,
+ clockListener: ClockEventListener?,
) {
largeClock.recomputePadding(null)
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
index 6dfd2268005f..5acd4468fe92 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
@@ -16,13 +16,13 @@
package com.android.systemui.shared.clocks
-import android.graphics.RectF
import com.android.systemui.animation.GSFAxes
import com.android.systemui.customization.R
import com.android.systemui.plugins.clocks.AlarmData
import com.android.systemui.plugins.clocks.AxisType
import com.android.systemui.plugins.clocks.ClockConfig
import com.android.systemui.plugins.clocks.ClockController
+import com.android.systemui.plugins.clocks.ClockEventListener
import com.android.systemui.plugins.clocks.ClockEvents
import com.android.systemui.plugins.clocks.ClockFontAxis
import com.android.systemui.plugins.clocks.ClockFontAxis.Companion.merge
@@ -107,11 +107,11 @@ class FlexClockController(private val clockCtx: ClockContext) : ClockController
isDarkTheme: Boolean,
dozeFraction: Float,
foldFraction: Float,
- onBoundsChanged: (RectF) -> Unit,
+ clockListener: ClockEventListener?,
) {
events.onFontAxesChanged(clockCtx.settings.axes)
smallClock.run {
- layerController.onViewBoundsChanged = onBoundsChanged
+ layerController.onViewBoundsChanged = { clockListener?.onBoundsChanged(it) }
events.onThemeChanged(theme.copy(isDarkTheme = isDarkTheme))
animations.doze(dozeFraction)
animations.fold(foldFraction)
@@ -119,7 +119,7 @@ class FlexClockController(private val clockCtx: ClockContext) : ClockController
}
largeClock.run {
- layerController.onViewBoundsChanged = onBoundsChanged
+ layerController.onViewBoundsChanged = { clockListener?.onBoundsChanged(it) }
events.onThemeChanged(theme.copy(isDarkTheme = isDarkTheme))
animations.doze(dozeFraction)
animations.fold(foldFraction)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt
new file mode 100644
index 000000000000..239e02640908
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt
@@ -0,0 +1,150 @@
+/*
+ * 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.clipboardoverlay
+
+import android.content.ClipData
+import android.content.ComponentName
+import android.content.Intent
+import android.net.Uri
+import android.text.SpannableString
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.res.R
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ActionIntentCreatorTest : SysuiTestCase() {
+ val creator = ActionIntentCreator()
+
+ @Test
+ fun test_getTextEditorIntent() {
+ val intent = creator.getTextEditorIntent(context)
+ assertEquals(ComponentName(context, EditTextActivity::class.java), intent.component)
+ assertFlags(intent, Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ }
+
+ @Test
+ fun test_getRemoteCopyIntent() {
+ context.getOrCreateTestableResources().addOverride(R.string.config_remoteCopyPackage, "")
+
+ val clipData = ClipData.newPlainText("Test", "Test Item")
+ var intent = creator.getRemoteCopyIntent(clipData, context)
+
+ assertEquals(null, intent.component)
+ assertFlags(intent, EXTERNAL_INTENT_FLAGS)
+ assertEquals(clipData, intent.clipData)
+
+ // Try again with a remote copy component
+ val fakeComponent =
+ ComponentName("com.android.remotecopy", "com.android.remotecopy.RemoteCopyActivity")
+ context
+ .getOrCreateTestableResources()
+ .addOverride(R.string.config_remoteCopyPackage, fakeComponent.flattenToString())
+
+ intent = creator.getRemoteCopyIntent(clipData, context)
+ assertEquals(fakeComponent, intent.component)
+ }
+
+ @Test
+ fun test_getImageEditIntent() {
+ context.getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor, "")
+ val fakeUri = Uri.parse("content://foo")
+ var intent = creator.getImageEditIntent(fakeUri, context)
+
+ assertEquals(Intent.ACTION_EDIT, intent.action)
+ assertEquals("image/*", intent.type)
+ assertEquals(null, intent.component)
+ assertEquals("clipboard", intent.getStringExtra("edit_source"))
+ assertFlags(intent, EXTERNAL_INTENT_FLAGS)
+
+ // try again with an editor component
+ val fakeComponent =
+ ComponentName("com.android.remotecopy", "com.android.remotecopy.RemoteCopyActivity")
+ context
+ .getOrCreateTestableResources()
+ .addOverride(R.string.config_screenshotEditor, fakeComponent.flattenToString())
+ intent = creator.getImageEditIntent(fakeUri, context)
+ assertEquals(fakeComponent, intent.component)
+ }
+
+ @Test
+ fun test_getShareIntent_plaintext() {
+ val clipData = ClipData.newPlainText("Test", "Test Item")
+ val intent = creator.getShareIntent(clipData, context)
+
+ assertEquals(Intent.ACTION_CHOOSER, intent.action)
+ assertFlags(intent, EXTERNAL_INTENT_FLAGS)
+ val target = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java)
+ assertEquals("Test Item", target?.getStringExtra(Intent.EXTRA_TEXT))
+ assertEquals("text/plain", target?.type)
+ }
+
+ @Test
+ fun test_getShareIntent_html() {
+ val clipData = ClipData.newHtmlText("Test", "Some HTML", "<b>Some HTML</b>")
+ val intent = creator.getShareIntent(clipData, getContext())
+
+ assertEquals(Intent.ACTION_CHOOSER, intent.action)
+ assertFlags(intent, EXTERNAL_INTENT_FLAGS)
+ val target = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java)
+ assertEquals("Some HTML", target?.getStringExtra(Intent.EXTRA_TEXT))
+ assertEquals("text/plain", target?.type)
+ }
+
+ @Test
+ fun test_getShareIntent_image() {
+ val uri = Uri.parse("content://something")
+ val clipData = ClipData("Test", arrayOf("image/png"), ClipData.Item(uri))
+ val intent = creator.getShareIntent(clipData, context)
+
+ assertEquals(Intent.ACTION_CHOOSER, intent.action)
+ assertFlags(intent, EXTERNAL_INTENT_FLAGS)
+ val target = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java)
+ assertEquals(uri, target?.getParcelableExtra(Intent.EXTRA_STREAM, Uri::class.java))
+ assertEquals(uri, target?.clipData?.getItemAt(0)?.uri)
+ assertEquals("image/png", target?.type)
+ }
+
+ @Test
+ fun test_getShareIntent_spannableText() {
+ val clipData = ClipData.newPlainText("Test", SpannableString("Test Item"))
+ val intent = creator.getShareIntent(clipData, context)
+
+ assertEquals(Intent.ACTION_CHOOSER, intent.action)
+ assertFlags(intent, EXTERNAL_INTENT_FLAGS)
+ val target = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java)
+ assertEquals("Test Item", target?.getStringExtra(Intent.EXTRA_TEXT))
+ assertEquals("text/plain", target?.type)
+ }
+
+ // Assert that the given flags are set
+ private fun assertFlags(intent: Intent, flags: Int) {
+ assertTrue((intent.flags and flags) == flags)
+ }
+
+ companion object {
+ private const val EXTERNAL_INTENT_FLAGS: Int =
+ (Intent.FLAG_ACTIVITY_NEW_TASK or
+ Intent.FLAG_ACTIVITY_CLEAR_TASK or
+ Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/IntentCreatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/DefaultIntentCreatorTest.java
index ea6cb3b6d178..126b3fa9e7ca 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/IntentCreatorTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/DefaultIntentCreatorTest.java
@@ -36,13 +36,15 @@ import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class IntentCreatorTest extends SysuiTestCase {
+public class DefaultIntentCreatorTest extends SysuiTestCase {
private static final int EXTERNAL_INTENT_FLAGS = Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION;
+ private final DefaultIntentCreator mIntentCreator = new DefaultIntentCreator();
+
@Test
public void test_getTextEditorIntent() {
- Intent intent = IntentCreator.getTextEditorIntent(getContext());
+ Intent intent = mIntentCreator.getTextEditorIntent(getContext());
assertEquals(new ComponentName(getContext(), EditTextActivity.class),
intent.getComponent());
assertFlags(intent, Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
@@ -54,7 +56,7 @@ public class IntentCreatorTest extends SysuiTestCase {
"");
ClipData clipData = ClipData.newPlainText("Test", "Test Item");
- Intent intent = IntentCreator.getRemoteCopyIntent(clipData, getContext());
+ Intent intent = mIntentCreator.getRemoteCopyIntent(clipData, getContext());
assertEquals(null, intent.getComponent());
assertFlags(intent, EXTERNAL_INTENT_FLAGS);
@@ -66,7 +68,7 @@ public class IntentCreatorTest extends SysuiTestCase {
getContext().getOrCreateTestableResources().addOverride(R.string.config_remoteCopyPackage,
fakeComponent.flattenToString());
- intent = IntentCreator.getRemoteCopyIntent(clipData, getContext());
+ intent = mIntentCreator.getRemoteCopyIntent(clipData, getContext());
assertEquals(fakeComponent, intent.getComponent());
}
@@ -75,7 +77,7 @@ public class IntentCreatorTest extends SysuiTestCase {
getContext().getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor,
"");
Uri fakeUri = Uri.parse("content://foo");
- Intent intent = IntentCreator.getImageEditIntent(fakeUri, getContext());
+ Intent intent = mIntentCreator.getImageEditIntent(fakeUri, getContext());
assertEquals(Intent.ACTION_EDIT, intent.getAction());
assertEquals("image/*", intent.getType());
@@ -88,14 +90,14 @@ public class IntentCreatorTest extends SysuiTestCase {
"com.android.remotecopy.RemoteCopyActivity");
getContext().getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor,
fakeComponent.flattenToString());
- intent = IntentCreator.getImageEditIntent(fakeUri, getContext());
+ intent = mIntentCreator.getImageEditIntent(fakeUri, getContext());
assertEquals(fakeComponent, intent.getComponent());
}
@Test
public void test_getShareIntent_plaintext() {
ClipData clipData = ClipData.newPlainText("Test", "Test Item");
- Intent intent = IntentCreator.getShareIntent(clipData, getContext());
+ Intent intent = mIntentCreator.getShareIntent(clipData, getContext());
assertEquals(Intent.ACTION_CHOOSER, intent.getAction());
assertFlags(intent, EXTERNAL_INTENT_FLAGS);
@@ -108,7 +110,7 @@ public class IntentCreatorTest extends SysuiTestCase {
public void test_getShareIntent_html() {
ClipData clipData = ClipData.newHtmlText("Test", "Some HTML",
"<b>Some HTML</b>");
- Intent intent = IntentCreator.getShareIntent(clipData, getContext());
+ Intent intent = mIntentCreator.getShareIntent(clipData, getContext());
assertEquals(Intent.ACTION_CHOOSER, intent.getAction());
assertFlags(intent, EXTERNAL_INTENT_FLAGS);
@@ -122,7 +124,7 @@ public class IntentCreatorTest extends SysuiTestCase {
Uri uri = Uri.parse("content://something");
ClipData clipData = new ClipData("Test", new String[]{"image/png"},
new ClipData.Item(uri));
- Intent intent = IntentCreator.getShareIntent(clipData, getContext());
+ Intent intent = mIntentCreator.getShareIntent(clipData, getContext());
assertEquals(Intent.ACTION_CHOOSER, intent.getAction());
assertFlags(intent, EXTERNAL_INTENT_FLAGS);
@@ -135,7 +137,7 @@ public class IntentCreatorTest extends SysuiTestCase {
@Test
public void test_getShareIntent_spannableText() {
ClipData clipData = ClipData.newPlainText("Test", new SpannableString("Test Item"));
- Intent intent = IntentCreator.getShareIntent(clipData, getContext());
+ Intent intent = mIntentCreator.getShareIntent(clipData, getContext());
assertEquals(Intent.ACTION_CHOOSER, intent.getAction());
assertFlags(intent, EXTERNAL_INTENT_FLAGS);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt
index 299105e2dabd..e41d46ce90af 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt
@@ -20,6 +20,7 @@ import android.view.Display
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.dumpManager
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.testKosmos
@@ -29,6 +30,9 @@ import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyString
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.verify
@RunWith(AndroidJUnit4::class)
@SmallTest
@@ -99,6 +103,11 @@ class PerDisplayInstanceRepositoryImplTest : SysuiTestCase() {
assertThat(fakePerDisplayInstanceProviderWithTeardown.destroyed).isEmpty()
}
+ @Test
+ fun start_registersDumpable() {
+ verify(kosmos.dumpManager).registerNormalDumpable(anyString(), eq(underTest))
+ }
+
private fun createDisplay(displayId: Int): Display =
display(type = Display.TYPE_INTERNAL, id = displayId)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 2c852c3f6185..94db429c2225 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -598,7 +598,8 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
mKeyguardClockPositionAlgorithm,
mMSDLPlayer,
mBrightnessMirrorShowingRepository,
- new BlurConfig(0f, 0f));
+ new BlurConfig(0f, 0f),
+ () -> mKosmos.getFakeShadeDisplaysRepository());
mNotificationPanelViewController.initDependencies(
mCentralSurfaces,
null,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index f54c36754d31..89263fc42739 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -195,9 +195,7 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo
@EnableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND)
public void updateSystemUiStateFlags_updatesSysuiStateInteractor() {
var DISPLAY_ID = 10;
- var displayMock = display(TYPE_INTERNAL, /* flags= */ 0, /* id= */DISPLAY_ID,
- /* state= */ null);
- when(mView.getDisplay()).thenReturn(displayMock);
+ mKosmos.getFakeShadeDisplaysRepository().setPendingDisplayId(DISPLAY_ID);
mNotificationPanelViewController.updateSystemUiStateFlags();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
index 4f301031e77c..fad66581682f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
@@ -108,7 +108,7 @@ class DefaultClockProviderTest : SysuiTestCase() {
verify(mockSmallClockView).setColors(DOZE_COLOR, Color.MAGENTA)
verify(mockLargeClockView).setColors(DOZE_COLOR, Color.MAGENTA)
- clock.initialize(true, 0f, 0f, {})
+ clock.initialize(true, 0f, 0f, null)
val expectedColor = 0
verify(mockSmallClockView).setColors(DOZE_COLOR, expectedColor)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt
index 4bd02e0fab22..17509dc6a80f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt
@@ -19,6 +19,10 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class ConditionExtensionsTest : SysuiTestCase() {
private lateinit var testScope: TestScope
+ private val testCallback =
+ Condition.Callback {
+ // This is a no-op
+ }
@Before
fun setUp() {
@@ -33,7 +37,7 @@ class ConditionExtensionsTest : SysuiTestCase() {
assertThat(condition.isConditionSet).isFalse()
- condition.start()
+ condition.testStart()
assertThat(condition.isConditionSet).isTrue()
assertThat(condition.isConditionMet).isTrue()
}
@@ -46,7 +50,7 @@ class ConditionExtensionsTest : SysuiTestCase() {
assertThat(condition.isConditionSet).isFalse()
- condition.start()
+ condition.testStart()
assertThat(condition.isConditionSet).isTrue()
assertThat(condition.isConditionMet).isFalse()
}
@@ -56,7 +60,7 @@ class ConditionExtensionsTest : SysuiTestCase() {
testScope.runTest {
val flow = emptyFlow<Boolean>()
val condition = flow.toCondition(scope = this, Condition.START_EAGERLY)
- condition.start()
+ condition.testStop()
assertThat(condition.isConditionSet).isFalse()
assertThat(condition.isConditionMet).isFalse()
@@ -72,7 +76,7 @@ class ConditionExtensionsTest : SysuiTestCase() {
strategy = Condition.START_EAGERLY,
initialValue = true,
)
- condition.start()
+ condition.testStart()
assertThat(condition.isConditionSet).isTrue()
assertThat(condition.isConditionMet).isTrue()
@@ -88,7 +92,7 @@ class ConditionExtensionsTest : SysuiTestCase() {
strategy = Condition.START_EAGERLY,
initialValue = false,
)
- condition.start()
+ condition.testStart()
assertThat(condition.isConditionSet).isTrue()
assertThat(condition.isConditionMet).isFalse()
@@ -99,7 +103,7 @@ class ConditionExtensionsTest : SysuiTestCase() {
testScope.runTest {
val flow = MutableStateFlow(false)
val condition = flow.toCondition(scope = this, strategy = Condition.START_EAGERLY)
- condition.start()
+ condition.testStart()
assertThat(condition.isConditionSet).isTrue()
assertThat(condition.isConditionMet).isFalse()
@@ -110,7 +114,7 @@ class ConditionExtensionsTest : SysuiTestCase() {
flow.value = false
assertThat(condition.isConditionMet).isFalse()
- condition.stop()
+ condition.testStop()
}
@Test
@@ -120,10 +124,18 @@ class ConditionExtensionsTest : SysuiTestCase() {
val condition = flow.toCondition(scope = this, strategy = Condition.START_EAGERLY)
assertThat(flow.subscriptionCount.value).isEqualTo(0)
- condition.start()
+ condition.testStart()
assertThat(flow.subscriptionCount.value).isEqualTo(1)
- condition.stop()
+ condition.testStop()
assertThat(flow.subscriptionCount.value).isEqualTo(0)
}
+
+ fun Condition.testStart() {
+ addCallback(testCallback)
+ }
+
+ fun Condition.testStop() {
+ removeCallback(testCallback)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/FakeCondition.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/FakeCondition.java
index 5416536305fc..da660e2f6009 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/FakeCondition.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/FakeCondition.java
@@ -40,7 +40,7 @@ public class FakeCondition extends Condition {
}
@Override
- protected int getStartStrategy() {
+ public int getStartStrategy() {
return START_EAGERLY;
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt
index 7ed2bd38bcd2..0b9b297130a2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt
@@ -34,6 +34,8 @@ import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
+import com.android.systemui.statusbar.notification.data.repository.addNotif
+import com.android.systemui.statusbar.notification.data.repository.removeNotif
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
import com.android.systemui.statusbar.notification.shared.CallType
@@ -409,6 +411,63 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun shownNotificationChips_lastAppVisibleTimeMaintainedAcrossNotifAddsAndRemoves() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.shownNotificationChips)
+
+ val notif1Info = NotifInfo("notif1", mock<StatusBarIconView>(), uid = 100)
+ val notif2Info = NotifInfo("notif2", mock<StatusBarIconView>(), uid = 200)
+
+ // Have notif1's app start as showing and then hide later so we get the chip
+ activityManagerRepository.fake.startingIsAppVisibleValue = true
+ fakeSystemClock.setCurrentTimeMillis(9_000)
+ activeNotificationListRepository.addNotif(
+ activeNotificationModel(
+ key = notif1Info.key,
+ uid = notif1Info.uid,
+ statusBarChipIcon = notif1Info.icon,
+ promotedContent =
+ PromotedNotificationContentModel.Builder(notif1Info.key).build(),
+ )
+ )
+ activityManagerRepository.fake.setIsAppVisible(notif1Info.uid, isAppVisible = false)
+
+ assertThat(latest!![0].key).isEqualTo(notif1Info.key)
+ assertThat(latest!![0].lastAppVisibleTime).isEqualTo(9_000)
+
+ // WHEN a new notification is added
+ activityManagerRepository.fake.startingIsAppVisibleValue = true
+ fakeSystemClock.setCurrentTimeMillis(10_000)
+ activeNotificationListRepository.addNotif(
+ activeNotificationModel(
+ key = notif2Info.key,
+ uid = notif2Info.uid,
+ statusBarChipIcon = notif2Info.icon,
+ promotedContent =
+ PromotedNotificationContentModel.Builder(notif2Info.key).build(),
+ )
+ )
+ activityManagerRepository.fake.setIsAppVisible(notif2Info.uid, isAppVisible = false)
+
+ // THEN the new notification is first
+ assertThat(latest!![0].key).isEqualTo(notif2Info.key)
+ assertThat(latest!![0].lastAppVisibleTime).isEqualTo(10_000)
+
+ // And THEN the original notification maintains its lastAppVisibleTime
+ assertThat(latest!![1].key).isEqualTo(notif1Info.key)
+ assertThat(latest!![1].lastAppVisibleTime).isEqualTo(9_000)
+
+ // WHEN notif1 is removed
+ fakeSystemClock.setCurrentTimeMillis(11_000)
+ activeNotificationListRepository.removeNotif(notif1Info.key)
+
+ // THEN notif2 still has its lastAppVisibleTime
+ assertThat(latest!![0].key).isEqualTo(notif2Info.key)
+ assertThat(latest!![0].lastAppVisibleTime).isEqualTo(10_000)
+ }
+
+ @Test
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
fun shownNotificationChips_sortedByLastAppVisibleTime() =
kosmos.runTest {
val latest by collectLastValue(underTest.shownNotificationChips)
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 1b5b0d6ff897..e2d1498270c8 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
@@ -16,18 +16,19 @@
package com.android.systemui.statusbar.chips.ui.viewmodel
+import android.content.Context
import android.content.DialogInterface
import android.content.packageManager
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.Expandable
+import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.collectLastValue
@@ -181,7 +182,7 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() {
val latest by collectLastValue(underTest.primaryChip)
- assertIsCallChip(latest, notificationKey)
+ assertIsCallChip(latest, notificationKey, context)
}
@Test
@@ -196,7 +197,7 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() {
val latest by collectLastValue(underTest.primaryChip)
- assertIsCallChip(latest, callNotificationKey)
+ assertIsCallChip(latest, callNotificationKey, context)
// WHEN the higher priority media projection chip is added
mediaProjectionState.value =
@@ -241,7 +242,7 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() {
mediaProjectionState.value = MediaProjectionState.NotProjecting
// THEN the lower priority call is used
- assertIsCallChip(latest, callNotificationKey)
+ assertIsCallChip(latest, callNotificationKey, context)
}
/** Regression test for b/347726238. */
@@ -395,18 +396,29 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() {
assertThat(icon.res).isEqualTo(R.drawable.ic_present_to_all)
}
- fun assertIsCallChip(latest: OngoingActivityChipModel?, notificationKey: String) {
+ fun assertIsCallChip(
+ latest: OngoingActivityChipModel?,
+ notificationKey: String,
+ context: Context,
+ ) {
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
assertThat((latest as OngoingActivityChipModel.Active).key).isEqualTo(notificationKey)
if (StatusBarConnectedDisplays.isEnabled) {
assertNotificationIcon(latest, notificationKey)
- return
+ } else {
+ val contentDescription =
+ if (latest.icon is OngoingActivityChipModel.ChipIcon.SingleColorIcon) {
+ ((latest.icon) as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
+ .impl
+ .contentDescription
+ } else {
+ (latest.icon as OngoingActivityChipModel.ChipIcon.StatusBarView)
+ .contentDescription
+ }
+ assertThat(contentDescription.loadContentDescription(context))
+ .contains(context.getString(R.string.ongoing_call_content_description))
}
- val icon =
- ((latest.icon) as OngoingActivityChipModel.ChipIcon.SingleColorIcon).impl
- as Icon.Resource
- assertThat(icon.res).isEqualTo(com.android.internal.R.drawable.ic_phone)
}
private fun assertNotificationIcon(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
index e3f93f237742..96c4a59f752d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
@@ -254,7 +254,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
val unused by collectLastValue(underTest.chips)
assertIsScreenRecordChip(latest!!.primary)
- assertIsCallChip(latest!!.secondary, callNotificationKey)
+ assertIsCallChip(latest!!.secondary, callNotificationKey, context)
assertThat(unused).isEqualTo(MultipleOngoingActivityChipsModel())
}
@@ -285,7 +285,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
assertThat(latest!!.active.size).isEqualTo(2)
assertIsScreenRecordChip(latest!!.active[0])
- assertIsCallChip(latest!!.active[1], callNotificationKey)
+ assertIsCallChip(latest!!.active[1], callNotificationKey, context)
assertThat(latest!!.overflow).isEmpty()
assertThat(latest!!.inactive.size).isEqualTo(2)
assertThat(unused).isEqualTo(MultipleOngoingActivityChipsModelLegacy())
@@ -617,7 +617,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
val unused by collectLastValue(underTest.chips)
assertIsShareToAppChip(latest!!.primary)
- assertIsCallChip(latest!!.secondary, callNotificationKey)
+ assertIsCallChip(latest!!.secondary, callNotificationKey, context)
assertThat(unused).isEqualTo(MultipleOngoingActivityChipsModel())
}
@@ -636,7 +636,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
assertThat(latest!!.active.size).isEqualTo(2)
assertIsShareToAppChip(latest!!.active[0])
- assertIsCallChip(latest!!.active[1], callNotificationKey)
+ assertIsCallChip(latest!!.active[1], callNotificationKey, context)
assertThat(latest!!.overflow).isEmpty()
assertThat(latest!!.inactive.size).isEqualTo(2)
assertThat(unused).isEqualTo(MultipleOngoingActivityChipsModelLegacy())
@@ -654,7 +654,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
val latest by collectLastValue(underTest.primaryChip)
- assertIsCallChip(latest, callNotificationKey)
+ assertIsCallChip(latest, callNotificationKey, context)
}
@DisableChipsModernization
@@ -671,7 +671,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
val latest by collectLastValue(underTest.chipsLegacy)
val unused by collectLastValue(underTest.chips)
- assertIsCallChip(latest!!.primary, callNotificationKey)
+ assertIsCallChip(latest!!.primary, callNotificationKey, context)
assertThat(latest!!.secondary)
.isInstanceOf(OngoingActivityChipModel.Inactive::class.java)
assertThat(unused).isEqualTo(MultipleOngoingActivityChipsModel())
@@ -691,7 +691,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
val unused by collectLastValue(underTest.chipsLegacy)
assertThat(latest!!.active.size).isEqualTo(1)
- assertIsCallChip(latest!!.active[0], callNotificationKey)
+ assertIsCallChip(latest!!.active[0], callNotificationKey, context)
assertThat(latest!!.overflow).isEmpty()
assertThat(latest!!.inactive.size).isEqualTo(3)
assertThat(unused).isEqualTo(MultipleOngoingActivityChipsModelLegacy())
@@ -950,7 +950,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
)
)
- assertIsCallChip(latest!!.primary, callNotificationKey)
+ assertIsCallChip(latest!!.primary, callNotificationKey, context)
assertIsNotifChip(latest!!.secondary, context, firstIcon, "firstNotif")
assertThat(unused).isEqualTo(MultipleOngoingActivityChipsModel())
}
@@ -984,7 +984,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
)
assertThat(latest!!.active.size).isEqualTo(2)
- assertIsCallChip(latest!!.active[0], callNotificationKey)
+ assertIsCallChip(latest!!.active[0], callNotificationKey, context)
assertIsNotifChip(latest!!.active[1], context, firstIcon, "firstNotif")
assertThat(latest!!.overflow.size).isEqualTo(1)
assertIsNotifChip(latest!!.overflow[0], context, secondIcon, "secondNotif")
@@ -1011,7 +1011,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
)
assertIsScreenRecordChip(latest!!.primary)
- assertIsCallChip(latest!!.secondary, callNotificationKey)
+ assertIsCallChip(latest!!.secondary, callNotificationKey, context)
assertThat(unused).isEqualTo(MultipleOngoingActivityChipsModel())
}
@@ -1057,7 +1057,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
assertThat(latest!!.active.size).isEqualTo(2)
assertIsScreenRecordChip(latest!!.active[0])
- assertIsCallChip(latest!!.active[1], callNotificationKey)
+ assertIsCallChip(latest!!.active[1], callNotificationKey, context)
assertThat(latest!!.overflow.size).isEqualTo(1)
assertIsNotifChip(latest!!.overflow[0], context, notifIcon, "notif")
assertThat(latest!!.inactive.size).isEqualTo(2)
@@ -1092,7 +1092,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
addOngoingCallState(callNotificationKey)
// THEN the higher priority call chip is used
- assertIsCallChip(latest, callNotificationKey)
+ assertIsCallChip(latest, callNotificationKey, context)
// WHEN the higher priority media projection chip is added
mediaProjectionState.value =
@@ -1145,7 +1145,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
mediaProjectionState.value = MediaProjectionState.NotProjecting
// THEN the lower priority call is used
- assertIsCallChip(latest, callNotificationKey)
+ assertIsCallChip(latest, callNotificationKey, context)
// WHEN the higher priority call is removed
removeOngoingCallState(key = callNotificationKey)
@@ -1186,7 +1186,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
// THEN the higher priority call chip is used as primary and notif is demoted to
// secondary
- assertIsCallChip(latest!!.primary, callNotificationKey)
+ assertIsCallChip(latest!!.primary, callNotificationKey, context)
assertIsNotifChip(latest!!.secondary, context, notifIcon, "notif")
assertThat(unused).isEqualTo(MultipleOngoingActivityChipsModel())
@@ -1201,7 +1201,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
// THEN the higher priority media projection chip is used as primary and call is demoted
// to secondary (and notif is dropped altogether)
assertIsShareToAppChip(latest!!.primary)
- assertIsCallChip(latest!!.secondary, callNotificationKey)
+ assertIsCallChip(latest!!.secondary, callNotificationKey, context)
assertThat(unused).isEqualTo(MultipleOngoingActivityChipsModel())
// WHEN the higher priority screen record chip is added
@@ -1264,7 +1264,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
// THEN the higher priority call chip and notif are active in that order
assertThat(latest!!.active.size).isEqualTo(2)
- assertIsCallChip(latest!!.active[0], callNotificationKey)
+ assertIsCallChip(latest!!.active[0], callNotificationKey, context)
assertIsNotifChip(latest!!.active[1], context, notifIcon, "notif")
assertThat(latest!!.overflow).isEmpty()
assertThat(latest!!.inactive.size).isEqualTo(3)
@@ -1282,7 +1282,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
// notif is demoted to overflow
assertThat(latest!!.active.size).isEqualTo(2)
assertIsShareToAppChip(latest!!.active[0])
- assertIsCallChip(latest!!.active[1], callNotificationKey)
+ assertIsCallChip(latest!!.active[1], callNotificationKey, context)
assertThat(latest!!.overflow.size).isEqualTo(1)
assertIsNotifChip(latest!!.overflow[0], context, notifIcon, "notif")
assertThat(latest!!.inactive.size).isEqualTo(2)
@@ -1295,7 +1295,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
// media projection and notif are demoted in overflow
assertThat(latest!!.active.size).isEqualTo(2)
assertIsScreenRecordChip(latest!!.active[0])
- assertIsCallChip(latest!!.active[1], callNotificationKey)
+ assertIsCallChip(latest!!.active[1], callNotificationKey, context)
assertThat(latest!!.overflow.size).isEqualTo(2)
assertIsShareToAppChip(latest!!.overflow[0])
assertIsNotifChip(latest!!.overflow[1], context, notifIcon, "notif")
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt
index 0f1cdac05f7a..fdc6657acd1d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt
@@ -31,6 +31,8 @@ import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.fakeSceneDataSource
+import com.android.systemui.shade.domain.interactor.enableDualShade
+import com.android.systemui.shade.domain.interactor.enableSingleShade
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationTransitionThresholds.EXPANSION_FOR_DELAYED_STACK_FADE_IN
@@ -69,6 +71,7 @@ class NotificationStackAppearanceIntegrationTest : SysuiTestCase() {
@Test
fun updateBounds() =
testScope.runTest {
+ kosmos.enableSingleShade()
val radius = MutableStateFlow(32)
val leftOffset = MutableStateFlow(0)
val shape by
@@ -106,7 +109,7 @@ class NotificationStackAppearanceIntegrationTest : SysuiTestCase() {
)
)
- // When: QuickSettings shows up full screen
+ // When: QuickSettings shows up full screen on single shade.
fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
val transitionState =
MutableStateFlow<ObservableTransitionState>(
@@ -115,6 +118,10 @@ class NotificationStackAppearanceIntegrationTest : SysuiTestCase() {
sceneInteractor.setTransitionState(transitionState)
// Then: shape is null
assertThat(shape).isNull()
+
+ // Same scenario on Dual Shade, shape should have clipping bounds
+ kosmos.enableDualShade()
+ assertThat(shape).isNotNull()
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapterTest.kt
index 83fd5dcd5f82..a13b8647e170 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapterTest.kt
@@ -34,123 +34,122 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
@RunWithLooper
-class BundleEntryTest : SysuiTestCase() {
- private lateinit var underTest: BundleEntry
+class BundleEntryAdapterTest : SysuiTestCase() {
+ private lateinit var underTest: BundleEntryAdapter
- @get:Rule
- val setFlagsRule = SetFlagsRule()
+ @get:Rule val setFlagsRule = SetFlagsRule()
@Before
fun setUp() {
- underTest = BundleEntry("key")
+ underTest = BundleEntryAdapter(BundleEntry("key"))
}
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
fun getParent_adapter() {
- assertThat(underTest.entryAdapter.parent).isEqualTo(GroupEntry.ROOT_ENTRY)
+ assertThat(underTest.parent).isEqualTo(GroupEntry.ROOT_ENTRY)
}
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
fun isTopLevelEntry_adapter() {
- assertThat(underTest.entryAdapter.isTopLevelEntry).isTrue()
+ assertThat(underTest.isTopLevelEntry).isTrue()
}
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
fun getRow_adapter() {
- assertThat(underTest.entryAdapter.row).isNull()
+ assertThat(underTest.row).isNull()
}
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
fun isGroupRoot_adapter() {
- assertThat(underTest.entryAdapter.isGroupRoot).isTrue()
+ assertThat(underTest.isGroupRoot).isTrue()
}
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
fun getKey_adapter() {
- assertThat(underTest.entryAdapter.key).isEqualTo("key")
+ assertThat(underTest.key).isEqualTo("key")
}
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
fun isClearable_adapter() {
- assertThat(underTest.entryAdapter.isClearable).isTrue()
+ assertThat(underTest.isClearable).isTrue()
}
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
fun getSummarization_adapter() {
- assertThat(underTest.entryAdapter.summarization).isNull()
+ assertThat(underTest.summarization).isNull()
}
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
fun getContrastedColor_adapter() {
- assertThat(underTest.entryAdapter.getContrastedColor(context, false, Color.WHITE))
+ assertThat(underTest.getContrastedColor(context, false, Color.WHITE))
.isEqualTo(Notification.COLOR_DEFAULT)
}
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
fun canPeek_adapter() {
- assertThat(underTest.entryAdapter.canPeek()).isFalse()
+ assertThat(underTest.canPeek()).isFalse()
}
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
fun getWhen_adapter() {
- assertThat(underTest.entryAdapter.`when`).isEqualTo(0)
+ assertThat(underTest.`when`).isEqualTo(0)
}
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
fun isColorized() {
- assertThat(underTest.entryAdapter.isColorized).isFalse()
+ assertThat(underTest.isColorized).isFalse()
}
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
fun getSbn() {
- assertThat(underTest.entryAdapter.sbn).isNull()
+ assertThat(underTest.sbn).isNull()
}
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
fun canDragAndDrop() {
- assertThat(underTest.entryAdapter.canDragAndDrop()).isFalse()
+ assertThat(underTest.canDragAndDrop()).isFalse()
}
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
fun isBubble() {
- assertThat(underTest.entryAdapter.isBubbleCapable).isFalse()
+ assertThat(underTest.isBubbleCapable).isFalse()
}
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
fun getStyle() {
- assertThat(underTest.entryAdapter.style).isNull()
+ assertThat(underTest.style).isNull()
}
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
fun getSectionBucket() {
- assertThat(underTest.entryAdapter.sectionBucket).isEqualTo(underTest.bucket)
+ assertThat(underTest.sectionBucket).isEqualTo(underTest.entry.bucket)
}
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
fun isAmbient() {
- assertThat(underTest.entryAdapter.isAmbient).isFalse()
+ assertThat(underTest.isAmbient).isFalse()
}
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
fun canShowFullScreen() {
- assertThat(underTest.entryAdapter.isFullScreenCapable()).isFalse()
+ assertThat(underTest.isFullScreenCapable()).isFalse()
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapterTest.kt
new file mode 100644
index 000000000000..b6889afa4e8a
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapterTest.kt
@@ -0,0 +1,370 @@
+/*
+ * 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.notification.collection
+
+import android.app.ActivityManager
+import android.app.Notification
+import android.app.NotificationManager
+import android.app.PendingIntent
+import android.os.UserHandle
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.RankingBuilder
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.row.entryAdapterFactory
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
+import com.android.systemui.statusbar.notification.stack.BUCKET_ALERTING
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito
+import org.mockito.Mockito.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@RunWithLooper
+class NotificationEntryAdapterTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+
+ private val factory: EntryAdapterFactory = kosmos.entryAdapterFactory
+ private lateinit var underTest: NotificationEntryAdapter
+
+ @get:Rule val setFlagsRule = SetFlagsRule()
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun getParent_adapter() {
+ val ge = GroupEntryBuilder().build()
+ val notification: Notification =
+ Notification.Builder(mContext, "").setSmallIcon(R.drawable.ic_person).build()
+
+ val entry =
+ NotificationEntryBuilder()
+ .setNotification(notification)
+ .setUser(UserHandle(ActivityManager.getCurrentUser()))
+ .setParent(ge)
+ .build()
+
+ underTest = factory.create(entry) as NotificationEntryAdapter
+ assertThat(underTest.parent).isEqualTo(entry.parent)
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun isTopLevelEntry_adapter() {
+ val notification: Notification =
+ Notification.Builder(mContext, "").setSmallIcon(R.drawable.ic_person).build()
+
+ val entry =
+ NotificationEntryBuilder()
+ .setNotification(notification)
+ .setUser(UserHandle(ActivityManager.getCurrentUser()))
+ .setParent(GroupEntry.ROOT_ENTRY)
+ .build()
+
+ underTest = factory.create(entry) as NotificationEntryAdapter
+ assertThat(underTest.isTopLevelEntry).isTrue()
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun getKey_adapter() {
+ val notification: Notification =
+ Notification.Builder(mContext, "").setSmallIcon(R.drawable.ic_person).build()
+
+ val entry =
+ NotificationEntryBuilder()
+ .setNotification(notification)
+ .setUser(UserHandle(ActivityManager.getCurrentUser()))
+ .build()
+
+ underTest = factory.create(entry) as NotificationEntryAdapter
+ assertThat(underTest.key).isEqualTo(entry.key)
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun getRow_adapter() {
+ val row = Mockito.mock(ExpandableNotificationRow::class.java)
+ val notification: Notification =
+ Notification.Builder(mContext, "").setSmallIcon(R.drawable.ic_person).build()
+
+ val entry =
+ NotificationEntryBuilder()
+ .setNotification(notification)
+ .setUser(UserHandle(ActivityManager.getCurrentUser()))
+ .build()
+ entry.row = row
+
+ underTest = factory.create(entry) as NotificationEntryAdapter
+ assertThat(underTest.row).isEqualTo(entry.row)
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun isGroupRoot_adapter_groupSummary() {
+ val row = Mockito.mock(ExpandableNotificationRow::class.java)
+ val notification: Notification =
+ Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .setGroupSummary(true)
+ .setGroup("key")
+ .build()
+
+ val entry =
+ NotificationEntryBuilder()
+ .setNotification(notification)
+ .setUser(UserHandle(ActivityManager.getCurrentUser()))
+ .setParent(GroupEntry.ROOT_ENTRY)
+ .build()
+ entry.row = row
+
+ underTest = factory.create(entry) as NotificationEntryAdapter
+ assertThat(underTest.isGroupRoot).isFalse()
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun isGroupRoot_adapter_groupChild() {
+ val notification: Notification =
+ Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .setGroupSummary(true)
+ .setGroup("key")
+ .build()
+
+ val parent = NotificationEntryBuilder().setParent(GroupEntry.ROOT_ENTRY).build()
+ val groupEntry = GroupEntryBuilder().setSummary(parent)
+
+ val entry =
+ NotificationEntryBuilder()
+ .setNotification(notification)
+ .setUser(UserHandle(ActivityManager.getCurrentUser()))
+ .setParent(groupEntry.build())
+ .build()
+
+ underTest = factory.create(entry) as NotificationEntryAdapter
+ assertThat(underTest.isGroupRoot).isFalse()
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun isClearable_adapter() {
+ val row = Mockito.mock(ExpandableNotificationRow::class.java)
+ val notification: Notification =
+ Notification.Builder(mContext, "").setSmallIcon(R.drawable.ic_person).build()
+
+ val entry =
+ NotificationEntryBuilder()
+ .setNotification(notification)
+ .setUser(UserHandle(ActivityManager.getCurrentUser()))
+ .build()
+ entry.row = row
+
+ underTest = factory.create(entry) as NotificationEntryAdapter
+ assertThat(underTest.isClearable).isEqualTo(entry.isClearable)
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun getSummarization_adapter() {
+ val row = Mockito.mock(ExpandableNotificationRow::class.java)
+ val notification: Notification =
+ Notification.Builder(mContext, "").setSmallIcon(R.drawable.ic_person).build()
+
+ val entry =
+ NotificationEntryBuilder()
+ .setNotification(notification)
+ .setUser(UserHandle(ActivityManager.getCurrentUser()))
+ .build()
+ val ranking = RankingBuilder(entry.ranking).setSummarization("hello").build()
+ entry.setRanking(ranking)
+ entry.row = row
+
+ underTest = factory.create(entry) as NotificationEntryAdapter
+ assertThat(underTest.summarization).isEqualTo("hello")
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun getIcons_adapter() {
+ val row = Mockito.mock(ExpandableNotificationRow::class.java)
+ val notification: Notification =
+ Notification.Builder(mContext, "").setSmallIcon(R.drawable.ic_person).build()
+
+ val entry =
+ NotificationEntryBuilder()
+ .setNotification(notification)
+ .setUser(UserHandle(ActivityManager.getCurrentUser()))
+ .build()
+ entry.row = row
+
+ underTest = factory.create(entry) as NotificationEntryAdapter
+ assertThat(underTest.icons).isEqualTo(entry.icons)
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun isColorized() {
+ val notification: Notification =
+ Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .setColorized(true)
+ .build()
+
+ val entry = NotificationEntryBuilder().setNotification(notification).build()
+
+ underTest = factory.create(entry) as NotificationEntryAdapter
+ assertThat(underTest.isColorized).isEqualTo(entry.sbn.notification.isColorized)
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun getSbn() {
+ val notification: Notification =
+ Notification.Builder(mContext, "").setSmallIcon(R.drawable.ic_person).build()
+
+ val entry = NotificationEntryBuilder().setNotification(notification).build()
+
+ underTest = factory.create(entry) as NotificationEntryAdapter
+ assertThat(underTest.sbn).isEqualTo(entry.sbn)
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun canDragAndDrop() {
+ val pi = Mockito.mock(PendingIntent::class.java)
+ Mockito.`when`(pi.isActivity).thenReturn(true)
+ val notification: Notification =
+ Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .setContentIntent(pi)
+ .build()
+
+ val entry = NotificationEntryBuilder().setNotification(notification).build()
+
+ underTest = factory.create(entry) as NotificationEntryAdapter
+ assertThat(underTest.canDragAndDrop()).isTrue()
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun isBubble() {
+ val notification: Notification =
+ Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .setFlag(Notification.FLAG_BUBBLE, true)
+ .build()
+
+ val entry = NotificationEntryBuilder().setNotification(notification).build()
+
+ underTest = factory.create(entry) as NotificationEntryAdapter
+ assertThat(underTest.isBubbleCapable).isEqualTo(entry.isBubble)
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun getStyle() {
+ val notification: Notification =
+ Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .setStyle(Notification.BigTextStyle())
+ .build()
+
+ val entry = NotificationEntryBuilder().setNotification(notification).build()
+
+ underTest = factory.create(entry) as NotificationEntryAdapter
+ assertThat(underTest.style).isEqualTo(entry.notificationStyle)
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun getSectionBucket() {
+ val notification: Notification =
+ Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .setStyle(Notification.BigTextStyle())
+ .build()
+
+ val entry = NotificationEntryBuilder().setNotification(notification).build()
+ entry.bucket = BUCKET_ALERTING
+
+ underTest = factory.create(entry) as NotificationEntryAdapter
+ assertThat(underTest.sectionBucket).isEqualTo(BUCKET_ALERTING)
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun isAmbient() {
+ val notification: Notification =
+ Notification.Builder(mContext, "").setSmallIcon(R.drawable.ic_person).build()
+
+ val entry =
+ NotificationEntryBuilder()
+ .setNotification(notification)
+ .setImportance(NotificationManager.IMPORTANCE_MIN)
+ .build()
+
+ underTest = factory.create(entry) as NotificationEntryAdapter
+ assertThat(underTest.isAmbient).isTrue()
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun canShowFullScreen() {
+ val notification: Notification =
+ Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .setFullScreenIntent(Mockito.mock(PendingIntent::class.java), true)
+ .build()
+
+ val entry =
+ NotificationEntryBuilder()
+ .setNotification(notification)
+ .setImportance(NotificationManager.IMPORTANCE_MIN)
+ .build()
+
+ underTest = factory.create(entry) as NotificationEntryAdapter
+ assertThat(underTest.isFullScreenCapable).isTrue()
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun onNotificationBubbleIconClicked() {
+ val notification: Notification =
+ Notification.Builder(mContext, "").setSmallIcon(R.drawable.ic_person).build()
+
+ val entry =
+ NotificationEntryBuilder()
+ .setNotification(notification)
+ .setImportance(NotificationManager.IMPORTANCE_MIN)
+ .build()
+
+ underTest = factory.create(entry) as NotificationEntryAdapter
+
+ underTest.onNotificationBubbleIconClicked()
+ verify((factory as? EntryAdapterFactoryImpl)?.getNotificationActivityStarter())
+ ?.onNotificationBubbleIconClicked(entry)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
index 481081344dbb..790b2c343a11 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
@@ -21,24 +21,17 @@ import static android.app.Notification.CATEGORY_CALL;
import static android.app.Notification.CATEGORY_EVENT;
import static android.app.Notification.CATEGORY_MESSAGE;
import static android.app.Notification.CATEGORY_REMINDER;
-import static android.app.Notification.FLAG_BUBBLE;
import static android.app.Notification.FLAG_FSI_REQUESTED_BUT_DENIED;
import static android.app.Notification.FLAG_PROMOTED_ONGOING;
-import static android.app.NotificationManager.IMPORTANCE_MIN;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
import static com.android.systemui.statusbar.NotificationEntryHelper.modifySbn;
-import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_ALERTING;
-
-import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.app.Notification;
@@ -66,8 +59,6 @@ import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.SbnBuilder;
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips;
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
@@ -458,336 +449,6 @@ public class NotificationEntryTest extends SysuiTestCase {
// no crash, good
}
- @Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
- public void getParent_adapter() {
- GroupEntry ge = new GroupEntryBuilder()
- .build();
- Notification notification = new Notification.Builder(mContext, "")
- .setSmallIcon(R.drawable.ic_person)
- .build();
-
- NotificationEntry entry = new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_NAME)
- .setOpPkg(TEST_PACKAGE_NAME)
- .setUid(TEST_UID)
- .setChannel(mChannel)
- .setId(mId++)
- .setNotification(notification)
- .setUser(new UserHandle(ActivityManager.getCurrentUser()))
- .setParent(ge)
- .build();
-
- assertThat(entry.getEntryAdapter().getParent()).isEqualTo(entry.getParent());
- }
-
- @Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
- public void isTopLevelEntry_adapter() {
- Notification notification = new Notification.Builder(mContext, "")
- .setSmallIcon(R.drawable.ic_person)
- .build();
-
- NotificationEntry entry = new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_NAME)
- .setOpPkg(TEST_PACKAGE_NAME)
- .setUid(TEST_UID)
- .setChannel(mChannel)
- .setId(mId++)
- .setNotification(notification)
- .setUser(new UserHandle(ActivityManager.getCurrentUser()))
- .setParent(GroupEntry.ROOT_ENTRY)
- .build();
-
- assertThat(entry.getEntryAdapter().isTopLevelEntry()).isTrue();
- }
-
- @Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
- public void getKey_adapter() {
- Notification notification = new Notification.Builder(mContext, "")
- .setSmallIcon(R.drawable.ic_person)
- .build();
-
- NotificationEntry entry = new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_NAME)
- .setOpPkg(TEST_PACKAGE_NAME)
- .setUid(TEST_UID)
- .setChannel(mChannel)
- .setId(mId++)
- .setNotification(notification)
- .setUser(new UserHandle(ActivityManager.getCurrentUser()))
- .build();
-
- assertThat(entry.getEntryAdapter().getKey()).isEqualTo(entry.getKey());
- }
-
- @Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
- public void getRow_adapter() {
- ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
- Notification notification = new Notification.Builder(mContext, "")
- .setSmallIcon(R.drawable.ic_person)
- .build();
-
- NotificationEntry entry = new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_NAME)
- .setOpPkg(TEST_PACKAGE_NAME)
- .setUid(TEST_UID)
- .setChannel(mChannel)
- .setId(mId++)
- .setNotification(notification)
- .setUser(new UserHandle(ActivityManager.getCurrentUser()))
- .build();
- entry.setRow(row);
-
- assertThat(entry.getEntryAdapter().getRow()).isEqualTo(entry.getRow());
- }
-
- @Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
- public void isGroupRoot_adapter_groupSummary() {
- ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
- Notification notification = new Notification.Builder(mContext, "")
- .setSmallIcon(R.drawable.ic_person)
- .setGroupSummary(true)
- .setGroup("key")
- .build();
-
- NotificationEntry entry = new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_NAME)
- .setOpPkg(TEST_PACKAGE_NAME)
- .setUid(TEST_UID)
- .setChannel(mChannel)
- .setId(mId++)
- .setNotification(notification)
- .setUser(new UserHandle(ActivityManager.getCurrentUser()))
- .setParent(GroupEntry.ROOT_ENTRY)
- .build();
- entry.setRow(row);
-
- assertThat(entry.getEntryAdapter().isGroupRoot()).isFalse();
- }
-
- @Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
- public void isGroupRoot_adapter_groupChild() {
- Notification notification = new Notification.Builder(mContext, "")
- .setSmallIcon(R.drawable.ic_person)
- .setGroupSummary(true)
- .setGroup("key")
- .build();
-
- NotificationEntry parent = new NotificationEntryBuilder()
- .setParent(GroupEntry.ROOT_ENTRY)
- .build();
- GroupEntryBuilder groupEntry = new GroupEntryBuilder()
- .setSummary(parent);
-
- NotificationEntry entry = new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_NAME)
- .setOpPkg(TEST_PACKAGE_NAME)
- .setUid(TEST_UID)
- .setChannel(mChannel)
- .setId(mId++)
- .setNotification(notification)
- .setUser(new UserHandle(ActivityManager.getCurrentUser()))
- .setParent(groupEntry.build())
- .build();
-
- assertThat(entry.getEntryAdapter().isGroupRoot()).isFalse();
- }
-
- @Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
- public void isClearable_adapter() {
- ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
- Notification notification = new Notification.Builder(mContext, "")
- .setSmallIcon(R.drawable.ic_person)
- .build();
-
- NotificationEntry entry = new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_NAME)
- .setOpPkg(TEST_PACKAGE_NAME)
- .setUid(TEST_UID)
- .setChannel(mChannel)
- .setId(mId++)
- .setNotification(notification)
- .setUser(new UserHandle(ActivityManager.getCurrentUser()))
- .build();
- entry.setRow(row);
-
- assertThat(entry.getEntryAdapter().isClearable()).isEqualTo(entry.isClearable());
- }
-
- @Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
- public void getSummarization_adapter() {
- ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
- Notification notification = new Notification.Builder(mContext, "")
- .setSmallIcon(R.drawable.ic_person)
- .build();
-
- NotificationEntry entry = new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_NAME)
- .setOpPkg(TEST_PACKAGE_NAME)
- .setUid(TEST_UID)
- .setChannel(mChannel)
- .setId(mId++)
- .setNotification(notification)
- .setUser(new UserHandle(ActivityManager.getCurrentUser()))
- .build();
- Ranking ranking = new RankingBuilder(entry.getRanking())
- .setSummarization("hello")
- .build();
- entry.setRanking(ranking);
- entry.setRow(row);
-
- assertThat(entry.getEntryAdapter().getSummarization()).isEqualTo("hello");
- }
-
- @Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
- public void getIcons_adapter() {
- ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
- Notification notification = new Notification.Builder(mContext, "")
- .setSmallIcon(R.drawable.ic_person)
- .build();
-
- NotificationEntry entry = new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_NAME)
- .setOpPkg(TEST_PACKAGE_NAME)
- .setUid(TEST_UID)
- .setChannel(mChannel)
- .setId(mId++)
- .setNotification(notification)
- .setUser(new UserHandle(ActivityManager.getCurrentUser()))
- .build();
- entry.setRow(row);
-
- assertThat(entry.getEntryAdapter().getIcons()).isEqualTo(entry.getIcons());
- }
-
- @Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
- public void isColorized() {
- Notification notification = new Notification.Builder(mContext, "")
- .setSmallIcon(R.drawable.ic_person)
- .setColorized(true)
- .build();
-
- NotificationEntry entry = new NotificationEntryBuilder()
- .setNotification(notification)
- .build();
- assertThat(entry.getEntryAdapter().isColorized()).isEqualTo(
- entry.getSbn().getNotification().isColorized());
- }
-
- @Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
- public void getSbn() {
- Notification notification = new Notification.Builder(mContext, "")
- .setSmallIcon(R.drawable.ic_person)
- .build();
-
- NotificationEntry entry = new NotificationEntryBuilder()
- .setNotification(notification)
- .build();
- assertThat(entry.getEntryAdapter().getSbn()).isEqualTo(
- entry.getSbn());
- }
-
- @Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
- public void canDragAndDrop() {
- PendingIntent pi = mock(PendingIntent.class);
- when(pi.isActivity()).thenReturn(true);
- Notification notification = new Notification.Builder(mContext, "")
- .setSmallIcon(R.drawable.ic_person)
- .setContentIntent(pi)
- .build();
-
- NotificationEntry entry = new NotificationEntryBuilder()
- .setNotification(notification)
- .build();
- assertThat(entry.getEntryAdapter().canDragAndDrop()).isTrue();
- }
-
- @Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
- public void isBubble() {
- Notification notification = new Notification.Builder(mContext, "")
- .setSmallIcon(R.drawable.ic_person)
- .setFlag(FLAG_BUBBLE, true)
- .build();
-
- NotificationEntry entry = new NotificationEntryBuilder()
- .setNotification(notification)
- .build();
- assertThat(entry.getEntryAdapter().isBubbleCapable()).isEqualTo(entry.isBubble());
- }
-
- @Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
- public void getStyle() {
- Notification notification = new Notification.Builder(mContext, "")
- .setSmallIcon(R.drawable.ic_person)
- .setStyle(new Notification.BigTextStyle())
- .build();
-
- NotificationEntry entry = new NotificationEntryBuilder()
- .setNotification(notification)
- .build();
- assertThat(entry.getEntryAdapter().getStyle()).isEqualTo(entry.getNotificationStyle());
- }
-
- @Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
- public void getSectionBucket() {
- Notification notification = new Notification.Builder(mContext, "")
- .setSmallIcon(R.drawable.ic_person)
- .setStyle(new Notification.BigTextStyle())
- .build();
-
- NotificationEntry entry = new NotificationEntryBuilder()
- .setNotification(notification)
- .build();
- entry.setBucket(BUCKET_ALERTING);
-
- assertThat(entry.getEntryAdapter().getSectionBucket()).isEqualTo(BUCKET_ALERTING);
- }
-
- @Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
- public void isAmbient() {
- Notification notification = new Notification.Builder(mContext, "")
- .setSmallIcon(R.drawable.ic_person)
- .build();
-
- NotificationEntry entry = new NotificationEntryBuilder()
- .setNotification(notification)
- .setImportance(IMPORTANCE_MIN)
- .build();
-
- assertThat(entry.getEntryAdapter().isAmbient()).isTrue();
- }
-
- @Test
- @EnableFlags(NotificationBundleUi.FLAG_NAME)
- public void canShowFullScreen() {
- Notification notification = new Notification.Builder(mContext, "")
- .setSmallIcon(R.drawable.ic_person)
- .setFullScreenIntent(mock(PendingIntent.class), true)
- .build();
-
- NotificationEntry entry = new NotificationEntryBuilder()
- .setNotification(notification)
- .setImportance(IMPORTANCE_MIN)
- .build();
-
- assertThat(entry.getEntryAdapter().isFullScreenCapable()).isTrue();
- }
-
private Notification.Action createContextualAction(String title) {
return new Notification.Action.Builder(
Icon.createWithResource(getContext(), android.R.drawable.sym_def_app_icon),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt
index 8e6aedcae506..9e27c79d54b8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar.notification.collection.render
-import android.os.Build
-import android.os.UserHandle
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
@@ -26,6 +24,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.log.assertLogsWtf
+import com.android.systemui.statusbar.notification.collection.EntryAdapterFactoryImpl
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
import com.android.systemui.statusbar.notification.collection.ListEntry
@@ -36,12 +35,13 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.OnBefo
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager.OnGroupExpansionChangeListener
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.NotificationTestHelper
+import com.android.systemui.statusbar.notification.row.entryAdapterFactory
import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
-import org.junit.Assume
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -54,11 +54,11 @@ import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
class GroupExpansionManagerTest : SysuiTestCase() {
- @get:Rule
- val setFlagsRule = SetFlagsRule()
+ @get:Rule val setFlagsRule = SetFlagsRule()
private lateinit var underTest: GroupExpansionManagerImpl
+ private val kosmos = testKosmos()
private lateinit var testHelper: NotificationTestHelper
private val dumpManager: DumpManager = mock()
private val groupMembershipManager: GroupMembershipManager = mock()
@@ -66,15 +66,14 @@ class GroupExpansionManagerTest : SysuiTestCase() {
private val pipeline: NotifPipeline = mock()
private lateinit var beforeRenderListListener: OnBeforeRenderListListener
+ private val factory: EntryAdapterFactoryImpl = kosmos.entryAdapterFactory
private lateinit var summary1: NotificationEntry
private lateinit var summary2: NotificationEntry
private lateinit var entries: List<ListEntry>
private fun notificationEntry(pkg: String, id: Int, parent: ExpandableNotificationRow?) =
NotificationEntryBuilder().setPkg(pkg).setId(id).build().apply {
- row = testHelper.createRow().apply {
- setIsChildInGroup(true, parent)
- }
+ row = testHelper.createRow().apply { setIsChildInGroup(true, parent) }
}
@Before
@@ -91,7 +90,7 @@ class GroupExpansionManagerTest : SysuiTestCase() {
listOf(
notificationEntry("foo", 2, summary1.row),
notificationEntry("foo", 3, summary1.row),
- notificationEntry("foo", 4, summary1.row)
+ notificationEntry("foo", 4, summary1.row),
)
)
.build(),
@@ -101,11 +100,11 @@ class GroupExpansionManagerTest : SysuiTestCase() {
listOf(
notificationEntry("bar", 2, summary2.row),
notificationEntry("bar", 3, summary2.row),
- notificationEntry("bar", 4, summary2.row)
+ notificationEntry("bar", 4, summary2.row),
)
)
.build(),
- notificationEntry("baz", 1, null)
+ notificationEntry("baz", 1, null),
)
whenever(groupMembershipManager.getGroupSummary(summary1)).thenReturn(summary1)
@@ -135,18 +134,20 @@ class GroupExpansionManagerTest : SysuiTestCase() {
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
fun notifyOnlyOnChange_withEntryAdapter() {
+ val entryAdapter1 = factory.create(summary1)
+ val entryAdapter2 = factory.create(summary2)
var listenerCalledCount = 0
underTest.registerGroupExpansionChangeListener { _, _ -> listenerCalledCount++ }
- underTest.setGroupExpanded(summary1.entryAdapter, false)
+ underTest.setGroupExpanded(entryAdapter1, false)
assertThat(listenerCalledCount).isEqualTo(0)
- underTest.setGroupExpanded(summary1.entryAdapter, true)
+ underTest.setGroupExpanded(entryAdapter1, true)
assertThat(listenerCalledCount).isEqualTo(1)
- underTest.setGroupExpanded(summary2.entryAdapter, true)
+ underTest.setGroupExpanded(entryAdapter2, true)
assertThat(listenerCalledCount).isEqualTo(2)
- underTest.setGroupExpanded(summary1.entryAdapter, true)
+ underTest.setGroupExpanded(entryAdapter1, true)
assertThat(listenerCalledCount).isEqualTo(2)
- underTest.setGroupExpanded(summary2.entryAdapter, false)
+ underTest.setGroupExpanded(entryAdapter2, false)
assertThat(listenerCalledCount).isEqualTo(3)
}
@@ -168,16 +169,17 @@ class GroupExpansionManagerTest : SysuiTestCase() {
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
fun expandUnattachedEntryAdapter() {
+ val entryAdapter = factory.create(summary1)
// First, expand the entry when it is attached.
- underTest.setGroupExpanded(summary1.entryAdapter, true)
- assertThat(underTest.isGroupExpanded(summary1.entryAdapter)).isTrue()
+ underTest.setGroupExpanded(entryAdapter, true)
+ assertThat(underTest.isGroupExpanded(entryAdapter)).isTrue()
// Un-attach it, and un-expand it.
NotificationEntryBuilder.setNewParent(summary1, null)
- underTest.setGroupExpanded(summary1.entryAdapter, false)
+ underTest.setGroupExpanded(entryAdapter, false)
// Expanding again should throw.
- assertLogsWtf { underTest.setGroupExpanded(summary1.entryAdapter, true) }
+ assertLogsWtf { underTest.setGroupExpanded(entryAdapter, true) }
}
@Test
@@ -207,6 +209,7 @@ class GroupExpansionManagerTest : SysuiTestCase() {
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
fun syncWithPipeline_withEntryAdapter() {
+ val entryAdapter = factory.create(summary1)
underTest.attach(pipeline)
beforeRenderListListener = withArgCaptor {
verify(pipeline).addOnBeforeRenderListListener(capture())
@@ -219,7 +222,7 @@ class GroupExpansionManagerTest : SysuiTestCase() {
verify(listener, never()).onGroupExpansionChange(any(), any())
// Expand one of the groups.
- underTest.setGroupExpanded(summary1.entryAdapter, true)
+ underTest.setGroupExpanded(entryAdapter, true)
verify(listener).onGroupExpansionChange(summary1.row, true)
// Empty the pipeline list and verify that the group is no longer expanded.
@@ -231,11 +234,15 @@ class GroupExpansionManagerTest : SysuiTestCase() {
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
fun isGroupExpanded() {
- underTest.setGroupExpanded(summary1.entryAdapter, true)
-
- assertThat(underTest.isGroupExpanded(summary1.entryAdapter)).isTrue();
- assertThat(underTest.isGroupExpanded(
- (entries[0] as? GroupEntry)?.getChildren()?.get(0)?.entryAdapter))
- .isTrue();
+ val entryAdapter = summary1.row.entryAdapter
+ underTest.setGroupExpanded(entryAdapter, true)
+
+ assertThat(underTest.isGroupExpanded(entryAdapter)).isTrue()
+ assertThat(
+ underTest.isGroupExpanded(
+ (entries[0] as? GroupEntry)?.getChildren()?.get(0)?.row?.entryAdapter
+ )
+ )
+ .isTrue()
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerTest.kt
index 2bbf094021cf..0ccf58507696 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerTest.kt
@@ -22,10 +22,13 @@ import android.platform.test.flag.junit.SetFlagsRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.notification.collection.EntryAdapterFactoryImpl
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.row.entryAdapterFactory
import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
@@ -35,8 +38,10 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class GroupMembershipManagerTest : SysuiTestCase() {
- @get:Rule
- val setFlagsRule = SetFlagsRule()
+ @get:Rule val setFlagsRule = SetFlagsRule()
+
+ private val kosmos = testKosmos()
+ private val factory: EntryAdapterFactoryImpl = kosmos.entryAdapterFactory
private var underTest = GroupMembershipManagerImpl()
@@ -144,14 +149,14 @@ class GroupMembershipManagerTest : SysuiTestCase() {
@EnableFlags(NotificationBundleUi.FLAG_NAME)
fun isChildEntryAdapterInGroup_topLevel() {
val topLevelEntry = NotificationEntryBuilder().setParent(GroupEntry.ROOT_ENTRY).build()
- assertThat(underTest.isChildInGroup(topLevelEntry.entryAdapter)).isFalse()
+ assertThat(underTest.isChildInGroup(factory.create(topLevelEntry))).isFalse()
}
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
fun isChildEntryAdapterInGroup_noParent() {
val noParentEntry = NotificationEntryBuilder().setParent(null).build()
- assertThat(underTest.isChildInGroup(noParentEntry.entryAdapter)).isFalse()
+ assertThat(underTest.isChildInGroup(factory.create(noParentEntry))).isFalse()
}
@Test
@@ -165,7 +170,7 @@ class GroupMembershipManagerTest : SysuiTestCase() {
.build()
GroupEntryBuilder().setKey(groupKey).setSummary(summary).build()
- assertThat(underTest.isChildInGroup(summary.entryAdapter)).isFalse()
+ assertThat(underTest.isChildInGroup(factory.create(summary))).isFalse()
}
@Test
@@ -180,14 +185,14 @@ class GroupMembershipManagerTest : SysuiTestCase() {
val entry = NotificationEntryBuilder().setGroup(mContext, groupKey).build()
GroupEntryBuilder().setKey(groupKey).setSummary(summary).addChild(entry).build()
- assertThat(underTest.isChildInGroup(entry.entryAdapter)).isTrue()
+ assertThat(underTest.isChildInGroup(factory.create(entry))).isTrue()
}
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
fun isGroupRoot_topLevelEntry() {
val entry = NotificationEntryBuilder().setParent(GroupEntry.ROOT_ENTRY).build()
- assertThat(underTest.isGroupRoot(entry.entryAdapter)).isFalse()
+ assertThat(underTest.isGroupRoot(factory.create(entry))).isFalse()
}
@Test
@@ -201,7 +206,7 @@ class GroupMembershipManagerTest : SysuiTestCase() {
.build()
GroupEntryBuilder().setKey(groupKey).setSummary(summary).build()
- assertThat(underTest.isGroupRoot(summary.entryAdapter)).isTrue()
+ assertThat(underTest.isGroupRoot(factory.create(summary))).isTrue()
}
@Test
@@ -216,6 +221,6 @@ class GroupMembershipManagerTest : SysuiTestCase() {
val entry = NotificationEntryBuilder().setGroup(mContext, groupKey).build()
GroupEntryBuilder().setKey(groupKey).setSummary(summary).addChild(entry).build()
- assertThat(underTest.isGroupRoot(entry.entryAdapter)).isFalse()
+ assertThat(underTest.isGroupRoot(factory.create(entry))).isFalse()
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
index f060caea4f24..bb12eff51288 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
@@ -37,9 +37,12 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.SbnBuilder
import com.android.systemui.statusbar.SmartReplyController
import com.android.systemui.statusbar.notification.ColorUpdateLogger
+import com.android.systemui.statusbar.notification.NotificationActivityStarter
import com.android.systemui.statusbar.notification.collection.EntryAdapter
+import com.android.systemui.statusbar.notification.collection.EntryAdapterFactory
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator
import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider
import com.android.systemui.statusbar.notification.collection.render.FakeNodeController
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager
@@ -47,6 +50,7 @@ import com.android.systemui.statusbar.notification.collection.render.GroupMember
import com.android.systemui.statusbar.notification.headsup.HeadsUpManager
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController.BUBBLES_SETTING_URI
+import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
@@ -112,6 +116,7 @@ class ExpandableNotificationRowControllerTest : SysuiTestCase() {
private val uiEventLogger: UiEventLogger = mock()
private val msdlPlayer: MSDLPlayer = mock()
private val rebindingTracker: NotificationRebindingTracker = mock()
+ private val entryAdapterFactory: EntryAdapterFactory = mock()
private lateinit var controller: ExpandableNotificationRowController
@Before
@@ -154,6 +159,7 @@ class ExpandableNotificationRowControllerTest : SysuiTestCase() {
uiEventLogger,
msdlPlayer,
rebindingTracker,
+ entryAdapterFactory,
)
whenever(view.childrenContainer).thenReturn(childrenContainer)
@@ -277,8 +283,10 @@ class ExpandableNotificationRowControllerTest : SysuiTestCase() {
whenever(view.privateLayout).thenReturn(childView)
val entryAdapter = mock(EntryAdapter::class.java)
val sbn =
- SbnBuilder().setNotification(Notification.Builder(mContext).build())
- .setUser(UserHandle.of(view.entry.sbn.userId)).build()
+ SbnBuilder()
+ .setNotification(Notification.Builder(mContext).build())
+ .setUser(UserHandle.of(view.entry.sbn.userId))
+ .build()
whenever(entryAdapter.sbn).thenReturn(sbn)
whenever(view.entryAdapter).thenReturn(entryAdapter)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index c376fad1503f..8e7733bb23ca 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -70,10 +70,14 @@ import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.notification.ColorUpdateLogger;
import com.android.systemui.statusbar.notification.ConversationNotificationProcessor;
+import com.android.systemui.statusbar.notification.NotificationActivityStarter;
+import com.android.systemui.statusbar.notification.collection.EntryAdapter;
+import com.android.systemui.statusbar.notification.collection.EntryAdapterFactoryImpl;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider;
@@ -87,6 +91,7 @@ import com.android.systemui.statusbar.notification.promoted.FakePromotedNotifica
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.ExpandableNotificationRowLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.OnExpandClickListener;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
+import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider;
import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor;
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -101,11 +106,6 @@ import com.android.systemui.util.time.SystemClock;
import com.android.systemui.util.time.SystemClockImpl;
import com.android.systemui.wmshell.BubblesTestActivity;
-import kotlin.coroutines.CoroutineContext;
-
-import kotlinx.coroutines.flow.StateFlowKt;
-import kotlinx.coroutines.test.TestScope;
-
import org.mockito.ArgumentCaptor;
import java.util.List;
@@ -114,6 +114,10 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
+import kotlin.coroutines.CoroutineContext;
+import kotlinx.coroutines.flow.StateFlowKt;
+import kotlinx.coroutines.test.TestScope;
+
/**
* A helper class to create {@link ExpandableNotificationRow} (for both individual and group
* notifications).
@@ -734,7 +738,16 @@ public class NotificationTestHelper {
mBindPipelineEntryListener.onEntryInit(entry);
mBindPipeline.manageRow(entry, row);
+ EntryAdapter entryAdapter = new EntryAdapterFactoryImpl(
+ mock(NotificationActivityStarter.class),
+ mock(MetricsLogger.class),
+ mock(PeopleNotificationIdentifier.class),
+ mock(NotificationIconStyleProvider.class),
+ mock(VisualStabilityCoordinator.class)
+ ).create(entry);
+
row.initialize(
+ entryAdapter,
entry,
mock(RemoteInputViewSubcomponent.Factory.class),
APP_NAME,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
index 14bbd38ece2c..761ed6186afc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
@@ -27,6 +27,7 @@ import android.view.NotificationHeaderView;
import android.view.View;
import android.widget.RemoteViews;
+import androidx.compose.ui.platform.ComposeView;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -35,7 +36,9 @@ import com.android.systemui.statusbar.notification.SourceType;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation;
+import com.android.systemui.statusbar.notification.row.ui.viewmodel.BundleHeaderViewModelImpl;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationHeaderViewWrapper;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import org.junit.Assert;
import org.junit.Before;
@@ -273,6 +276,22 @@ public class NotificationChildrenContainerTest extends SysuiTestCase {
Assert.assertEquals(1f, header.getTopRoundness(), 0.001f);
}
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ public void initBundleHeader_composeview_is_initialized_once() {
+ View currentView = mChildrenContainer.getChildAt(mChildrenContainer.getChildCount() - 1);
+ Assert.assertFalse(currentView instanceof ComposeView);
+
+ BundleHeaderViewModelImpl viewModel = new BundleHeaderViewModelImpl();
+ mChildrenContainer.initBundleHeader(viewModel);
+ currentView = mChildrenContainer.getChildAt(mChildrenContainer.getChildCount() - 1);
+ Assert.assertTrue(currentView instanceof ComposeView);
+
+ mChildrenContainer.initBundleHeader(viewModel);
+ View finalView = mChildrenContainer.getChildAt(mChildrenContainer.getChildCount() - 1);
+ Assert.assertEquals(currentView, finalView);
+ }
+
private NotificationHeaderView createHeaderView(boolean lowPriority) {
Notification notification = mNotificationTestHelper.createNotification();
final Notification.Builder builder = Notification.Builder.recoverBuilder(getContext(),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.kt
index 47967b3f0828..670c195ee5a1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.kt
@@ -43,6 +43,7 @@ import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.commandQueue
import com.android.systemui.statusbar.lockscreenShadeTransitionController
+import com.android.systemui.statusbar.notification.collection.EntryAdapterFactoryImpl
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
import com.android.systemui.statusbar.notification.domain.interactor.notificationAlertsInteractor
@@ -54,6 +55,7 @@ import com.android.systemui.statusbar.notification.interruption.VisualInterrupti
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.row.entryAdapterFactory
import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
import com.android.systemui.statusbar.notification.stack.notificationStackScrollLayoutController
import com.android.systemui.statusbar.notification.visualInterruptionDecisionProvider
@@ -105,10 +107,14 @@ class StatusBarNotificationPresenterTest : SysuiTestCase() {
private val notificationAlertsInteractor = kosmos.notificationAlertsInteractor
private val visualInterruptionDecisionProvider = kosmos.visualInterruptionDecisionProvider
+ private lateinit var factory: EntryAdapterFactoryImpl
+
private lateinit var underTest: StatusBarNotificationPresenter
@Before
fun setup() {
+ factory = kosmos.entryAdapterFactory
+
underTest = createPresenter()
if (VisualInterruptionRefactor.isEnabled) {
verifyAndCaptureSuppressors()
@@ -451,7 +457,7 @@ class StatusBarNotificationPresenterTest : SysuiTestCase() {
private fun createRow(entry: NotificationEntry): ExpandableNotificationRow {
val row: ExpandableNotificationRow = mock()
if (NotificationBundleUi.isEnabled) {
- whenever(row.entryAdapter).thenReturn(entry.entryAdapter)
+ whenever(row.entryAdapter).thenReturn(factory.create(entry))
} else {
whenever(row.entry).thenReturn(entry)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
index 345ddae42798..c23e0e733b41 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
@@ -46,7 +46,7 @@ import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry.NotifEntryAdapter;
+import com.android.systemui.statusbar.notification.collection.NotificationEntryAdapter;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationContentView;
@@ -135,7 +135,7 @@ public class StatusBarRemoteInputCallbackTest extends SysuiTestCase {
final ExpandableNotificationRow enr = mock(ExpandableNotificationRow.class);
final NotificationContentView privateLayout = mock(NotificationContentView.class);
final NotificationEntry enrEntry = mock(NotificationEntry.class);
- final NotifEntryAdapter enrEntryAdapter = mock(NotifEntryAdapter.class);
+ final NotificationEntryAdapter enrEntryAdapter = mock(NotificationEntryAdapter.class);
when(enr.getPrivateLayout()).thenReturn(privateLayout);
when(enr.getEntry()).thenReturn(enrEntry);
@@ -178,7 +178,7 @@ public class StatusBarRemoteInputCallbackTest extends SysuiTestCase {
// THEN
verify(mGroupExpansionManager, never()).toggleGroupExpansion(any(NotificationEntry.class));
- verify(mGroupExpansionManager, never()).toggleGroupExpansion(any(NotifEntryAdapter.class));
+ verify(mGroupExpansionManager, never()).toggleGroupExpansion(any(NotificationEntryAdapter.class));
verify(enr).setUserExpanded(true);
verify(privateLayout).setOnExpandedVisibleListener(onExpandedVisibleRunner);
}
@@ -203,7 +203,7 @@ public class StatusBarRemoteInputCallbackTest extends SysuiTestCase {
// THEN
verify(mGroupExpansionManager, never()).toggleGroupExpansion(any(NotificationEntry.class));
- verify(mGroupExpansionManager, never()).toggleGroupExpansion(any(NotifEntryAdapter.class));
+ verify(mGroupExpansionManager, never()).toggleGroupExpansion(any(NotificationEntryAdapter.class));
verify(enr).setUserExpanded(true);
verify(privateLayout).setOnExpandedVisibleListener(onExpandedVisibleRunner);
}
@@ -217,7 +217,7 @@ public class StatusBarRemoteInputCallbackTest extends SysuiTestCase {
final ExpandableNotificationRow enr = mock(ExpandableNotificationRow.class);
final NotificationContentView privateLayout = mock(NotificationContentView.class);
final NotificationEntry enrEntry = mock(NotificationEntry.class);
- final NotifEntryAdapter enrEntryAdapter = mock(NotifEntryAdapter.class);
+ final NotificationEntryAdapter enrEntryAdapter = mock(NotificationEntryAdapter.class);
when(enr.getPrivateLayout()).thenReturn(privateLayout);
when(enr.getEntry()).thenReturn(enrEntry);
@@ -260,7 +260,7 @@ public class StatusBarRemoteInputCallbackTest extends SysuiTestCase {
// THEN
verify(mGroupExpansionManager, never()).toggleGroupExpansion(enrEntry);
- verify(mGroupExpansionManager, never()).toggleGroupExpansion(any(NotifEntryAdapter.class));
+ verify(mGroupExpansionManager, never()).toggleGroupExpansion(any(NotificationEntryAdapter.class));
verify(enr, never()).setUserExpanded(anyBoolean());
verify(privateLayout, never()).setOnExpandedVisibleListener(any());
}
@@ -290,7 +290,7 @@ public class StatusBarRemoteInputCallbackTest extends SysuiTestCase {
verify(privateLayout).setOnExpandedVisibleListener(onExpandedVisibleRunner);
verify(enr, never()).setUserExpanded(anyBoolean());
verify(mGroupExpansionManager, never()).toggleGroupExpansion(any(NotificationEntry.class));
- verify(mGroupExpansionManager, never()).toggleGroupExpansion(any(NotifEntryAdapter.class));
+ verify(mGroupExpansionManager, never()).toggleGroupExpansion(any(NotificationEntryAdapter.class));
}
@Test
@@ -318,7 +318,7 @@ public class StatusBarRemoteInputCallbackTest extends SysuiTestCase {
verify(privateLayout, never()).setOnExpandedVisibleListener(onExpandedVisibleRunner);
verify(enr, never()).setUserExpanded(anyBoolean());
verify(mGroupExpansionManager, never()).toggleGroupExpansion(any(NotificationEntry.class));
- verify(mGroupExpansionManager, never()).toggleGroupExpansion(any(NotifEntryAdapter.class));
+ verify(mGroupExpansionManager, never()).toggleGroupExpansion(any(NotificationEntryAdapter.class));
}
@Test
@@ -346,7 +346,7 @@ public class StatusBarRemoteInputCallbackTest extends SysuiTestCase {
verify(privateLayout).setOnExpandedVisibleListener(onExpandedVisibleRunner);
verify(enr, never()).setUserExpanded(anyBoolean());
verify(mGroupExpansionManager, never()).toggleGroupExpansion(any(NotificationEntry.class));
- verify(mGroupExpansionManager, never()).toggleGroupExpansion(any(NotifEntryAdapter.class));
+ verify(mGroupExpansionManager, never()).toggleGroupExpansion(any(NotificationEntryAdapter.class));
}
@Test
@@ -374,6 +374,6 @@ public class StatusBarRemoteInputCallbackTest extends SysuiTestCase {
verify(privateLayout, never()).setOnExpandedVisibleListener(onExpandedVisibleRunner);
verify(enr, never()).setUserExpanded(anyBoolean());
verify(mGroupExpansionManager, never()).toggleGroupExpansion(any(NotificationEntry.class));
- verify(mGroupExpansionManager, never()).toggleGroupExpansion(any(NotifEntryAdapter.class));
+ verify(mGroupExpansionManager, never()).toggleGroupExpansion(any(NotificationEntryAdapter.class));
}
}
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 ee5979c6ed9f..12cf3b6cd2cf 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
@@ -1018,7 +1018,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
addOngoingCallState(key = "call")
assertIsScreenRecordChip(latest!!.chips.active[0])
- assertIsCallChip(latest!!.chips.active[1], "call")
+ assertIsCallChip(latest!!.chips.active[1], "call", context)
}
@Test
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockController.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockController.kt
index 4c1f6450dd78..b52db83d513c 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockController.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockController.kt
@@ -42,9 +42,13 @@ interface ClockController {
isDarkTheme: Boolean,
dozeFraction: Float,
foldFraction: Float,
- onBoundsChanged: (RectF) -> Unit,
+ clockListener: ClockEventListener?,
)
/** Optional method for dumping debug information */
fun dump(pw: PrintWriter)
}
+
+interface ClockEventListener {
+ fun onBoundsChanged(bounds: RectF)
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/DisplaySpecific.kt b/packages/SystemUI/pods/com/android/systemui/dagger/qualifiers/DisplaySpecific.kt
index 3e39ae9a3fe5..0c46e0784a31 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/DisplaySpecific.kt
+++ b/packages/SystemUI/pods/com/android/systemui/dagger/qualifiers/DisplaySpecific.kt
@@ -18,4 +18,7 @@ package com.android.systemui.dagger.qualifiers
import javax.inject.Qualifier
/** Annotates a class that is display specific. */
-@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class DisplaySpecific
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+public annotation class DisplaySpecific
diff --git a/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/Main.java b/packages/SystemUI/pods/com/android/systemui/dagger/qualifiers/Main.java
index 7b097740ff11..7b097740ff11 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/Main.java
+++ b/packages/SystemUI/pods/com/android/systemui/dagger/qualifiers/Main.java
diff --git a/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/UiBackground.java b/packages/SystemUI/pods/com/android/systemui/dagger/qualifiers/UiBackground.java
index 3d37468c322a..3d37468c322a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/UiBackground.java
+++ b/packages/SystemUI/pods/com/android/systemui/dagger/qualifiers/UiBackground.java
diff --git a/packages/SystemUI/res/layout/magic_action_button.xml b/packages/SystemUI/res/layout/magic_action_button.xml
index 381d6b17dec5..63fc1e485635 100644
--- a/packages/SystemUI/res/layout/magic_action_button.xml
+++ b/packages/SystemUI/res/layout/magic_action_button.xml
@@ -6,7 +6,7 @@
android:background="@drawable/magic_action_button_background"
android:drawablePadding="@dimen/magic_action_button_drawable_padding"
android:ellipsize="none"
- android:fontFamily="google-sans-flex"
+ android:fontFamily="google-sans-flex-medium"
android:gravity="center"
android:minWidth="0dp"
android:paddingHorizontal="@dimen/magic_action_button_padding_horizontal"
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index f41eaec8e18b..a1fa54cf592d 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -40,7 +40,7 @@
android:layout_marginBottom="@dimen/volume_dialog_components_spacing"
android:clipChildren="false"
app:layout_constraintBottom_toTopOf="@id/volume_dialog_main_slider_container"
- app:layout_constraintEnd_toEndOf="@id/volume_dialog_main_slider_container"
+ app:layout_constraintEnd_toEndOf="@id/volume_dialog_background"
app:layout_constraintHeight_default="spread"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
@@ -70,9 +70,9 @@
android:layout_marginTop="@dimen/volume_dialog_components_spacing"
android:clipChildren="false"
app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="@id/volume_dialog_main_slider_container"
+ app:layout_constraintEnd_toEndOf="@id/volume_dialog_background"
app:layout_constraintHeight_default="wrap"
- app:layout_constraintStart_toStartOf="@id/volume_dialog_main_slider_container"
+ app:layout_constraintStart_toStartOf="@id/volume_dialog_background"
app:layout_constraintTop_toBottomOf="@id/volume_dialog_main_slider_container"
app:layout_constraintVertical_bias="0"
app:layout_constraintWidth_default="wrap">
diff --git a/packages/SystemUI/res/layout/volume_dialog_top_section.xml b/packages/SystemUI/res/layout/volume_dialog_top_section.xml
index 29f52480bfe0..b7455471d9a6 100644
--- a/packages/SystemUI/res/layout/volume_dialog_top_section.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_top_section.xml
@@ -22,7 +22,7 @@
android:clipChildren="false"
android:clipToPadding="false"
android:gravity="center"
- android:paddingEnd="@dimen/volume_dialog_ringer_drawer_diff_end_margin"
+ android:paddingEnd="@dimen/volume_dialog_buttons_margin"
app:layoutDescription="@xml/volume_dialog_ringer_drawer_motion_scene">
<View
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index c8e31079fca0..8342a9cc244b 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -2189,11 +2189,6 @@
<dimen name="volume_dialog_background_square_corner_radius">12dp</dimen>
<dimen name="volume_dialog_ringer_drawer_margin">@dimen/volume_dialog_buttons_margin</dimen>
- <!--
- (volume_dialog_slider_width - volume_dialog_button_size) / 2
- This centers ringer drawer against the volume slider
- -->
- <dimen name="volume_dialog_ringer_drawer_diff_end_margin">6dp</dimen>
<dimen name="volume_dialog_ringer_drawer_button_size">@dimen/volume_dialog_button_size</dimen>
<dimen name="volume_dialog_ringer_drawer_button_icon_radius">10dp</dimen>
<dimen name="volume_dialog_ringer_selected_button_background_radius">20dp</dimen>
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index ae3a76e2d2ca..8d7cab41ea20 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -51,6 +51,7 @@ android_library {
":wm_shell-shared-aidls",
],
static_libs: [
+ "com.android.systemui.dagger-api",
"BiometricsSharedLib",
"PlatformAnimationLib",
"PluginCoreLib",
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/CombinedCondition.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/CombinedCondition.kt
index a2b6e2cf9ae3..f276a82baaef 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/CombinedCondition.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/CombinedCondition.kt
@@ -41,7 +41,7 @@ class CombinedCondition
constructor(
private val scope: CoroutineScope,
private val conditions: Collection<Condition>,
- @Evaluator.ConditionOperand private val operand: Int
+ @Evaluator.ConditionOperand private val operand: Int,
) : Condition(scope, null, false) {
private var job: Job? = null
@@ -54,7 +54,7 @@ constructor(
lazilyEvaluate(
conditions = groupedConditions.getOrDefault(true, emptyList()),
- filterUnknown = true
+ filterUnknown = true,
)
.distinctUntilChanged()
.flatMapLatest { overriddenValue ->
@@ -62,7 +62,7 @@ constructor(
if (overriddenValue == null) {
lazilyEvaluate(
conditions = groupedConditions.getOrDefault(false, emptyList()),
- filterUnknown = false
+ filterUnknown = false,
)
} else {
flowOf(overriddenValue)
@@ -188,7 +188,6 @@ constructor(
return startStrategy
}
- override fun getStartStrategy(): Int {
- return _startStrategy
- }
+ override val startStrategy: Int
+ get() = _startStrategy
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Condition.java b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Condition.java
deleted file mode 100644
index 670feeba6e8a..000000000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Condition.java
+++ /dev/null
@@ -1,306 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.shared.condition;
-
-import android.util.Log;
-
-import androidx.annotation.IntDef;
-import androidx.annotation.NonNull;
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleEventObserver;
-import androidx.lifecycle.LifecycleOwner;
-
-import kotlinx.coroutines.CoroutineScope;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-
-/**
- * Base class for a condition that needs to be fulfilled in order for {@link Monitor} to inform
- * its callbacks.
- */
-public abstract class Condition {
- private final String mTag = getClass().getSimpleName();
-
- private final ArrayList<WeakReference<Callback>> mCallbacks = new ArrayList<>();
- private final boolean mOverriding;
- private final CoroutineScope mScope;
- private Boolean mIsConditionMet;
- private boolean mStarted = false;
-
- /**
- * By default, conditions have an initial value of false and are not overriding.
- */
- public Condition(CoroutineScope scope) {
- this(scope, false, false);
- }
-
- /**
- * Constructor for specifying initial state and overriding condition attribute.
- *
- * @param initialConditionMet Initial state of the condition.
- * @param overriding Whether this condition overrides others.
- */
- protected Condition(CoroutineScope scope, Boolean initialConditionMet, boolean overriding) {
- mIsConditionMet = initialConditionMet;
- mOverriding = overriding;
- mScope = scope;
- }
-
- /**
- * Starts monitoring the condition.
- */
- protected abstract void start();
-
- /**
- * Stops monitoring the condition.
- */
- protected abstract void stop();
-
- /**
- * Condition should be started as soon as there is an active subscription.
- */
- public static final int START_EAGERLY = 0;
- /**
- * Condition should be started lazily only if needed. But once started, it will not be cancelled
- * unless there are no more active subscriptions.
- */
- public static final int START_LAZILY = 1;
- /**
- * Condition should be started lazily only if needed, and can be stopped when not needed. This
- * should be used for conditions which are expensive to keep running.
- */
- public static final int START_WHEN_NEEDED = 2;
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({START_EAGERLY, START_LAZILY, START_WHEN_NEEDED})
- @interface StartStrategy {
- }
-
- @StartStrategy
- protected abstract int getStartStrategy();
-
- /**
- * Returns whether the current condition overrides
- */
- public boolean isOverridingCondition() {
- return mOverriding;
- }
-
- /**
- * Registers a callback to receive updates once started. This should be called before
- * {@link #start()}. Also triggers the callback immediately if already started.
- */
- public void addCallback(@NonNull Callback callback) {
- if (shouldLog()) Log.d(mTag, "adding callback");
- mCallbacks.add(new WeakReference<>(callback));
-
- if (mStarted) {
- callback.onConditionChanged(this);
- return;
- }
-
- start();
- mStarted = true;
- }
-
- /**
- * Removes the provided callback from further receiving updates.
- */
- public void removeCallback(@NonNull Callback callback) {
- if (shouldLog()) Log.d(mTag, "removing callback");
- final Iterator<WeakReference<Callback>> iterator = mCallbacks.iterator();
- while (iterator.hasNext()) {
- final Callback cb = iterator.next().get();
- if (cb == null || cb == callback) {
- iterator.remove();
- }
- }
-
- if (!mCallbacks.isEmpty() || !mStarted) {
- return;
- }
-
- stop();
- mStarted = false;
- }
-
- /**
- * Wrapper to {@link #addCallback(Callback)} when a lifecycle is in the resumed state
- * and {@link #removeCallback(Callback)} when not resumed automatically.
- */
- public Callback observe(LifecycleOwner owner, Callback listener) {
- return observe(owner.getLifecycle(), listener);
- }
-
- /**
- * Wrapper to {@link #addCallback(Callback)} when a lifecycle is in the resumed state
- * and {@link #removeCallback(Condition.Callback)} when not resumed automatically.
- */
- public Callback observe(Lifecycle lifecycle, Callback listener) {
- lifecycle.addObserver((LifecycleEventObserver) (lifecycleOwner, event) -> {
- if (event == Lifecycle.Event.ON_RESUME) {
- addCallback(listener);
- } else if (event == Lifecycle.Event.ON_PAUSE) {
- removeCallback(listener);
- }
- });
- return listener;
- }
-
- /**
- * Updates the value for whether the condition has been fulfilled, and sends an update if the
- * value changes and any callback is registered.
- *
- * @param isConditionMet True if the condition has been fulfilled. False otherwise.
- */
- protected void updateCondition(boolean isConditionMet) {
- if (mIsConditionMet != null && mIsConditionMet == isConditionMet) {
- return;
- }
-
- if (shouldLog()) Log.d(mTag, "updating condition to " + isConditionMet);
- mIsConditionMet = isConditionMet;
- sendUpdate();
- }
-
- /**
- * Clears the set condition value. This is purposefully separate from
- * {@link #updateCondition(boolean)} to avoid confusion around {@code null} values.
- */
- protected void clearCondition() {
- if (mIsConditionMet == null) {
- return;
- }
-
- if (shouldLog()) Log.d(mTag, "clearing condition");
-
- mIsConditionMet = null;
- sendUpdate();
- }
-
- private void sendUpdate() {
- final Iterator<WeakReference<Callback>> iterator = mCallbacks.iterator();
- while (iterator.hasNext()) {
- final Callback cb = iterator.next().get();
- if (cb == null) {
- iterator.remove();
- } else {
- cb.onConditionChanged(this);
- }
- }
- }
-
- /**
- * Returns whether the condition is set. This method should be consulted to understand the
- * value of {@link #isConditionMet()}.
- *
- * @return {@code true} if value is present, {@code false} otherwise.
- */
- public boolean isConditionSet() {
- return mIsConditionMet != null;
- }
-
- /**
- * Returns whether the condition has been met. Note that this method will return {@code false}
- * if the condition is not set as well.
- */
- public boolean isConditionMet() {
- return Boolean.TRUE.equals(mIsConditionMet);
- }
-
- protected final boolean shouldLog() {
- return Log.isLoggable(mTag, Log.DEBUG);
- }
-
- protected final String getTag() {
- if (isOverridingCondition()) {
- return mTag + "[OVRD]";
- }
-
- return mTag;
- }
-
- /**
- * Returns the state of the condition.
- * - "Invalid", condition hasn't been set / not monitored
- * - "True", condition has been met
- * - "False", condition has not been met
- */
- protected final String getState() {
- if (!isConditionSet()) {
- return "Invalid";
- }
- return isConditionMet() ? "True" : "False";
- }
-
- /**
- * Creates a new condition which will only be true when both this condition and all the provided
- * conditions are true.
- */
- public Condition and(@NonNull Collection<Condition> others) {
- final List<Condition> conditions = new ArrayList<>();
- conditions.add(this);
- conditions.addAll(others);
- return new CombinedCondition(mScope, conditions, Evaluator.OP_AND);
- }
-
- /**
- * Creates a new condition which will only be true when both this condition and the provided
- * condition is true.
- */
- public Condition and(@NonNull Condition... others) {
- return and(Arrays.asList(others));
- }
-
- /**
- * Creates a new condition which will only be true when either this condition or any of the
- * provided conditions are true.
- */
- public Condition or(@NonNull Collection<Condition> others) {
- final List<Condition> conditions = new ArrayList<>();
- conditions.add(this);
- conditions.addAll(others);
- return new CombinedCondition(mScope, conditions, Evaluator.OP_OR);
- }
-
- /**
- * Creates a new condition which will only be true when either this condition or the provided
- * condition is true.
- */
- public Condition or(@NonNull Condition... others) {
- return or(Arrays.asList(others));
- }
-
- /**
- * Callback that receives updates about whether the condition has been fulfilled.
- */
- public interface Callback {
- /**
- * Called when the fulfillment of the condition changes.
- *
- * @param condition The condition in question.
- */
- void onConditionChanged(Condition condition);
- }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Condition.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Condition.kt
new file mode 100644
index 000000000000..69236b48ed51
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Condition.kt
@@ -0,0 +1,270 @@
+/*
+ * 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.shared.condition
+
+import android.util.Log
+import androidx.annotation.IntDef
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleEventObserver
+import androidx.lifecycle.LifecycleOwner
+import java.lang.ref.WeakReference
+import kotlinx.coroutines.CoroutineScope
+
+/**
+ * Base class for a condition that needs to be fulfilled in order for [Monitor] to inform its
+ * callbacks.
+ */
+abstract class Condition
+/**
+ * Constructor for specifying initial state and overriding condition attribute.
+ *
+ * @param initialConditionMet Initial state of the condition.
+ * @param overriding Whether this condition overrides others.
+ */
+@JvmOverloads
+protected constructor(
+ private val _scope: CoroutineScope,
+ private var _isConditionMet: Boolean? = false,
+ /** Returns whether the current condition overrides */
+ val isOverridingCondition: Boolean = false,
+) {
+ private val mTag: String = javaClass.simpleName
+
+ private val _callbacks = mutableListOf<WeakReference<Callback>>()
+ private var _started = false
+
+ /** Starts monitoring the condition. */
+ protected abstract fun start()
+
+ /** Stops monitoring the condition. */
+ protected abstract fun stop()
+
+ @Retention(AnnotationRetention.SOURCE)
+ @IntDef(START_EAGERLY, START_LAZILY, START_WHEN_NEEDED)
+ annotation class StartStrategy
+
+ @get:StartStrategy abstract val startStrategy: Int
+
+ /**
+ * Registers a callback to receive updates once started. This should be called before [.start].
+ * Also triggers the callback immediately if already started.
+ */
+ fun addCallback(callback: Callback) {
+ if (shouldLog()) Log.d(mTag, "adding callback")
+ _callbacks.add(WeakReference(callback))
+
+ if (_started) {
+ callback.onConditionChanged(this)
+ return
+ }
+
+ start()
+ _started = true
+ }
+
+ /** Removes the provided callback from further receiving updates. */
+ fun removeCallback(callback: Callback) {
+ if (shouldLog()) Log.d(mTag, "removing callback")
+ val iterator = _callbacks.iterator()
+ while (iterator.hasNext()) {
+ val cb = iterator.next().get()
+ if (cb == null || cb === callback) {
+ iterator.remove()
+ }
+ }
+
+ if (_callbacks.isNotEmpty() || !_started) {
+ return
+ }
+
+ stop()
+ _started = false
+ }
+
+ /**
+ * Wrapper to [.addCallback] when a lifecycle is in the resumed state and [.removeCallback] when
+ * not resumed automatically.
+ */
+ fun observe(owner: LifecycleOwner, listener: Callback): Callback {
+ return observe(owner.lifecycle, listener)
+ }
+
+ /**
+ * Wrapper to [.addCallback] when a lifecycle is in the resumed state and [.removeCallback] when
+ * not resumed automatically.
+ */
+ fun observe(lifecycle: Lifecycle, listener: Callback): Callback {
+ lifecycle.addObserver(
+ LifecycleEventObserver { lifecycleOwner: LifecycleOwner?, event: Lifecycle.Event ->
+ if (event == Lifecycle.Event.ON_RESUME) {
+ addCallback(listener)
+ } else if (event == Lifecycle.Event.ON_PAUSE) {
+ removeCallback(listener)
+ }
+ }
+ )
+ return listener
+ }
+
+ /**
+ * Updates the value for whether the condition has been fulfilled, and sends an update if the
+ * value changes and any callback is registered.
+ *
+ * @param isConditionMet True if the condition has been fulfilled. False otherwise.
+ */
+ protected fun updateCondition(isConditionMet: Boolean) {
+ if (_isConditionMet != null && _isConditionMet == isConditionMet) {
+ return
+ }
+
+ if (shouldLog()) Log.d(mTag, "updating condition to $isConditionMet")
+ _isConditionMet = isConditionMet
+ sendUpdate()
+ }
+
+ /**
+ * Clears the set condition value. This is purposefully separate from [.updateCondition] to
+ * avoid confusion around `null` values.
+ */
+ fun clearCondition() {
+ if (_isConditionMet == null) {
+ return
+ }
+
+ if (shouldLog()) Log.d(mTag, "clearing condition")
+
+ _isConditionMet = null
+ sendUpdate()
+ }
+
+ private fun sendUpdate() {
+ val iterator = _callbacks.iterator()
+ while (iterator.hasNext()) {
+ val cb = iterator.next().get()
+ if (cb == null) {
+ iterator.remove()
+ } else {
+ cb.onConditionChanged(this)
+ }
+ }
+ }
+
+ val isConditionSet: Boolean
+ /**
+ * Returns whether the condition is set. This method should be consulted to understand the
+ * value of [.isConditionMet].
+ *
+ * @return `true` if value is present, `false` otherwise.
+ */
+ get() = _isConditionMet != null
+
+ val isConditionMet: Boolean
+ /**
+ * Returns whether the condition has been met. Note that this method will return `false` if
+ * the condition is not set as well.
+ */
+ get() = true == _isConditionMet
+
+ protected fun shouldLog(): Boolean {
+ return Log.isLoggable(mTag, Log.DEBUG)
+ }
+
+ val tag: String
+ get() {
+ if (isOverridingCondition) {
+ return "$mTag[OVRD]"
+ }
+
+ return mTag
+ }
+
+ val state: String
+ /**
+ * Returns the state of the condition.
+ * - "Invalid", condition hasn't been set / not monitored
+ * - "True", condition has been met
+ * - "False", condition has not been met
+ */
+ get() {
+ if (!isConditionSet) {
+ return "Invalid"
+ }
+ return if (isConditionMet) "True" else "False"
+ }
+
+ /**
+ * Creates a new condition which will only be true when both this condition and all the provided
+ * conditions are true.
+ */
+ fun and(others: Collection<Condition>): Condition {
+ val conditions: List<Condition> = listOf(this, *others.toTypedArray())
+ return CombinedCondition(_scope, conditions, Evaluator.OP_AND)
+ }
+
+ /**
+ * Creates a new condition which will only be true when both this condition and the provided
+ * condition is true.
+ */
+ fun and(vararg others: Condition): Condition {
+ return and(listOf(*others))
+ }
+
+ /**
+ * Creates a new condition which will only be true when either this condition or any of the
+ * provided conditions are true.
+ */
+ fun or(others: Collection<Condition>): Condition {
+ val conditions: MutableList<Condition> = ArrayList()
+ conditions.add(this)
+ conditions.addAll(others)
+ return CombinedCondition(_scope, conditions, Evaluator.OP_OR)
+ }
+
+ /**
+ * Creates a new condition which will only be true when either this condition or the provided
+ * condition is true.
+ */
+ fun or(vararg others: Condition): Condition {
+ return or(listOf(*others))
+ }
+
+ /** Callback that receives updates about whether the condition has been fulfilled. */
+ fun interface Callback {
+ /**
+ * Called when the fulfillment of the condition changes.
+ *
+ * @param condition The condition in question.
+ */
+ fun onConditionChanged(condition: Condition)
+ }
+
+ companion object {
+ /** Condition should be started as soon as there is an active subscription. */
+ const val START_EAGERLY: Int = 0
+
+ /**
+ * Condition should be started lazily only if needed. But once started, it will not be
+ * cancelled unless there are no more active subscriptions.
+ */
+ const val START_LAZILY: Int = 1
+
+ /**
+ * Condition should be started lazily only if needed, and can be stopped when not needed.
+ * This should be used for conditions which are expensive to keep running.
+ */
+ const val START_WHEN_NEEDED: Int = 2
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/ConditionExtensions.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/ConditionExtensions.kt
index 84edc3577007..0f535cdfa5cc 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/ConditionExtensions.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/ConditionExtensions.kt
@@ -14,7 +14,7 @@ import kotlinx.coroutines.launch
fun Flow<Boolean>.toCondition(
scope: CoroutineScope,
@StartStrategy strategy: Int,
- initialValue: Boolean? = null
+ initialValue: Boolean? = null,
): Condition {
return object : Condition(scope, initialValue, false) {
var job: Job? = null
@@ -28,7 +28,8 @@ fun Flow<Boolean>.toCondition(
job = null
}
- override fun getStartStrategy() = strategy
+ override val startStrategy: Int
+ get() = strategy
}
}
@@ -36,13 +37,16 @@ fun Flow<Boolean>.toCondition(
fun Condition.toFlow(): Flow<Boolean?> {
return callbackFlow {
val callback =
- Condition.Callback { condition ->
- if (condition.isConditionSet) {
- trySend(condition.isConditionMet)
- } else {
- trySend(null)
+ object : Condition.Callback {
+ override fun onConditionChanged(condition: Condition) {
+ if (condition.isConditionSet) {
+ trySend(condition.isConditionMet)
+ } else {
+ trySend(null)
+ }
}
}
+
addCallback(callback)
callback.onConditionChanged(this@toFlow)
awaitClose { removeCallback(callback) }
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index e827b2d54a19..1549b699eee6 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -56,6 +56,7 @@ import com.android.systemui.log.core.Logger
import com.android.systemui.modes.shared.ModesUi
import com.android.systemui.plugins.clocks.AlarmData
import com.android.systemui.plugins.clocks.ClockController
+import com.android.systemui.plugins.clocks.ClockEventListener
import com.android.systemui.plugins.clocks.ClockFaceController
import com.android.systemui.plugins.clocks.ClockMessageBuffers
import com.android.systemui.plugins.clocks.ClockTickRate
@@ -148,7 +149,7 @@ constructor(
val clockStr = clock.toString()
loggers.forEach { it.d({ "New Clock: $str1" }) { str1 = clockStr } }
- clock.initialize(isDarkTheme(), dozeAmount.value, 0f, { onClockBoundsChanged.value = it })
+ clock.initialize(isDarkTheme(), dozeAmount.value, 0f, clockListener)
if (!regionSamplingEnabled) {
updateColors()
@@ -312,6 +313,13 @@ constructor(
private var zenData: ZenData? = null
private var alarmData: AlarmData? = null
+ private val clockListener =
+ object : ClockEventListener {
+ override fun onBoundsChanged(bounds: RectF) {
+ onClockBoundsChanged.value = bounds
+ }
+ }
+
private val configListener =
object : ConfigurationController.ConfigurationListener {
override fun onThemeChanged() {
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt
new file mode 100644
index 000000000000..df6c1b18e3e9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.clipboardoverlay
+
+import android.content.ClipData
+import android.content.ClipDescription
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import android.text.TextUtils
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+@SysUISingleton
+class ActionIntentCreator @Inject constructor() : IntentCreator {
+ override fun getTextEditorIntent(context: Context?) =
+ Intent(context, EditTextActivity::class.java).apply {
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ }
+
+ override fun getShareIntent(clipData: ClipData, context: Context?): Intent {
+ val shareIntent = Intent(Intent.ACTION_SEND)
+
+ // From the ACTION_SEND docs:
+ // "If using EXTRA_TEXT, the MIME type should be "text/plain"; otherwise it should be the
+ // MIME type of the data in EXTRA_STREAM"
+ val uri = clipData.getItemAt(0).uri
+ shareIntent.apply {
+ if (uri != null) {
+ // We don't use setData here because some apps interpret this as "to:".
+ setType(clipData.description.getMimeType(0))
+ // Include URI in ClipData also, so that grantPermission picks it up.
+ setClipData(
+ ClipData(
+ ClipDescription("content", arrayOf(clipData.description.getMimeType(0))),
+ ClipData.Item(uri),
+ )
+ )
+ putExtra(Intent.EXTRA_STREAM, uri)
+ addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ } else {
+ putExtra(Intent.EXTRA_TEXT, clipData.getItemAt(0).coerceToText(context).toString())
+ setType("text/plain")
+ }
+ }
+
+ return Intent.createChooser(shareIntent, null)
+ .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
+ .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ }
+
+ override fun getImageEditIntent(uri: Uri?, context: Context): Intent {
+ val editorPackage = context.getString(R.string.config_screenshotEditor)
+ return Intent(Intent.ACTION_EDIT).apply {
+ if (!TextUtils.isEmpty(editorPackage)) {
+ setComponent(ComponentName.unflattenFromString(editorPackage))
+ }
+ setDataAndType(uri, "image/*")
+ addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ putExtra(EXTRA_EDIT_SOURCE, EDIT_SOURCE_CLIPBOARD)
+ }
+ }
+
+ override fun getRemoteCopyIntent(clipData: ClipData?, context: Context): Intent {
+ val remoteCopyPackage = context.getString(R.string.config_remoteCopyPackage)
+ return Intent(REMOTE_COPY_ACTION).apply {
+ if (!TextUtils.isEmpty(remoteCopyPackage)) {
+ setComponent(ComponentName.unflattenFromString(remoteCopyPackage))
+ }
+
+ setClipData(clipData)
+ addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ }
+ }
+
+ companion object {
+ private const val EXTRA_EDIT_SOURCE: String = "edit_source"
+ private const val EDIT_SOURCE_CLIPBOARD: String = "clipboard"
+ private const val REMOTE_COPY_ACTION: String = "android.intent.action.REMOTE_COPY"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index ac747845267c..314b6e7f5a28 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -65,7 +65,6 @@ import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.clipboardoverlay.dagger.ClipboardOverlayModule.OverlayWindowContext;
import com.android.systemui.dagger.qualifiers.Background;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.res.R;
import com.android.systemui.screenshot.TimeoutHandler;
@@ -94,13 +93,13 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
private final ClipboardOverlayWindow mWindow;
private final TimeoutHandler mTimeoutHandler;
private final ClipboardOverlayUtils mClipboardUtils;
- private final FeatureFlags mFeatureFlags;
private final Executor mBgExecutor;
private final ClipboardImageLoader mClipboardImageLoader;
private final ClipboardTransitionExecutor mTransitionExecutor;
private final ClipboardOverlayView mView;
private final ClipboardIndicationProvider mClipboardIndicationProvider;
+ private final IntentCreator mIntentCreator;
private Runnable mOnSessionCompleteListener;
private Runnable mOnRemoteCopyTapped;
@@ -189,13 +188,13 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
BroadcastDispatcher broadcastDispatcher,
BroadcastSender broadcastSender,
TimeoutHandler timeoutHandler,
- FeatureFlags featureFlags,
ClipboardOverlayUtils clipboardUtils,
@Background Executor bgExecutor,
ClipboardImageLoader clipboardImageLoader,
ClipboardTransitionExecutor transitionExecutor,
ClipboardIndicationProvider clipboardIndicationProvider,
- UiEventLogger uiEventLogger) {
+ UiEventLogger uiEventLogger,
+ IntentCreator intentCreator) {
mContext = context;
mBroadcastDispatcher = broadcastDispatcher;
mClipboardImageLoader = clipboardImageLoader;
@@ -203,6 +202,7 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
mClipboardIndicationProvider = clipboardIndicationProvider;
mClipboardLogger = new ClipboardLogger(uiEventLogger);
+ mIntentCreator = intentCreator;
mView = clipboardOverlayView;
mWindow = clipboardOverlayWindow;
@@ -211,7 +211,6 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
hideImmediate();
});
- mFeatureFlags = featureFlags;
mTimeoutHandler = timeoutHandler;
mTimeoutHandler.setDefaultTimeoutMillis(CLIPBOARD_DEFAULT_TIMEOUT_MILLIS);
@@ -508,7 +507,8 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
}
private void maybeShowRemoteCopy(ClipData clipData) {
- Intent remoteCopyIntent = IntentCreator.getRemoteCopyIntent(clipData, mContext);
+ Intent remoteCopyIntent = mIntentCreator.getRemoteCopyIntent(clipData, mContext);
+
// Only show remote copy if it's available.
PackageManager packageManager = mContext.getPackageManager();
if (packageManager.resolveActivity(
@@ -558,19 +558,19 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
private void editImage(Uri uri) {
mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_EDIT_TAPPED);
- mContext.startActivity(IntentCreator.getImageEditIntent(uri, mContext));
+ mContext.startActivity(mIntentCreator.getImageEditIntent(uri, mContext));
animateOut();
}
private void editText() {
mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_EDIT_TAPPED);
- mContext.startActivity(IntentCreator.getTextEditorIntent(mContext));
+ mContext.startActivity(mIntentCreator.getTextEditorIntent(mContext));
animateOut();
}
private void shareContent(ClipData clip) {
mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_SHARE_TAPPED);
- mContext.startActivity(IntentCreator.getShareIntent(clip, mContext));
+ mContext.startActivity(mIntentCreator.getShareIntent(clip, mContext));
animateOut();
}
@@ -717,22 +717,22 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
public void onRemoteCopyButtonTapped() {
if (clipboardSharedTransitions()) {
finish(CLIPBOARD_OVERLAY_REMOTE_COPY_TAPPED,
- IntentCreator.getRemoteCopyIntent(mClipboardModel.getClipData(), mContext));
+ mIntentCreator.getRemoteCopyIntent(mClipboardModel.getClipData(), mContext));
}
}
@Override
public void onShareButtonTapped() {
if (clipboardSharedTransitions()) {
+ Intent shareIntent =
+ mIntentCreator.getShareIntent(mClipboardModel.getClipData(), mContext);
switch (mClipboardModel.getType()) {
case TEXT:
case URI:
- finish(CLIPBOARD_OVERLAY_SHARE_TAPPED,
- IntentCreator.getShareIntent(mClipboardModel.getClipData(), mContext));
+ finish(CLIPBOARD_OVERLAY_SHARE_TAPPED, shareIntent);
break;
case IMAGE:
- finishWithSharedTransition(CLIPBOARD_OVERLAY_SHARE_TAPPED,
- IntentCreator.getShareIntent(mClipboardModel.getClipData(), mContext));
+ finishWithSharedTransition(CLIPBOARD_OVERLAY_SHARE_TAPPED, shareIntent);
break;
}
}
@@ -744,11 +744,11 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
switch (mClipboardModel.getType()) {
case TEXT:
finish(CLIPBOARD_OVERLAY_EDIT_TAPPED,
- IntentCreator.getTextEditorIntent(mContext));
+ mIntentCreator.getTextEditorIntent(mContext));
break;
case IMAGE:
finishWithSharedTransition(CLIPBOARD_OVERLAY_EDIT_TAPPED,
- IntentCreator.getImageEditIntent(mClipboardModel.getUri(), mContext));
+ mIntentCreator.getImageEditIntent(mClipboardModel.getUri(), mContext));
break;
default:
Log.w(TAG, "Got preview tapped callback for non-editable type "
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DefaultIntentCreator.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DefaultIntentCreator.java
new file mode 100644
index 000000000000..4b24536ad28f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DefaultIntentCreator.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.clipboardoverlay;
+
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.text.TextUtils;
+
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.res.R;
+
+import javax.inject.Inject;
+
+@SysUISingleton
+public class DefaultIntentCreator implements IntentCreator {
+ private static final String EXTRA_EDIT_SOURCE = "edit_source";
+ private static final String EDIT_SOURCE_CLIPBOARD = "clipboard";
+ private static final String REMOTE_COPY_ACTION = "android.intent.action.REMOTE_COPY";
+
+ @Inject
+ public DefaultIntentCreator() {}
+
+ public Intent getTextEditorIntent(Context context) {
+ Intent intent = new Intent(context, EditTextActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ return intent;
+ }
+
+ public Intent getShareIntent(ClipData clipData, Context context) {
+ Intent shareIntent = new Intent(Intent.ACTION_SEND);
+
+ // From the ACTION_SEND docs:
+ // "If using EXTRA_TEXT, the MIME type should be "text/plain"; otherwise it should be the
+ // MIME type of the data in EXTRA_STREAM"
+ Uri uri = clipData.getItemAt(0).getUri();
+ if (uri != null) {
+ // We don't use setData here because some apps interpret this as "to:".
+ shareIntent.setType(clipData.getDescription().getMimeType(0));
+ // Include URI in ClipData also, so that grantPermission picks it up.
+ shareIntent.setClipData(new ClipData(
+ new ClipDescription(
+ "content", new String[]{clipData.getDescription().getMimeType(0)}),
+ new ClipData.Item(uri)));
+ shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
+ shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ } else {
+ shareIntent.putExtra(
+ Intent.EXTRA_TEXT, clipData.getItemAt(0).coerceToText(context).toString());
+ shareIntent.setType("text/plain");
+ }
+ Intent chooserIntent = Intent.createChooser(shareIntent, null)
+ .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
+ .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+ return chooserIntent;
+ }
+
+ public Intent getImageEditIntent(Uri uri, Context context) {
+ String editorPackage = context.getString(R.string.config_screenshotEditor);
+ Intent editIntent = new Intent(Intent.ACTION_EDIT);
+ if (!TextUtils.isEmpty(editorPackage)) {
+ editIntent.setComponent(ComponentName.unflattenFromString(editorPackage));
+ }
+ editIntent.setDataAndType(uri, "image/*");
+ editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ editIntent.putExtra(EXTRA_EDIT_SOURCE, EDIT_SOURCE_CLIPBOARD);
+ return editIntent;
+ }
+
+ public Intent getRemoteCopyIntent(ClipData clipData, Context context) {
+ Intent nearbyIntent = new Intent(REMOTE_COPY_ACTION);
+
+ String remoteCopyPackage = context.getString(R.string.config_remoteCopyPackage);
+ if (!TextUtils.isEmpty(remoteCopyPackage)) {
+ nearbyIntent.setComponent(ComponentName.unflattenFromString(remoteCopyPackage));
+ }
+
+ nearbyIntent.setClipData(clipData);
+ nearbyIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ nearbyIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ return nearbyIntent;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java
index a18b4c84b081..c8a6b05f090b 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 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.
@@ -17,79 +17,13 @@
package com.android.systemui.clipboardoverlay;
import android.content.ClipData;
-import android.content.ClipDescription;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
-import android.text.TextUtils;
-import com.android.systemui.res.R;
-
-class IntentCreator {
- private static final String EXTRA_EDIT_SOURCE = "edit_source";
- private static final String EDIT_SOURCE_CLIPBOARD = "clipboard";
- private static final String REMOTE_COPY_ACTION = "android.intent.action.REMOTE_COPY";
-
- static Intent getTextEditorIntent(Context context) {
- Intent intent = new Intent(context, EditTextActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- return intent;
- }
-
- static Intent getShareIntent(ClipData clipData, Context context) {
- Intent shareIntent = new Intent(Intent.ACTION_SEND);
-
- // From the ACTION_SEND docs:
- // "If using EXTRA_TEXT, the MIME type should be "text/plain"; otherwise it should be the
- // MIME type of the data in EXTRA_STREAM"
- Uri uri = clipData.getItemAt(0).getUri();
- if (uri != null) {
- // We don't use setData here because some apps interpret this as "to:".
- shareIntent.setType(clipData.getDescription().getMimeType(0));
- // Include URI in ClipData also, so that grantPermission picks it up.
- shareIntent.setClipData(new ClipData(
- new ClipDescription(
- "content", new String[]{clipData.getDescription().getMimeType(0)}),
- new ClipData.Item(uri)));
- shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
- shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- } else {
- shareIntent.putExtra(
- Intent.EXTRA_TEXT, clipData.getItemAt(0).coerceToText(context).toString());
- shareIntent.setType("text/plain");
- }
- Intent chooserIntent = Intent.createChooser(shareIntent, null)
- .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
- .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
-
- return chooserIntent;
- }
-
- static Intent getImageEditIntent(Uri uri, Context context) {
- String editorPackage = context.getString(R.string.config_screenshotEditor);
- Intent editIntent = new Intent(Intent.ACTION_EDIT);
- if (!TextUtils.isEmpty(editorPackage)) {
- editIntent.setComponent(ComponentName.unflattenFromString(editorPackage));
- }
- editIntent.setDataAndType(uri, "image/*");
- editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- editIntent.putExtra(EXTRA_EDIT_SOURCE, EDIT_SOURCE_CLIPBOARD);
- return editIntent;
- }
-
- static Intent getRemoteCopyIntent(ClipData clipData, Context context) {
- Intent nearbyIntent = new Intent(REMOTE_COPY_ACTION);
-
- String remoteCopyPackage = context.getString(R.string.config_remoteCopyPackage);
- if (!TextUtils.isEmpty(remoteCopyPackage)) {
- nearbyIntent.setComponent(ComponentName.unflattenFromString(remoteCopyPackage));
- }
-
- nearbyIntent.setClipData(clipData);
- nearbyIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- nearbyIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- return nearbyIntent;
- }
+public interface IntentCreator {
+ Intent getTextEditorIntent(Context context);
+ Intent getShareIntent(ClipData clipData, Context context);
+ Intent getImageEditIntent(Uri uri, Context context);
+ Intent getRemoteCopyIntent(ClipData clipData, Context context);
}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java
index 6c10eea07ffc..c86a84b17efe 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java
@@ -20,6 +20,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
import static com.android.systemui.Flags.clipboardOverlayMultiuser;
import static com.android.systemui.Flags.enableViewCaptureTracing;
+import static com.android.systemui.shared.Flags.usePreferredImageEditor;
import static com.android.systemui.util.ConvenienceExtensionsKt.toKotlinLazy;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@@ -32,7 +33,10 @@ import android.view.WindowManager;
import com.android.app.viewcapture.ViewCapture;
import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
+import com.android.systemui.clipboardoverlay.ActionIntentCreator;
import com.android.systemui.clipboardoverlay.ClipboardOverlayView;
+import com.android.systemui.clipboardoverlay.DefaultIntentCreator;
+import com.android.systemui.clipboardoverlay.IntentCreator;
import com.android.systemui.res.R;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.UserTracker;
@@ -102,6 +106,17 @@ public interface ClipboardOverlayModule {
/* isViewCaptureEnabled= */ enableViewCaptureTracing());
}
+ @Provides
+ static IntentCreator provideIntentCreator(
+ Lazy<DefaultIntentCreator> defaultIntentCreator,
+ Lazy<ActionIntentCreator> actionIntentCreator) {
+ if (usePreferredImageEditor()) {
+ return actionIntentCreator.get();
+ } else {
+ return defaultIntentCreator.get();
+ }
+ }
+
@Qualifier
@Documented
@Retention(RUNTIME)
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt b/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt
index ba7e17bdd7a5..07cc136e6bc6 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt
@@ -23,7 +23,6 @@ import androidx.annotation.ColorInt
import androidx.annotation.DimenRes
import androidx.annotation.LayoutRes
import com.android.settingslib.Utils
-import com.android.systemui.statusbar.data.repository.StatusBarConfigurationState
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.onDensityOrFontScaleChanged
import com.android.systemui.statusbar.policy.onThemeChanged
@@ -79,7 +78,7 @@ class ConfigurationStateImpl
constructor(
@Assisted private val configurationController: ConfigurationController,
@Assisted private val context: Context,
-) : ConfigurationState, StatusBarConfigurationState {
+) : ConfigurationState {
private val layoutInflater = LayoutInflater.from(context)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/DeviceInactiveCondition.java b/packages/SystemUI/src/com/android/systemui/communal/DeviceInactiveCondition.java
index e456310febfd..4be9601f4277 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/DeviceInactiveCondition.java
+++ b/packages/SystemUI/src/com/android/systemui/communal/DeviceInactiveCondition.java
@@ -102,7 +102,7 @@ public class DeviceInactiveCondition extends Condition {
}
@Override
- protected int getStartStrategy() {
+ public int getStartStrategy() {
return START_EAGERLY;
}
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepository.kt
index 3458c9549665..d27e33e53dbb 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepository.kt
@@ -126,7 +126,7 @@ constructor(
get() = perDisplayInstances.keys
private suspend fun start() {
- dumpManager.registerDumpable(this)
+ dumpManager.registerNormalDumpable("PerDisplayRepository-${debugName}", this)
displayRepository.displayIds.collectLatest { displayIds ->
val toRemove = perDisplayInstances.keys - displayIds
toRemove.forEach { displayId ->
@@ -184,8 +184,13 @@ constructor(
* Provides an instance of a given class **only** for the default display, even if asked for another
* display.
*
- * This is useful in case of flag refactors: it can be provided instead of an instance of
+ * This is useful in case of **flag refactors**: it can be provided instead of an instance of
* [PerDisplayInstanceRepositoryImpl] when a flag related to multi display refactoring is off.
+ *
+ * Note that this still requires all instances to be provided by a [PerDisplayInstanceProvider]. If
+ * you want to provide an existing instance instead for the default display, either implement it in
+ * a custom [PerDisplayInstanceProvider] (e.g. inject it in the constructor and return it if the
+ * displayId is zero), or use [SingleInstanceRepositoryImpl].
*/
class DefaultDisplayOnlyInstanceRepositoryImpl<T>(
override val debugName: String,
@@ -198,3 +203,18 @@ class DefaultDisplayOnlyInstanceRepositoryImpl<T>(
override fun get(displayId: Int): T? = lazyDefaultDisplayInstance
}
+
+/**
+ * Always returns [instance] for any display.
+ *
+ * This can be used to provide a single instance based on a flag value during a refactor. Similar to
+ * [DefaultDisplayOnlyInstanceRepositoryImpl], but also avoids creating the
+ * [PerDisplayInstanceProvider]. This is useful when you want to provide an existing instance only,
+ * without even instantiating a [PerDisplayInstanceProvider].
+ */
+class SingleInstanceRepositoryImpl<T>(override val debugName: String, private val instance: T) :
+ PerDisplayRepository<T> {
+ override val displayIds: Set<Int> = setOf(Display.DEFAULT_DISPLAY)
+
+ override fun get(displayId: Int): T? = instance
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/conditions/AssistantAttentionCondition.java b/packages/SystemUI/src/com/android/systemui/dreams/conditions/AssistantAttentionCondition.java
index d81949d08a66..c17094b7456c 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/conditions/AssistantAttentionCondition.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/conditions/AssistantAttentionCondition.java
@@ -63,7 +63,7 @@ public class AssistantAttentionCondition extends Condition {
}
@Override
- protected int getStartStrategy() {
+ public int getStartStrategy() {
return START_EAGERLY;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java b/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java
index c7fe1e1e754b..fb4ed1419688 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java
@@ -63,7 +63,7 @@ public class DreamCondition extends Condition {
}
@Override
- protected int getStartStrategy() {
+ public int getStartStrategy() {
return START_EAGERLY;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/RefactorFlagUtils.kt b/packages/SystemUI/src/com/android/systemui/flags/RefactorFlagUtils.kt
index ca157afb7721..293461daef7b 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/RefactorFlagUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/RefactorFlagUtils.kt
@@ -91,6 +91,7 @@ object RefactorFlagUtils {
* }
* ````
*/
+ @Deprecated("Avoid crashing.", ReplaceWith("if (this.isUnexpectedlyInLegacyMode()) return"))
inline fun unsafeAssertInNewMode(isEnabled: Boolean, flagName: Any) =
check(isEnabled) { "New code path not supported when $flagName is disabled." }
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/DirectBootCondition.kt b/packages/SystemUI/src/com/android/systemui/lowlightclock/DirectBootCondition.kt
index 4c1da0198498..57d709835b2c 100644
--- a/packages/SystemUI/src/com/android/systemui/lowlightclock/DirectBootCondition.kt
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/DirectBootCondition.kt
@@ -54,7 +54,6 @@ constructor(
job?.cancel()
}
- override fun getStartStrategy(): Int {
- return START_EAGERLY
- }
+ override val startStrategy: Int
+ get() = START_EAGERLY
}
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/ForceLowLightCondition.java b/packages/SystemUI/src/com/android/systemui/lowlightclock/ForceLowLightCondition.java
index 7f21d0707f63..5ec81a9a94a1 100644
--- a/packages/SystemUI/src/com/android/systemui/lowlightclock/ForceLowLightCondition.java
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/ForceLowLightCondition.java
@@ -131,7 +131,7 @@ public class ForceLowLightCondition extends Condition {
}
@Override
- protected int getStartStrategy() {
+ public int getStartStrategy() {
return START_EAGERLY;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightCondition.java b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightCondition.java
index e91be5028777..c1a24e7e020e 100644
--- a/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightCondition.java
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightCondition.java
@@ -54,7 +54,7 @@ public class LowLightCondition extends Condition {
}
@Override
- protected int getStartStrategy() {
+ public int getStartStrategy() {
// As this condition keeps the lowlight sensor active, it should only run when needed.
return START_WHEN_NEEDED;
}
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/ScreenSaverEnabledCondition.java b/packages/SystemUI/src/com/android/systemui/lowlightclock/ScreenSaverEnabledCondition.java
index fd6ce1762a28..81572554cb86 100644
--- a/packages/SystemUI/src/com/android/systemui/lowlightclock/ScreenSaverEnabledCondition.java
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/ScreenSaverEnabledCondition.java
@@ -70,7 +70,7 @@ public class ScreenSaverEnabledCondition extends Condition {
}
@Override
- protected int getStartStrategy() {
+ public int getStartStrategy() {
return START_EAGERLY;
}
diff --git a/packages/SystemUI/src/com/android/systemui/modes/shared/ModesUi.kt b/packages/SystemUI/src/com/android/systemui/modes/shared/ModesUi.kt
index a344a5cce6ba..3f804f768d8d 100644
--- a/packages/SystemUI/src/com/android/systemui/modes/shared/ModesUi.kt
+++ b/packages/SystemUI/src/com/android/systemui/modes/shared/ModesUi.kt
@@ -42,7 +42,9 @@ object ModesUi {
* Caution!! Using this check incorrectly will cause crashes in nextfood builds!
*/
@JvmStatic
- inline fun unsafeAssertInNewMode() = RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, Flags.FLAG_MODES_UI)
+ @Deprecated("Avoid crashing.", ReplaceWith("if (this.isUnexpectedlyInLegacyMode()) return"))
+ inline fun unsafeAssertInNewMode() =
+ RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, Flags.FLAG_MODES_UI)
/**
* Called to ensure code is only run when the flag is disabled. This will throw an exception if
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index c4d847f18269..efed260b4c99 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -44,7 +44,6 @@ import android.app.StatusBarManager.WindowVisibleState;
import android.content.Context;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
-import android.inputmethodservice.InputMethodService;
import android.inputmethodservice.InputMethodService.BackDispositionMode;
import android.inputmethodservice.InputMethodService.ImeWindowVisibility;
import android.os.Handler;
@@ -503,9 +502,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
@Override
public void setImeWindowStatus(int displayId, @ImeWindowVisibility int vis,
@BackDispositionMode int backDisposition, boolean showImeSwitcher) {
- // Count imperceptible changes as visible so we transition taskbar out quickly.
- final boolean isImeVisible = mNavBarHelper.isImeVisible(vis)
- || (vis & InputMethodService.IME_VISIBLE_IMPERCEPTIBLE) != 0;
+ final boolean isImeVisible = mNavBarHelper.isImeVisible(vis);
final int flags = Utilities.updateNavbarFlagsFromIme(mNavbarFlags, backDisposition,
isImeVisible, showImeSwitcher);
if (flags == mNavbarFlags) {
diff --git a/packages/SystemUI/src/com/android/systemui/process/condition/SystemProcessCondition.java b/packages/SystemUI/src/com/android/systemui/process/condition/SystemProcessCondition.java
index 694b525e7bc1..ea2bf6a44884 100644
--- a/packages/SystemUI/src/com/android/systemui/process/condition/SystemProcessCondition.java
+++ b/packages/SystemUI/src/com/android/systemui/process/condition/SystemProcessCondition.java
@@ -48,7 +48,7 @@ public class SystemProcessCondition extends Condition {
}
@Override
- protected int getStartStrategy() {
+ public int getStartStrategy() {
return START_EAGERLY;
}
}
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 9aedf0d1ee31..f1f5b267f9c1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
@@ -743,7 +743,13 @@ constructor(
BrightnessSliderContainer(
viewModel = containerViewModel.brightnessSliderViewModel,
modifier =
- Modifier.systemGestureExclusionInShade().fillMaxWidth(),
+ Modifier.systemGestureExclusionInShade(
+ enabled = {
+ layoutState.transitionState is
+ TransitionState.Idle
+ }
+ )
+ .fillMaxWidth(),
)
}
val TileGrid =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/flags/QsDetailedView.kt b/packages/SystemUI/src/com/android/systemui/qs/flags/QsDetailedView.kt
index 2eba36a25ae7..6865e0ee79f6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/flags/QsDetailedView.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/flags/QsDetailedView.kt
@@ -81,7 +81,9 @@ object QsDetailedView {
* Caution!! Using this check incorrectly will cause crashes in nextfood builds!
*/
@JvmStatic
- inline fun unsafeAssertInNewMode() = RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
+ @Deprecated("Avoid crashing.", ReplaceWith("if (this.isUnexpectedlyInLegacyMode()) return"))
+ inline fun unsafeAssertInNewMode() =
+ RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
/** Returns a developer-readable string that describes the current requirement list. */
@JvmStatic
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
index feb4d41e49b6..865ae9a37d87 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
@@ -42,6 +42,7 @@ import androidx.compose.ui.res.integerResource
import androidx.compose.ui.unit.dp
import com.android.compose.animation.scene.ContentScope
import com.android.compose.modifiers.padding
+import com.android.systemui.common.ui.compose.PagerDots
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.development.ui.compose.BuildNumber
import com.android.systemui.development.ui.viewmodel.BuildNumberViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/recents/LauncherProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/LauncherProxyService.java
index 7a6426c741a6..4be35f147c2f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/LauncherProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/LauncherProxyService.java
@@ -830,7 +830,8 @@ public class LauncherProxyService implements CallbackController<LauncherProxyLis
private void notifySystemUiStateFlags(@SystemUiStateFlags long flags, int displayId) {
if (SysUiState.DEBUG) {
Log.d(TAG_OPS, "Notifying sysui state change to launcher service: proxy="
- + mLauncherProxy + " flags=" + flags + " displayId=" + displayId);
+ + mLauncherProxy + " display=" + displayId + " flags="
+ + QuickStepContract.getSystemUiStateString(flags) + " displayId=" + displayId);
}
try {
if (mLauncherProxy != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt
index b0fb6193ee45..634323c1fe2e 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt
@@ -83,7 +83,9 @@ object SceneContainerFlag {
* testing.
*/
@JvmStatic
- inline fun unsafeAssertInNewMode() = RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, DESCRIPTION)
+ @Deprecated("Avoid crashing.", ReplaceWith("if (this.isUnexpectedlyInLegacyMode()) return"))
+ inline fun unsafeAssertInNewMode() =
+ RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, DESCRIPTION)
/** Returns a developer-readable string that describes the current requirement list. */
@JvmStatic
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 17fb50aa6890..24e7976011f4 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -140,6 +140,7 @@ import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.settings.brightness.data.repository.BrightnessMirrorShowingRepository;
import com.android.systemui.settings.brightness.domain.interactor.BrightnessMirrorShowingInteractor;
import com.android.systemui.shade.data.repository.FlingInfo;
+import com.android.systemui.shade.data.repository.ShadeDisplaysRepository;
import com.android.systemui.shade.data.repository.ShadeRepository;
import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor;
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround;
@@ -204,6 +205,7 @@ import dalvik.annotation.optimization.NeverCompile;
import com.google.android.msdl.data.model.MSDLToken;
import com.google.android.msdl.domain.MSDLPlayer;
+import dagger.Lazy;
import kotlin.Unit;
import kotlinx.coroutines.CoroutineDispatcher;
@@ -394,7 +396,7 @@ public final class NotificationPanelViewController implements
/** Whether the notifications are displayed full width (no margins on the side). */
private boolean mIsFullWidth;
private boolean mBlockingExpansionForCurrentTouch;
- // Following variables maintain state of events when input focus transfer may occur.
+ // Following variables maintain state of events when input focus transfer may occur.
private boolean mExpectingSynthesizedDown;
private boolean mLastEventSynthesizedDown;
@@ -456,6 +458,7 @@ public final class NotificationPanelViewController implements
@Deprecated // Use SysUIStateInteractor instead
private final SysUiState mSysUiState;
private final SysUIStateDisplaysInteractor mSysUIStateDisplaysInteractor;
+ private final Lazy<ShadeDisplaysRepository> mShadeDisplaysRepository;
private final NotificationShadeDepthController mDepthController;
private final NavigationBarController mNavigationBarController;
private final int mDisplayId;
@@ -637,7 +640,8 @@ public final class NotificationPanelViewController implements
KeyguardClockPositionAlgorithm keyguardClockPositionAlgorithm,
MSDLPlayer msdlPlayer,
BrightnessMirrorShowingRepository brightnessMirrorShowingRepository,
- BlurConfig blurConfig) {
+ BlurConfig blurConfig,
+ Lazy<ShadeDisplaysRepository> shadeDisplaysRepository) {
mBlurConfig = blurConfig;
SceneContainerFlag.assertInLegacyMode();
keyguardStateController.addCallback(new KeyguardStateController.Callback() {
@@ -745,6 +749,7 @@ public final class NotificationPanelViewController implements
mTapAgainViewController = tapAgainViewController;
mSysUiState = sysUiState;
mSysUIStateDisplaysInteractor = sysUIStateDisplaysInteractor;
+ mShadeDisplaysRepository = shadeDisplaysRepository;
mKeyguardBypassController = bypassController;
mUpdateMonitor = keyguardUpdateMonitor;
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
@@ -2716,8 +2721,17 @@ public final class NotificationPanelViewController implements
}
private int getShadeDisplayId() {
- if (mView != null && mView.getDisplay() != null) return mView.getDisplay().getDisplayId();
- return Display.DEFAULT_DISPLAY;
+ if (ShadeWindowGoesAround.isEnabled()) {
+ var pendingDisplayId =
+ mShadeDisplaysRepository.get().getPendingDisplayId().getValue();
+ // Use the pendingDisplayId from the repository, *not* the Shade's context.
+ // This ensures correct UI state updates also if this method is called just *before*
+ // the Shade window moves to another display.
+ // The pendingDisplayId is guaranteed to be updated before this method is called.
+ return pendingDisplayId;
+ } else {
+ return Display.DEFAULT_DISPLAY;
+ }
}
private void setPerDisplaySysUIStateFlags() {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpandsOnStatusBarLongPress.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpandsOnStatusBarLongPress.kt
index 79e63330e3a5..63b618f72921 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpandsOnStatusBarLongPress.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpandsOnStatusBarLongPress.kt
@@ -50,7 +50,9 @@ object ShadeExpandsOnStatusBarLongPress {
* Caution!! Using this check incorrectly will cause crashes in nextfood builds!
*/
@JvmStatic
- inline fun unsafeAssertInNewMode() = RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
+ @Deprecated("Avoid crashing.", ReplaceWith("if (this.isUnexpectedlyInLegacyMode()) return"))
+ inline fun unsafeAssertInNewMode() =
+ RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
/**
* Called to ensure code is only run when the flag is disabled. This will throw an exception if
diff --git a/packages/SystemUI/src/com/android/systemui/shade/shared/flag/ShadeWindowGoesAround.kt b/packages/SystemUI/src/com/android/systemui/shade/shared/flag/ShadeWindowGoesAround.kt
index 016f50f471de..81824f69eddd 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/shared/flag/ShadeWindowGoesAround.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/shared/flag/ShadeWindowGoesAround.kt
@@ -67,7 +67,9 @@ object ShadeWindowGoesAround {
* Caution!! Using this check incorrectly will cause crashes in nextfood builds!
*/
@JvmStatic
- inline fun unsafeAssertInNewMode() = RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
+ @Deprecated("Avoid crashing.", ReplaceWith("if (this.isUnexpectedlyInLegacyMode()) return"))
+ inline fun unsafeAssertInNewMode() =
+ RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
/**
* Called to ensure code is only run when the flag is disabled. This will throw an exception if
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 3db004848d22..bdfdb8191356 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -60,6 +60,8 @@ import java.util.Optional
import javax.inject.Inject
import kotlin.math.max
import kotlin.math.sign
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
/**
* Responsible for blurring the notification shade window, and applying a zoom effect to the
@@ -92,6 +94,7 @@ constructor(
private const val MIN_VELOCITY = -MAX_VELOCITY
private const val INTERACTION_BLUR_FRACTION = 0.8f
private const val ANIMATION_BLUR_FRACTION = 1f - INTERACTION_BLUR_FRACTION
+ private const val TRANSITION_THRESHOLD = 0.98f
private const val TAG = "DepthController"
}
@@ -156,6 +159,9 @@ constructor(
/**
* When launching an app from the shade, the animations progress should affect how blurry the
* shade is, overriding the expansion amount.
+ *
+ * TODO(b/399617511): remove this once [Flags.notificationShadeBlur] is launched and the Shade
+ * closing is actually instantaneous.
*/
var blursDisabledForAppLaunch: Boolean = false
set(value) {
@@ -185,8 +191,12 @@ constructor(
return
}
- shadeAnimation.animateTo(0)
- shadeAnimation.finishIfRunning()
+ if (Flags.notificationShadeBlur()) {
+ shadeAnimation.skipTo(0)
+ } else {
+ shadeAnimation.animateTo(0)
+ shadeAnimation.finishIfRunning()
+ }
}
@Deprecated(
message =
@@ -487,6 +497,22 @@ constructor(
scheduleUpdate()
}
+ fun onTransitionAnimationProgress(progress: Float) {
+ if (!Flags.notificationShadeBlur() || !Flags.moveTransitionAnimationLayer()) return
+ // Because the Shade takes a few frames to actually trigger the unblur after a transition
+ // has ended, we need to disable it manually, or the opening window itself will be blurred
+ // for a few frames due to relative ordering. We do this towards the end, so that the
+ // window is already covering the background and the unblur is not visible.
+ if (progress >= TRANSITION_THRESHOLD && shadeAnimation.radius > 0) {
+ blursDisabledForAppLaunch = true
+ }
+ }
+
+ fun onTransitionAnimationEnd() {
+ if (!Flags.notificationShadeBlur() || !Flags.moveTransitionAnimationLayer()) return
+ blursDisabledForAppLaunch = false
+ }
+
private fun updateShadeAnimationBlur(
expansion: Float,
tracking: Boolean,
@@ -631,6 +657,20 @@ constructor(
springAnimation.addEndListener { _, _, _, _ -> pendingRadius = -1 }
}
+ /**
+ * Starts an animation to [newRadius], or updates the current one if already ongoing.
+ * IMPORTANT: do NOT use this method + [finishIfRunning] to instantaneously change the value
+ * of the animation. The change will NOT be instantaneous. Use [skipTo] instead.
+ *
+ * Explanation:
+ * 1. If idle, [SpringAnimation.animateToFinalPosition] requests a start to the animation.
+ * 2. On the first frame after an idle animation is requested to start, the animation simply
+ * acquires the starting value and does nothing else.
+ * 3. [SpringAnimation.skipToEnd] requests a fast-forward to the end value, but this happens
+ * during calculation of the next animation value. Because on the first frame no such
+ * calculation happens (point #2), there is one lagging frame where we still see the old
+ * value.
+ */
fun animateTo(newRadius: Int) {
if (pendingRadius == newRadius) {
return
@@ -639,6 +679,19 @@ constructor(
springAnimation.animateToFinalPosition(newRadius.toFloat())
}
+ /**
+ * Instantaneously set a new blur radius to this animation. Always use this instead of
+ * [animateTo] and [finishIfRunning] to make sure that the change takes effect in the next
+ * frame. See the doc for [animateTo] for an explanation.
+ */
+ fun skipTo(newRadius: Int) {
+ if (pendingRadius == newRadius) return
+ pendingRadius = newRadius
+ springAnimation.cancel()
+ springAnimation.setStartValue(newRadius.toFloat())
+ springAnimation.animateToFinalPosition(newRadius.toFloat())
+ }
+
fun finishIfRunning() {
if (springAnimation.isRunning) {
springAnimation.skipToEnd()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt
index d8c3e2546a8f..a0de879845d3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt
@@ -56,7 +56,7 @@ constructor(
private val logger = Logger(logBuffer, "Notif".pad())
// [StatusBarChipLogTag] recommends a max tag length of 20, so [extraLogTag] should NOT be the
// top-level tag. It should instead be provided as the first string in each log message.
- private val extraLogTag = "SingleChipInteractor[key=$key]"
+ private val extraLogTag = "SingleNotifChipInteractor[key=$key][id=${hashCode()}]"
init {
if (startingModel.promotedContent == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt
index 35e622ba8e44..9380dfe32bf5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt
@@ -38,6 +38,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -45,6 +46,7 @@ import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.stateIn
/** An interactor for the notification chips shown in the status bar. */
@SysUISingleton
@@ -147,28 +149,42 @@ constructor(
*/
val allNotificationChips: Flow<List<NotificationChipModel>> =
if (StatusBarNotifChips.isEnabled) {
- // For all our current interactors...
- // TODO(b/364653005): When a promoted notification is added or removed, each individual
- // interactor's [notificationChip] flow becomes un-collected then re-collected, which
- // can cause some flows to remove then add callbacks when they don't need to. Is there a
- // better structure for this? Maybe Channels or a StateFlow with a short timeout?
- promotedNotificationInteractors.flatMapLatest { interactors ->
- if (interactors.isNotEmpty()) {
- // Combine each interactor's [notificationChip] flow...
- val allNotificationChips: List<Flow<NotificationChipModel?>> =
- interactors.map { interactor -> interactor.notificationChip }
- combine(allNotificationChips) {
+ // For all our current interactors...
+ promotedNotificationInteractors.flatMapLatest { interactors ->
+ if (interactors.isNotEmpty()) {
+ // Combine each interactor's [notificationChip] flow...
+ val allNotificationChips: List<Flow<NotificationChipModel?>> =
+ interactors.map { interactor -> interactor.notificationChip }
+ combine(allNotificationChips) {
// ... and emit just the non-null & sorted chips
it.filterNotNull().sortedWith(chipComparator)
}
- .logSort()
- } else {
- flowOf(emptyList())
+ } else {
+ flowOf(emptyList())
+ }
}
+ } else {
+ flowOf(emptyList())
}
- } else {
- flowOf(emptyList())
- }
+ .distinctUntilChanged()
+ .logSort()
+ .stateIn(
+ backgroundScope,
+ SharingStarted.WhileSubscribed(
+ // When a promoted notification is added or removed, the `.flatMapLatest` above
+ // will stop collection and then re-start collection on each individual
+ // interactor's flow. (It will happen even for a chip that didn't change.) We
+ // don't want the individual interactor flows to stop then re-start because they
+ // may be maintaining values that would get thrown away when collection stops
+ // (like an app's last visible time).
+ // stopTimeoutMillis ensures we maintain those values even during the brief
+ // moment (1-2ms) when `.flatMapLatest` causes collect to stop then immediately
+ // restart.
+ // Note: Channels could also work to solve this.
+ stopTimeoutMillis = 1000
+ ),
+ initialValue = emptyList(),
+ )
/** Emits the notifications that should actually be *shown* as chips in the status bar. */
val shownNotificationChips: Flow<List<NotificationChipModel>> =
@@ -199,14 +215,14 @@ constructor(
}
private fun Flow<List<NotificationChipModel>>.logSort(): Flow<List<NotificationChipModel>> {
- return this.distinctUntilChanged().onEach { chips ->
+ return this.onEach { chips ->
val logString =
chips.joinToString {
"{key=${it.key}. " +
"lastVisibleAppTime=${it.lastAppVisibleTime}. " +
"creationTime=${it.creationTime}}"
}
- logger.d({ "Sorted chips: $str1" }) { str1 = logString }
+ logger.d({ "Sorted notif chips: $str1" }) { str1 = logString }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/shared/StatusBarNotifChips.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/shared/StatusBarNotifChips.kt
index 947e93c8a432..6431f303089f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/shared/StatusBarNotifChips.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/shared/StatusBarNotifChips.kt
@@ -50,7 +50,9 @@ object StatusBarNotifChips {
* Caution!! Using this check incorrectly will cause crashes in nextfood builds!
*/
@JvmStatic
- inline fun unsafeAssertInNewMode() = RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
+ @Deprecated("Avoid crashing.", ReplaceWith("if (this.isUnexpectedlyInLegacyMode()) return"))
+ inline fun unsafeAssertInNewMode() =
+ RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
/**
* Called to ensure code is only run when the flag is disabled. This will throw an exception if
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
index e8ab39692558..d81ea07cae2d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
@@ -75,7 +75,12 @@ fun OngoingActivityChip(
}
}
is OngoingActivityChipModel.ClickBehavior.ShowHeadsUpNotification -> {
- ChipBody(model, iconViewStore, onClick = { clickBehavior.onClick() })
+ ChipBody(
+ model,
+ iconViewStore,
+ onClick = { clickBehavior.onClick() },
+ modifier = modifier,
+ )
}
is OngoingActivityChipModel.ClickBehavior.None -> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChips.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChips.kt
index 3b8c0f48e40e..7080c3402b08 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChips.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChips.kt
@@ -25,6 +25,7 @@ import androidx.compose.runtime.key
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
+import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.res.R
import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder
@@ -47,7 +48,13 @@ fun OngoingActivityChips(
chips.active
.filter { !it.isHidden }
.forEach {
- key(it.key) { OngoingActivityChip(model = it, iconViewStore = iconViewStore) }
+ key(it.key) {
+ OngoingActivityChip(
+ model = it,
+ iconViewStore = iconViewStore,
+ modifier = Modifier.sysuiResTag(it.key),
+ )
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/NewStatusBarIcons.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/NewStatusBarIcons.kt
index 9e1686ae135f..03316d3e326c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/NewStatusBarIcons.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/NewStatusBarIcons.kt
@@ -49,7 +49,9 @@ object NewStatusBarIcons {
* Caution!! Using this check incorrectly will cause crashes in nextfood builds!
*/
@JvmStatic
- inline fun unsafeAssertInNewMode() = RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
+ @Deprecated("Avoid crashing.", ReplaceWith("if (this.isUnexpectedlyInLegacyMode()) return"))
+ inline fun unsafeAssertInNewMode() =
+ RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
/**
* Called to ensure code is only run when the flag is disabled. This will throw an exception if
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarConnectedDisplays.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarConnectedDisplays.kt
index cb1827d57c2a..a58e00c5ee0a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarConnectedDisplays.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarConnectedDisplays.kt
@@ -50,7 +50,9 @@ object StatusBarConnectedDisplays {
* Caution!! Using this check incorrectly will cause crashes in nextfood builds!
*/
@JvmStatic
- inline fun unsafeAssertInNewMode() = RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
+ @Deprecated("Avoid crashing.", ReplaceWith("if (this.isUnexpectedlyInLegacyMode()) return"))
+ inline fun unsafeAssertInNewMode() =
+ RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
/**
* Called to ensure code is only run when the flag is disabled. This will throw an exception if
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarRootModernization.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarRootModernization.kt
index 4ee49d82b2fd..25dace32261e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarRootModernization.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarRootModernization.kt
@@ -52,7 +52,9 @@ object StatusBarRootModernization {
* Caution!! Using this check incorrectly will cause crashes in nextfood builds!
*/
@JvmStatic
- inline fun unsafeAssertInNewMode() = RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
+ @Deprecated("Avoid crashing.", ReplaceWith("if (this.isUnexpectedlyInLegacyMode()) return"))
+ inline fun unsafeAssertInNewMode() =
+ RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
/**
* Called to ensure code is only run when the flag is disabled. This will throw an exception if
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt
index 5e7e1793ab35..9db2f4b46dae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt
@@ -20,9 +20,9 @@ import com.android.systemui.statusbar.data.repository.KeyguardStatusBarRepositor
import com.android.systemui.statusbar.data.repository.LightBarControllerStoreModule
import com.android.systemui.statusbar.data.repository.RemoteInputRepositoryModule
import com.android.systemui.statusbar.data.repository.StatusBarConfigurationControllerModule
-import com.android.systemui.statusbar.data.repository.StatusBarConfigurationStateModule
import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStoreModule
import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryModule
+import com.android.systemui.statusbar.data.repository.StatusBarPerDisplayConfigurationStateModule
import com.android.systemui.statusbar.data.repository.SystemEventChipAnimationControllerStoreModule
import com.android.systemui.statusbar.phone.data.StatusBarPhoneDataLayerModule
import dagger.Module
@@ -35,7 +35,7 @@ import dagger.Module
LightBarControllerStoreModule::class,
RemoteInputRepositoryModule::class,
StatusBarConfigurationControllerModule::class,
- StatusBarConfigurationStateModule::class,
+ StatusBarPerDisplayConfigurationStateModule::class,
StatusBarContentInsetsProviderStoreModule::class,
StatusBarModeRepositoryModule::class,
StatusBarPhoneDataLayerModule::class,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarConfigurationStateStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarConfigurationStateStore.kt
deleted file mode 100644
index 4e6c5ddc3066..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarConfigurationStateStore.kt
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2025 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.data.repository
-
-import android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR
-import com.android.systemui.CoreStartable
-import com.android.systemui.common.ui.ConfigurationState
-import com.android.systemui.common.ui.ConfigurationStateImpl
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.display.data.repository.DisplayRepository
-import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepository
-import com.android.systemui.display.data.repository.PerDisplayStore
-import com.android.systemui.display.data.repository.PerDisplayStoreImpl
-import com.android.systemui.display.data.repository.SingleDisplayStore
-import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
-import dagger.Lazy
-import dagger.Module
-import dagger.Provides
-import dagger.multibindings.ClassKey
-import dagger.multibindings.IntoMap
-import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
-
-/** Status bar specific interface to disambiguate from the global [ConfigurationState]. */
-interface StatusBarConfigurationState : ConfigurationState
-
-/** Provides per display instances of [ConfigurationState], specifically for the Status Bar. */
-interface StatusBarConfigurationStateStore : PerDisplayStore<StatusBarConfigurationState>
-
-@SysUISingleton
-class MultiDisplayStatusBarConfigurationStateStore
-@Inject
-constructor(
- @Background backgroundApplicationScope: CoroutineScope,
- displayRepository: DisplayRepository,
- private val displayWindowPropertiesRepository: DisplayWindowPropertiesRepository,
- private val statusBarConfigurationControllerStore: StatusBarConfigurationControllerStore,
- private val factory: ConfigurationStateImpl.Factory,
-) :
- StatusBarConfigurationStateStore,
- PerDisplayStoreImpl<StatusBarConfigurationState>(
- backgroundApplicationScope,
- displayRepository,
- ) {
-
- init {
- StatusBarConnectedDisplays.unsafeAssertInNewMode()
- }
-
- override fun createInstanceForDisplay(displayId: Int): StatusBarConfigurationState? {
- val displayWindowProperties =
- displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR) ?: return null
- val configController =
- statusBarConfigurationControllerStore.forDisplay(displayId) ?: return null
- return factory.create(displayWindowProperties.context, configController)
- }
-
- override val instanceClass = StatusBarConfigurationState::class.java
-}
-
-@SysUISingleton
-class SingleDisplayStatusBarConfigurationStateStore
-@Inject
-constructor(@Main globalConfigState: ConfigurationState) :
- StatusBarConfigurationStateStore,
- PerDisplayStore<StatusBarConfigurationState> by SingleDisplayStore(
- globalConfigState as StatusBarConfigurationState
- ) {
-
- init {
- StatusBarConnectedDisplays.assertInLegacyMode()
- }
-}
-
-@Module
-object StatusBarConfigurationStateModule {
-
- @Provides
- @SysUISingleton
- fun store(
- singleDisplayLazy: Lazy<SingleDisplayStatusBarConfigurationStateStore>,
- multiDisplayLazy: Lazy<MultiDisplayStatusBarConfigurationStateStore>,
- ): StatusBarConfigurationStateStore {
- return if (StatusBarConnectedDisplays.isEnabled) {
- multiDisplayLazy.get()
- } else {
- singleDisplayLazy.get()
- }
- }
-
- @Provides
- @SysUISingleton
- @IntoMap
- @ClassKey(StatusBarConfigurationStateStore::class)
- fun storeAsCoreStartable(
- multiDisplayLazy: Lazy<MultiDisplayStatusBarConfigurationStateStore>
- ): CoreStartable {
- return if (StatusBarConnectedDisplays.isEnabled) {
- multiDisplayLazy.get()
- } else {
- CoreStartable.NOP
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarPerDisplayConfigurationStateRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarPerDisplayConfigurationStateRepository.kt
new file mode 100644
index 000000000000..3168a22c56ad
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarPerDisplayConfigurationStateRepository.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.data.repository
+
+import android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR
+import com.android.systemui.common.ui.ConfigurationState
+import com.android.systemui.common.ui.ConfigurationStateImpl
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepository
+import com.android.systemui.display.data.repository.PerDisplayInstanceProvider
+import com.android.systemui.display.data.repository.PerDisplayInstanceRepositoryImpl
+import com.android.systemui.display.data.repository.PerDisplayRepository
+import com.android.systemui.display.data.repository.SingleInstanceRepositoryImpl
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
+import dagger.Lazy
+import dagger.Module
+import dagger.Provides
+import javax.inject.Inject
+
+@SysUISingleton
+class StatusBarPerDisplayConfigurationStateProvider
+@Inject
+constructor(
+ private val displayWindowPropertiesRepository: DisplayWindowPropertiesRepository,
+ private val statusBarConfigurationControllerStore: StatusBarConfigurationControllerStore,
+ private val factory: ConfigurationStateImpl.Factory,
+) : PerDisplayInstanceProvider<ConfigurationState> {
+
+ override fun createInstance(displayId: Int): ConfigurationState? {
+ val displayWindowProperties =
+ displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR) ?: return null
+ val configController =
+ statusBarConfigurationControllerStore.forDisplay(displayId) ?: return null
+ return factory.create(displayWindowProperties.context, configController)
+ }
+}
+
+@Module
+object StatusBarPerDisplayConfigurationStateModule {
+
+ @Provides
+ @SysUISingleton
+ fun store(
+ instanceProvider: Lazy<StatusBarPerDisplayConfigurationStateProvider>,
+ factory: PerDisplayInstanceRepositoryImpl.Factory<ConfigurationState>,
+ defaultInstance: ConfigurationState,
+ ): PerDisplayRepository<ConfigurationState> {
+ val name = "ConfigurationState"
+ return if (StatusBarConnectedDisplays.isEnabled) {
+ factory.create(name, instanceProvider.get())
+ } else {
+ SingleInstanceRepositoryImpl(name, defaultInstance)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/StatusBarPopupChips.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/StatusBarPopupChips.kt
index 08a8960f6c96..d1307aa0ee31 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/StatusBarPopupChips.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/StatusBarPopupChips.kt
@@ -50,7 +50,9 @@ object StatusBarPopupChips {
* Caution!! Using this check incorrectly will cause crashes in nextfood builds!
*/
@JvmStatic
- inline fun unsafeAssertInNewMode() = RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
+ @Deprecated("Avoid crashing.", ReplaceWith("if (this.isUnexpectedlyInLegacyMode()) return"))
+ inline fun unsafeAssertInNewMode() =
+ RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
/**
* Called to ensure code is only run when the flag is disabled. This will throw an exception if
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/headsup/shared/StatusBarNoHunBehavior.kt b/packages/SystemUI/src/com/android/systemui/statusbar/headsup/shared/StatusBarNoHunBehavior.kt
index 7a985e7fae9a..7b1024ca65dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/headsup/shared/StatusBarNoHunBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/headsup/shared/StatusBarNoHunBehavior.kt
@@ -50,7 +50,9 @@ object StatusBarNoHunBehavior {
* Caution!! Using this check incorrectly will cause crashes in nextfood builds!
*/
@JvmStatic
- inline fun unsafeAssertInNewMode() = RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
+ @Deprecated("Avoid crashing.", ReplaceWith("if (this.isUnexpectedlyInLegacyMode()) return"))
+ inline fun unsafeAssertInNewMode() =
+ RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
/**
* Called to ensure code is only run when the flag is disabled. This will throw an exception if
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
index 4053d065cb16..8be9e410f8f6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
@@ -124,8 +124,14 @@ public final class NotificationClicker implements View.OnClickListener {
Notification notification = sbn.getNotification();
if (notification.contentIntent != null || notification.fullScreenIntent != null
|| row.getEntry().isBubble()) {
- row.setBubbleClickListener(v ->
- mNotificationActivityStarter.onNotificationBubbleIconClicked(row.getEntry()));
+ if (NotificationBundleUi.isEnabled()) {
+ row.setBubbleClickListener(
+ v -> row.getEntryAdapter().onNotificationBubbleIconClicked());
+ } else {
+ row.setBubbleClickListener(v ->
+ mNotificationActivityStarter.onNotificationBubbleIconClicked(
+ row.getEntry()));
+ }
row.setOnClickListener(this);
row.setOnDragSuccessListener(mOnDragSuccessListener);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java
index 6dd44a123538..41353b9921bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java
@@ -45,8 +45,6 @@ import kotlinx.coroutines.flow.StateFlowKt;
*/
public class BundleEntry extends PipelineEntry {
- private final BundleEntryAdapter mEntryAdapter;
-
// TODO(b/394483200): move NotificationEntry's implementation to PipelineEntry?
private final MutableStateFlow<Boolean> mSensitive = StateFlowKt.MutableStateFlow(false);
@@ -55,7 +53,6 @@ public class BundleEntry extends PipelineEntry {
public BundleEntry(String key) {
super(key);
- mEntryAdapter = new BundleEntryAdapter();
}
@Nullable
@@ -86,126 +83,9 @@ public class BundleEntry extends PipelineEntry {
return false;
}
- @VisibleForTesting
- public BundleEntryAdapter getEntryAdapter() {
- return mEntryAdapter;
- }
-
- public class BundleEntryAdapter implements EntryAdapter {
-
- /**
- * TODO (b/394483200): convert to PipelineEntry.ROOT_ENTRY when pipeline is migrated?
- */
- @Override
- public GroupEntry getParent() {
- return GroupEntry.ROOT_ENTRY;
- }
-
- @Override
- public boolean isTopLevelEntry() {
- return true;
- }
-
- @NonNull
- @Override
- public String getKey() {
- return mKey;
- }
-
- @Override
- @Nullable
- public ExpandableNotificationRow getRow() {
- return mRow;
- }
-
- @Override
- public boolean isGroupRoot() {
- return true;
- }
-
- @Override
- public StateFlow<Boolean> isSensitive() {
- return BundleEntry.this.mSensitive;
- }
-
- @Override
- public boolean isClearable() {
- // TODO(b/394483200): check whether all of the children are clearable, when implemented
- return true;
- }
-
- @Override
- public int getTargetSdk() {
- return Build.VERSION_CODES.CUR_DEVELOPMENT;
- }
-
- @Override
- public String getSummarization() {
- return null;
- }
-
- @Override
- public int getContrastedColor(Context context, boolean isLowPriority, int backgroundColor) {
- return Notification.COLOR_DEFAULT;
- }
-
- @Override
- public boolean canPeek() {
- return false;
- }
-
- @Override
- public long getWhen() {
- return 0;
- }
-
- @Override
- public IconPack getIcons() {
- // TODO(b/396446620): implement bundle icons
- return null;
- }
-
- @Override
- public boolean isColorized() {
- return false;
- }
-
- @Override
- @Nullable
- public StatusBarNotification getSbn() {
- return null;
- }
-
- @Override
- public boolean canDragAndDrop() {
- return false;
- }
-
- @Override
- public boolean isBubbleCapable() {
- return false;
- }
-
- @Override
- @Nullable
- public String getStyle() {
- return null;
- }
-
- @Override
- public int getSectionBucket() {
- return mBucket;
- }
-
- @Override
- public boolean isAmbient() {
- return false;
- }
-
- @Override
- public boolean isFullScreenCapable() {
- return false;
- }
+ @Nullable
+ public ExpandableNotificationRow getRow() {
+ return mRow;
}
public static final List<BundleEntry> ROOT_BUNDLES = List.of(
@@ -213,4 +93,8 @@ public class BundleEntry extends PipelineEntry {
new BundleEntry(SOCIAL_MEDIA_ID),
new BundleEntry(NEWS_ID),
new BundleEntry(RECS_ID));
+
+ public MutableStateFlow<Boolean> isSensitive() {
+ return mSensitive;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt
new file mode 100644
index 000000000000..64db9df8270c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt
@@ -0,0 +1,122 @@
+/*
+ * 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.notification.collection
+
+import android.app.Notification
+import android.content.Context
+import android.os.Build
+import android.service.notification.StatusBarNotification
+import com.android.systemui.statusbar.notification.icon.IconPack
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import kotlinx.coroutines.flow.StateFlow
+
+class BundleEntryAdapter(val entry: BundleEntry) : EntryAdapter {
+ /** TODO (b/394483200): convert to PipelineEntry.ROOT_ENTRY when pipeline is migrated? */
+ override fun getParent(): GroupEntry {
+ return GroupEntry.ROOT_ENTRY
+ }
+
+ override fun isTopLevelEntry(): Boolean {
+ return true
+ }
+
+ override fun getKey(): String {
+ return entry.key
+ }
+
+ override fun getRow(): ExpandableNotificationRow? {
+ return entry.row
+ }
+
+ override fun isGroupRoot(): Boolean {
+ return true
+ }
+
+ override fun isSensitive(): StateFlow<Boolean> {
+ return entry.isSensitive
+ }
+
+ override fun isClearable(): Boolean {
+ // TODO(b/394483200): check whether all of the children are clearable, when implemented
+ return true
+ }
+
+ override fun getTargetSdk(): Int {
+ return Build.VERSION_CODES.CUR_DEVELOPMENT
+ }
+
+ override fun getSummarization(): String? {
+ return null
+ }
+
+ override fun getContrastedColor(
+ context: Context?,
+ isLowPriority: Boolean,
+ backgroundColor: Int,
+ ): Int {
+ return Notification.COLOR_DEFAULT
+ }
+
+ override fun canPeek(): Boolean {
+ return false
+ }
+
+ override fun getWhen(): Long {
+ return 0
+ }
+
+ override fun getIcons(): IconPack? {
+ // TODO(b/396446620): implement bundle icons
+ return null
+ }
+
+ override fun isColorized(): Boolean {
+ return false
+ }
+
+ override fun getSbn(): StatusBarNotification? {
+ return null
+ }
+
+ override fun canDragAndDrop(): Boolean {
+ return false
+ }
+
+ override fun isBubbleCapable(): Boolean {
+ return false
+ }
+
+ override fun getStyle(): String? {
+ return null
+ }
+
+ override fun getSectionBucket(): Int {
+ return entry.bucket
+ }
+
+ override fun isAmbient(): Boolean {
+ return false
+ }
+
+ override fun isFullScreenCapable(): Boolean {
+ return false
+ }
+
+ override fun onNotificationBubbleIconClicked() {
+ // do nothing. these cannot be a bubble
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
index 307a9573d5d8..0e75b6050678 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
@@ -50,7 +50,7 @@ public interface EntryAdapter {
/**
* Gets the view that this entry is backing.
*/
- @NonNull
+ @Nullable
ExpandableNotificationRow getRow();
/**
@@ -135,5 +135,10 @@ public interface EntryAdapter {
default boolean isFullScreenCapable() {
return false;
}
+
+ /**
+ * Process a click on a notification bubble icon
+ */
+ void onNotificationBubbleIconClicked();
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/Tracing.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapterFactory.kt
index 9b7cd704aa2f..b7a84ddef103 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/Tracing.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapterFactory.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.
@@ -13,8 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.dagger.qualifiers
-import javax.inject.Qualifier
+package com.android.systemui.statusbar.notification.collection
-@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class Tracing
+/** Creates an appropriate EntryAdapter for the entry type given */
+interface EntryAdapterFactory {
+ fun create(entry: PipelineEntry): EntryAdapter
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapterFactoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapterFactoryImpl.kt
new file mode 100644
index 000000000000..779c25a3b402
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapterFactoryImpl.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.notification.collection
+
+import androidx.annotation.VisibleForTesting
+import com.android.internal.logging.MetricsLogger
+import com.android.systemui.statusbar.notification.NotificationActivityStarter
+import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
+import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider
+import javax.inject.Inject
+
+/** Creates an appropriate EntryAdapter for the entry type given */
+class EntryAdapterFactoryImpl
+@Inject
+constructor(
+ private val notificationActivityStarter: NotificationActivityStarter,
+ private val metricsLogger: MetricsLogger,
+ private val peopleNotificationIdentifier: PeopleNotificationIdentifier,
+ private val iconStyleProvider: NotificationIconStyleProvider,
+ private val visualStabilityCoordinator: VisualStabilityCoordinator,
+) : EntryAdapterFactory {
+ override fun create(entry: PipelineEntry): EntryAdapter {
+ return if (entry is NotificationEntry) {
+ NotificationEntryAdapter(
+ notificationActivityStarter,
+ metricsLogger,
+ peopleNotificationIdentifier,
+ iconStyleProvider,
+ visualStabilityCoordinator,
+ entry,
+ )
+ } else {
+ BundleEntryAdapter((entry as BundleEntry))
+ }
+ }
+
+ @VisibleForTesting
+ fun getNotificationActivityStarter() : NotificationActivityStarter {
+ return notificationActivityStarter
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index b19ba3a18826..3d8ba7f335bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -109,7 +109,6 @@ public final class NotificationEntry extends ListEntry {
private final String mKey;
private StatusBarNotification mSbn;
private Ranking mRanking;
- private final NotifEntryAdapter mEntryAdapter;
/*
* Bookkeeping members
@@ -270,141 +269,6 @@ public final class NotificationEntry extends ListEntry {
mKey = sbn.getKey();
setSbn(sbn);
setRanking(ranking);
- mEntryAdapter = new NotifEntryAdapter();
- }
-
- public class NotifEntryAdapter implements EntryAdapter {
- @Override
- public PipelineEntry getParent() {
- return NotificationEntry.this.getParent();
- }
-
- @Override
- public boolean isTopLevelEntry() {
- return getParent() != null
- && (getParent() == ROOT_ENTRY || ROOT_BUNDLES.contains(getParent()));
- }
-
- @Override
- public String getKey() {
- return NotificationEntry.this.getKey();
- }
-
- @Override
- public ExpandableNotificationRow getRow() {
- return NotificationEntry.this.getRow();
- }
-
- @Override
- public boolean isGroupRoot() {
- if (isTopLevelEntry() || getParent() == null) {
- return false;
- }
- if (NotificationEntry.this.getParent() instanceof GroupEntry parentGroupEntry) {
- return parentGroupEntry.getSummary() == NotificationEntry.this;
- }
- return false;
- }
-
- @Override
- public StateFlow<Boolean> isSensitive() {
- return NotificationEntry.this.isSensitive();
- }
-
- @Override
- public boolean isClearable() {
- return NotificationEntry.this.isClearable();
- }
-
- @Override
- public int getTargetSdk() {
- return NotificationEntry.this.targetSdk;
- }
-
- @Override
- public String getSummarization() {
- return getRanking().getSummarization();
- }
-
- @Override
- public void prepareForInflation() {
- getSbn().clearPackageContext();
- }
-
- @Override
- public int getContrastedColor(Context context, boolean isLowPriority, int backgroundColor) {
- return NotificationEntry.this.getContrastedColor(
- context, isLowPriority, backgroundColor);
- }
-
- @Override
- public boolean canPeek() {
- return isStickyAndNotDemoted();
- }
-
- @Override
- public long getWhen() {
- return getSbn().getNotification().getWhen();
- }
-
- @Override
- public IconPack getIcons() {
- return NotificationEntry.this.getIcons();
- }
-
- @Override
- public boolean isColorized() {
- return getSbn().getNotification().isColorized();
- }
-
- @Override
- @Nullable
- public StatusBarNotification getSbn() {
- return NotificationEntry.this.getSbn();
- }
-
- @Override
- public boolean canDragAndDrop() {
- boolean canBubble = canBubble();
- Notification notif = getSbn().getNotification();
- PendingIntent dragIntent = notif.contentIntent != null ? notif.contentIntent
- : notif.fullScreenIntent;
- if (dragIntent != null && dragIntent.isActivity() && !canBubble) {
- return true;
- }
- return false;
- }
-
- @Override
- public boolean isBubbleCapable() {
- return NotificationEntry.this.isBubble();
- }
-
- @Override
- @Nullable
- public String getStyle() {
- return getNotificationStyle();
- }
-
- @Override
- public int getSectionBucket() {
- return mBucket;
- }
-
- @Override
- public boolean isAmbient() {
- return mRanking.isAmbient();
- }
-
- @Override
- public boolean isFullScreenCapable() {
- return getSbn().getNotification().fullScreenIntent != null;
- }
-
- }
-
- public EntryAdapter getEntryAdapter() {
- return mEntryAdapter;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt
new file mode 100644
index 000000000000..0ff2dd7c7f43
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt
@@ -0,0 +1,145 @@
+/*
+ * 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.notification.collection
+
+import android.content.Context
+import android.service.notification.StatusBarNotification
+import com.android.internal.logging.MetricsLogger
+import com.android.systemui.statusbar.notification.NotificationActivityStarter
+import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator
+import com.android.systemui.statusbar.notification.icon.IconPack
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider
+import kotlinx.coroutines.flow.StateFlow
+
+class NotificationEntryAdapter(
+ private val notificationActivityStarter: NotificationActivityStarter,
+ private val metricsLogger: MetricsLogger,
+ private val peopleNotificationIdentifier: PeopleNotificationIdentifier,
+ private val iconStyleProvider: NotificationIconStyleProvider,
+ private val visualStabilityCoordinator: VisualStabilityCoordinator,
+ private val entry: NotificationEntry,
+) : EntryAdapter {
+
+ override fun getParent(): PipelineEntry? {
+ return entry.parent
+ }
+
+ override fun isTopLevelEntry(): Boolean {
+ return parent != null &&
+ (parent === GroupEntry.ROOT_ENTRY || BundleEntry.ROOT_BUNDLES.contains(parent))
+ }
+
+ override fun getKey(): String {
+ return entry.key
+ }
+
+ override fun getRow(): ExpandableNotificationRow {
+ return entry.row
+ }
+
+ override fun isGroupRoot(): Boolean {
+ if (isTopLevelEntry || parent == null) {
+ return false
+ }
+ return (entry.parent as? GroupEntry)?.summary == entry
+ }
+
+ override fun isSensitive(): StateFlow<Boolean> {
+ return entry.isSensitive
+ }
+
+ override fun isClearable(): Boolean {
+ return entry.isClearable
+ }
+
+ override fun getTargetSdk(): Int {
+ return entry.targetSdk
+ }
+
+ override fun getSummarization(): String? {
+ return entry.ranking?.summarization
+ }
+
+ override fun prepareForInflation() {
+ entry.sbn.clearPackageContext()
+ }
+
+ override fun getContrastedColor(
+ context: Context?,
+ isLowPriority: Boolean,
+ backgroundColor: Int,
+ ): Int {
+ return entry.getContrastedColor(context, isLowPriority, backgroundColor)
+ }
+
+ override fun canPeek(): Boolean {
+ return entry.isStickyAndNotDemoted
+ }
+
+ override fun getWhen(): Long {
+ return entry.sbn.notification.getWhen()
+ }
+
+ override fun getIcons(): IconPack {
+ return entry.icons
+ }
+
+ override fun isColorized(): Boolean {
+ return entry.sbn.notification.isColorized
+ }
+
+ override fun getSbn(): StatusBarNotification {
+ return entry.sbn
+ }
+
+ override fun canDragAndDrop(): Boolean {
+ val canBubble: Boolean = entry.canBubble()
+ val notif = entry.sbn.notification
+ val dragIntent =
+ if (notif.contentIntent != null) notif.contentIntent else notif.fullScreenIntent
+ if (dragIntent != null && dragIntent.isActivity && !canBubble) {
+ return true
+ }
+ return false
+ }
+
+ override fun isBubbleCapable(): Boolean {
+ return entry.isBubble
+ }
+
+ override fun getStyle(): String? {
+ return entry.notificationStyle
+ }
+
+ override fun getSectionBucket(): Int {
+ return entry.bucket
+ }
+
+ override fun isAmbient(): Boolean {
+ return entry.ranking.isAmbient
+ }
+
+ override fun isFullScreenCapable(): Boolean {
+ return entry.sbn.notification.fullScreenIntent != null
+ }
+
+ override fun onNotificationBubbleIconClicked() {
+ notificationActivityStarter.onNotificationBubbleIconClicked(entry)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StabilizeHeadsUpGroup.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StabilizeHeadsUpGroup.kt
index 179a87d7f007..cf7881214ddf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StabilizeHeadsUpGroup.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StabilizeHeadsUpGroup.kt
@@ -50,7 +50,9 @@ object StabilizeHeadsUpGroup {
* Caution!! Using this check incorrectly will cause crashes in nextfood builds!
*/
@JvmStatic
- inline fun unsafeAssertInNewMode() = RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
+ @Deprecated("Avoid crashing.", ReplaceWith("if (this.isUnexpectedlyInLegacyMode()) return"))
+ inline fun unsafeAssertInNewMode() =
+ RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
/**
* Called to ensure code is only run when the flag is disabled. This will throw an exception if
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 53d5dbc58677..ef3da9498f70 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -37,6 +37,8 @@ import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
+import com.android.systemui.statusbar.notification.collection.EntryAdapterFactory;
+import com.android.systemui.statusbar.notification.collection.EntryAdapterFactoryImpl;
import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
import com.android.systemui.statusbar.notification.collection.NotifLiveDataStoreImpl;
@@ -340,4 +342,8 @@ public interface NotificationsModule {
return MagneticNotificationRowManager.getEmpty();
}
}
+
+ /** Provides an instance of {@link EntryAdapterFactory} */
+ @Binds
+ EntryAdapterFactory provideEntryAdapterFactory(EntryAdapterFactoryImpl impl);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/shared/ModesEmptyShadeFix.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/shared/ModesEmptyShadeFix.kt
index d3a44f9b1497..52bf894c796c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/shared/ModesEmptyShadeFix.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/shared/ModesEmptyShadeFix.kt
@@ -50,7 +50,9 @@ object ModesEmptyShadeFix {
* Caution!! Using this check incorrectly will cause crashes in nextfood builds!
*/
@JvmStatic
- inline fun unsafeAssertInNewMode() = RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
+ @Deprecated("Avoid crashing.", ReplaceWith("if (this.isUnexpectedlyInLegacyMode()) return"))
+ inline fun unsafeAssertInNewMode() =
+ RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
/**
* Called to ensure code is only run when the flag is disabled. This will throw an exception if
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/shared/NotifRedesignFooter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/shared/NotifRedesignFooter.kt
index dbe046fdf30b..90faa4d2c714 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/shared/NotifRedesignFooter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/shared/NotifRedesignFooter.kt
@@ -50,7 +50,9 @@ object NotifRedesignFooter {
* Caution!! Using this check incorrectly will cause crashes in nextfood builds!
*/
@JvmStatic
- inline fun unsafeAssertInNewMode() = RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
+ @Deprecated("Avoid crashing.", ReplaceWith("if (this.isUnexpectedlyInLegacyMode()) return"))
+ inline fun unsafeAssertInNewMode() =
+ RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
/**
* Called to ensure code is only run when the flag is disabled. This will throw an exception if
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/NotificationsHunSharedAnimationValues.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/NotificationsHunSharedAnimationValues.kt
index c53831671bfc..f52d351cfb0e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/NotificationsHunSharedAnimationValues.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/NotificationsHunSharedAnimationValues.kt
@@ -50,7 +50,9 @@ object NotificationsHunSharedAnimationValues {
* Caution!! Using this check incorrectly will cause crashes in nextfood builds!
*/
@JvmStatic
- inline fun unsafeAssertInNewMode() = RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
+ @Deprecated("Avoid crashing.", ReplaceWith("if (this.isUnexpectedlyInLegacyMode()) return"))
+ inline fun unsafeAssertInNewMode() =
+ RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
/**
* Called to ensure code is only run when the flag is disabled. This will throw an exception if
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt
index 30aec8dabcaa..147a5afea306 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt
@@ -20,8 +20,8 @@ import android.view.Display
import androidx.lifecycle.lifecycleScope
import com.android.app.tracing.traceSection
import com.android.systemui.common.ui.ConfigurationState
+import com.android.systemui.display.data.repository.PerDisplayRepository
import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.statusbar.data.repository.StatusBarConfigurationStateStore
import com.android.systemui.statusbar.notification.collection.NotifCollection
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder.IconViewStore
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerStatusBarViewModel
@@ -36,7 +36,7 @@ class NotificationIconContainerStatusBarViewBinder
@Inject
constructor(
private val viewModel: NotificationIconContainerStatusBarViewModel,
- private val configurationStore: StatusBarConfigurationStateStore,
+ private val configurationStateRepository: PerDisplayRepository<ConfigurationState>,
private val defaultConfigurationState: ConfigurationState,
private val systemBarUtilsState: SystemBarUtilsState,
private val failureTracker: StatusBarIconViewBindingFailureTracker,
@@ -57,7 +57,7 @@ constructor(
}
}
val configurationState: ConfigurationState =
- configurationStore.forDisplay(displayId) ?: defaultConfigurationState
+ configurationStateRepository[displayId] ?: defaultConfigurationState
lifecycleScope.launch {
NotificationIconContainerViewBinder.bind(
displayId = displayId,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationUi.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationUi.kt
index 5bf5766a886c..da59a40a1624 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationUi.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationUi.kt
@@ -50,7 +50,9 @@ object PromotedNotificationUi {
* Caution!! Using this check incorrectly will cause crashes in nextfood builds!
*/
@JvmStatic
- inline fun unsafeAssertInNewMode() = RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
+ @Deprecated("Avoid crashing.", ReplaceWith("if (this.isUnexpectedlyInLegacyMode()) return"))
+ inline fun unsafeAssertInNewMode() =
+ RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
/**
* Called to ensure code is only run when the flag is disabled. This will throw an exception if
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationUiAod.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationUiAod.kt
index 8679975998f2..c6e3da1c5750 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationUiAod.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationUiAod.kt
@@ -49,7 +49,9 @@ object PromotedNotificationUiAod {
* Caution!! Using this check incorrectly will cause crashes in nextfood builds!
*/
@JvmStatic
- inline fun unsafeAssertInNewMode() = RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
+ @Deprecated("Avoid crashing.", ReplaceWith("if (this.isUnexpectedlyInLegacyMode()) return"))
+ inline fun unsafeAssertInNewMode() =
+ RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
/**
* Called to ensure code is only run when the flag is disabled. This will throw an exception if
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationUiForceExpanded.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationUiForceExpanded.kt
index 287e00257bc5..adeddde8ccc3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationUiForceExpanded.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationUiForceExpanded.kt
@@ -50,7 +50,9 @@ object PromotedNotificationUiForceExpanded {
* Caution!! Using this check incorrectly will cause crashes in nextfood builds!
*/
@JvmStatic
- inline fun unsafeAssertInNewMode() = RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
+ @Deprecated("Avoid crashing.", ReplaceWith("if (this.isUnexpectedlyInLegacyMode()) return"))
+ inline fun unsafeAssertInNewMode() =
+ RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
/**
* Called to ensure code is only run when the flag is disabled. This will throw an exception if
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 987068df3ee9..76d285007d1c 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
@@ -111,6 +111,8 @@ import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.SourceType;
import com.android.systemui.statusbar.notification.collection.EntryAdapter;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntryAdapter;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
@@ -121,6 +123,7 @@ import com.android.systemui.statusbar.notification.people.PeopleNotificationIden
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiForceExpanded;
import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation;
import com.android.systemui.statusbar.notification.row.shared.LockscreenOtpRedaction;
+import com.android.systemui.statusbar.notification.row.ui.viewmodel.BundleHeaderViewModelImpl;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationCompactMessagingTemplateViewWrapper;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
import com.android.systemui.statusbar.notification.shared.NotificationAddXOnHoverToDismiss;
@@ -428,7 +431,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
onExpansionChanged(true /* userAction */, wasExpanded);
}
} else if (mEnableNonGroupedNotificationExpand) {
- if (v.isAccessibilityFocused()) {
+ if (v != null && v.isAccessibilityFocused()) {
mPrivateLayout.setFocusOnVisibilityChange();
}
boolean nowExpanded;
@@ -1818,6 +1821,22 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
);
}
+ /**
+ * Init the bundle header view. The ComposeView is initialized within with the passed viewModel.
+ * This can only be init once and not in conjunction with any other header view.
+ */
+ public void initBundleHeader(@NonNull BundleHeaderViewModelImpl bundleHeaderViewModel) {
+ if (NotificationBundleUi.isUnexpectedlyInLegacyMode()) return;
+ NotificationChildrenContainer childrenContainer = getChildrenContainerNonNull();
+ bundleHeaderViewModel.setOnExpandClickListener(mExpandClickListener);
+
+ childrenContainer.initBundleHeader(bundleHeaderViewModel);
+
+ if (TransparentHeaderFix.isEnabled()) {
+ updateBackgroundForGroupState();
+ }
+ }
+
public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
boolean wasAboveShelf = isAboveShelf();
boolean changed = headsUpAnimatingAway != mHeadsupDisappearRunning;
@@ -2124,7 +2143,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
* Initialize row.
*/
public void initialize(
- NotificationEntry entry,
+ EntryAdapter entryAdapter,
+ PipelineEntry entry,
RemoteInputViewSubcomponent.Factory rivSubcomponentFactory,
String appName,
@NonNull String notificationKey,
@@ -2152,11 +2172,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
NotificationRebindingTracker notificationRebindingTracker) {
if (NotificationBundleUi.isEnabled()) {
+ mEntryAdapter = entryAdapter;
// TODO (b/395857098): remove when all usages are migrated
- mEntryAdapter = entry.getEntryAdapter();
- mEntry = entry;
+ mEntry = (NotificationEntry) entry;
} else {
- mEntry = entry;
+ mEntry = (NotificationEntry) entry;
}
mAppName = appName;
mRebindingTracker = notificationRebindingTracker;
@@ -3048,7 +3068,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mUserLocked = userLocked;
mPrivateLayout.setUserExpanding(userLocked);
- mPublicLayout.setUserExpanding(userLocked);
// This is intentionally not guarded with mIsSummaryWithChildren since we might have had
// children but not anymore.
if (mChildrenContainer != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index 02e8f4917da4..ac55930f5c11 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -45,7 +45,11 @@ import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.notification.ColorUpdateLogger;
import com.android.systemui.statusbar.notification.FeedbackIcon;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.NotificationActivityStarter;
+import com.android.systemui.statusbar.notification.collection.EntryAdapterFactory;
+import com.android.systemui.statusbar.notification.collection.EntryAdapterFactoryImpl;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
+import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator;
import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
@@ -56,6 +60,7 @@ import com.android.systemui.statusbar.notification.people.PeopleNotificationIden
import com.android.systemui.statusbar.notification.row.dagger.AppName;
import com.android.systemui.statusbar.notification.row.dagger.NotificationKey;
import com.android.systemui.statusbar.notification.row.dagger.NotificationRowScope;
+import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider;
import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -118,8 +123,8 @@ public class ExpandableNotificationRowController implements NotifViewController
private final IStatusBarService mStatusBarService;
private final UiEventLogger mUiEventLogger;
private final MSDLPlayer mMSDLPlayer;
-
private final NotificationSettingsController mSettingsController;
+ private final EntryAdapterFactory mEntryAdapterFactory;
@VisibleForTesting
final NotificationSettingsController.Listener mSettingsListener =
@@ -285,7 +290,8 @@ public class ExpandableNotificationRowController implements NotifViewController
IStatusBarService statusBarService,
UiEventLogger uiEventLogger,
MSDLPlayer msdlPlayer,
- NotificationRebindingTracker notificationRebindingTracker) {
+ NotificationRebindingTracker notificationRebindingTracker,
+ EntryAdapterFactory entryAdapterFactory) {
mView = view;
mListContainer = listContainer;
mRemoteInputViewSubcomponentFactory = rivSubcomponentFactory;
@@ -322,14 +328,16 @@ public class ExpandableNotificationRowController implements NotifViewController
mStatusBarService = statusBarService;
mUiEventLogger = uiEventLogger;
mMSDLPlayer = msdlPlayer;
+ mEntryAdapterFactory = entryAdapterFactory;
}
/**
* Initialize the controller.
*/
- public void init(NotificationEntry entry) {
+ public void init(PipelineEntry entry) {
mActivatableNotificationViewController.init();
mView.initialize(
+ mEntryAdapterFactory.create(entry),
entry,
mRemoteInputViewSubcomponentFactory,
mAppName,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt
index 77135802eced..d02f636728fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt
@@ -42,14 +42,6 @@ class MagicActionBackgroundDrawable(
private val buttonShape = Path()
// Color and style
- private val bgPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
- val bgColor =
- context.getColor(
- com.android.internal.R.color.materialColorPrimaryContainer
- )
- color = bgColor
- style = Paint.Style.FILL
- }
private val outlinePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
val outlineColor =
context.getColor(
@@ -99,7 +91,6 @@ class MagicActionBackgroundDrawable(
canvas.save()
// Draw background
canvas.clipPath(buttonShape)
- canvas.drawPath(buttonShape, bgPaint)
// Apply gradient to outline
canvas.drawPath(buttonShape, outlinePaint)
updateGradient(boundsF)
@@ -128,13 +119,11 @@ class MagicActionBackgroundDrawable(
}
override fun setAlpha(alpha: Int) {
- bgPaint.alpha = alpha
outlinePaint.alpha = alpha
invalidateSelf()
}
override fun setColorFilter(colorFilter: ColorFilter?) {
- bgPaint.colorFilter = colorFilter
outlinePaint.colorFilter = colorFilter
invalidateSelf()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationBundleUi.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationBundleUi.kt
index 11c942c9bcc0..37212a387fad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationBundleUi.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationBundleUi.kt
@@ -49,7 +49,9 @@ object NotificationBundleUi {
* Caution!! Using this check incorrectly will cause crashes in nextfood builds!
*/
@JvmStatic
- inline fun unsafeAssertInNewMode() = RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
+ @Deprecated("Avoid crashing.", ReplaceWith("if (this.isUnexpectedlyInLegacyMode()) return"))
+ inline fun unsafeAssertInNewMode() =
+ RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
/**
* Called to ensure code is only run when the flag is disabled. This will throw an exception if
@@ -57,4 +59,4 @@ object NotificationBundleUi {
*/
@JvmStatic
inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index e830d18b7d73..315d37e55bc3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -39,9 +39,11 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.compose.ui.platform.ComposeView;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.NotificationExpandButton;
+import com.android.systemui.notifications.ui.composable.row.BundleHeaderKt;
import com.android.systemui.res.R;
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.statusbar.CrossFadeHelper;
@@ -58,6 +60,7 @@ import com.android.systemui.statusbar.notification.row.HybridGroupManager;
import com.android.systemui.statusbar.notification.row.HybridNotificationView;
import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation;
import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation;
+import com.android.systemui.statusbar.notification.row.ui.viewmodel.BundleHeaderViewModelImpl;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationHeaderViewWrapper;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
@@ -119,6 +122,13 @@ public class NotificationChildrenContainer extends ViewGroup
*/
private boolean mEnableShadowOnChildNotifications;
+ /**
+ * This view is only set when this NCC is a bundle. If this view is set, all other header
+ * view variants have to be null.
+ */
+ private ComposeView mBundleHeaderView;
+ private BundleHeaderViewModelImpl mBundleHeaderViewModel;
+
private NotificationHeaderView mGroupHeader;
private NotificationHeaderViewWrapper mGroupHeaderWrapper;
private NotificationHeaderView mMinimizedGroupHeader;
@@ -189,6 +199,9 @@ public class NotificationChildrenContainer extends ViewGroup
R.dimen.notification_children_container_top_padding);
mHeaderHeight = mCollapsedHeaderMargin + mAdditionalExpandedHeaderMargin;
}
+ if (mBundleHeaderView != null) {
+ initBundleDimens();
+ }
mCollapsedBottomPadding = res.getDimensionPixelOffset(
R.dimen.notification_children_collapsed_bottom_padding);
mEnableShadowOnChildNotifications =
@@ -244,6 +257,10 @@ public class NotificationChildrenContainer extends ViewGroup
mMinimizedGroupHeader.getMeasuredWidth(),
mMinimizedGroupHeader.getMeasuredHeight());
}
+ if (mBundleHeaderView != null) {
+ mBundleHeaderView.layout(0, 0, mBundleHeaderView.getMeasuredWidth(),
+ mBundleHeaderView.getMeasuredHeight());
+ }
}
@Override
@@ -295,6 +312,9 @@ public class NotificationChildrenContainer extends ViewGroup
if (mMinimizedGroupHeader != null) {
mMinimizedGroupHeader.measure(widthMeasureSpec, headerHeightSpec);
}
+ if (mBundleHeaderView != null) {
+ mBundleHeaderView.measure(widthMeasureSpec, headerHeightSpec);
+ }
setMeasuredDimension(width, height);
Trace.endSection();
@@ -489,6 +509,28 @@ public class NotificationChildrenContainer extends ViewGroup
}
/**
+ * Init the bundle header view. The ComposeView is initialized within with the passed viewModel.
+ * This can only be init once and not in conjunction with any other header view.
+ */
+ public void initBundleHeader(@NonNull BundleHeaderViewModelImpl viewModel) {
+ if (NotificationBundleUi.isUnexpectedlyInLegacyMode()) return;
+ if (mBundleHeaderView != null) return;
+ initBundleDimens();
+
+ mBundleHeaderViewModel = viewModel;
+ mBundleHeaderView = BundleHeaderKt.createComposeView(mBundleHeaderViewModel, getContext());
+ addView(mBundleHeaderView);
+ invalidate();
+ }
+
+ private void initBundleDimens() {
+ NotificationBundleUi.unsafeAssertInNewMode();
+ mCollapsedHeaderMargin = mHeaderHeight;
+ mAdditionalExpandedHeaderMargin = 0;
+ mCollapsedBottomPadding = 0;
+ }
+
+ /**
* Set the group header view
* @param headerView view to set
* @param onClickListener OnClickListener of the header view
@@ -1311,6 +1353,17 @@ public class NotificationChildrenContainer extends ViewGroup
mGroupHeader.setHeaderBackgroundDrawable(null);
}
}
+ if (mBundleHeaderView != null) {
+ if (expanded) {
+ ColorDrawable cd = new ColorDrawable();
+ cd.setColor(mContainingNotification.calculateBgColor());
+ // TODO(b/389839492): The backgroundDrawable needs an outline like in the original:
+ // setOutlineProvider(mProvider);
+ mBundleHeaderViewModel.setBackgroundDrawable(cd);
+ } else {
+ mBundleHeaderViewModel.setBackgroundDrawable(null);
+ }
+ }
}
public int getMaxContentHeight() {
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 a277597e23df..c1aa5f12aa99 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
@@ -266,7 +266,7 @@ constructor(
combine(shadeModeInteractor.shadeMode, shadeInteractor.qsExpansion) { shadeMode, qsExpansion
->
when (shadeMode) {
- is ShadeMode.Dual -> false
+ is ShadeMode.Dual,
is ShadeMode.Split -> true
is ShadeMode.Single -> qsExpansion < 0.5f
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index face1c7512ce..fc721bfae369 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -109,6 +109,7 @@ import com.android.systemui.AutoReinflateContainer;
import com.android.systemui.CoreStartable;
import com.android.systemui.DejankUtils;
import com.android.systemui.EventLogTags;
+import com.android.systemui.Flags;
import com.android.systemui.InitController;
import com.android.systemui.Prefs;
import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController;
@@ -3183,12 +3184,27 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
new ActivityTransitionAnimator.Listener() {
@Override
public void onTransitionAnimationStart() {
- mKeyguardViewMediator.setBlursDisabledForAppLaunch(true);
+ if (!Flags.notificationShadeBlur() || !Flags.moveTransitionAnimationLayer()) {
+ mKeyguardViewMediator.setBlursDisabledForAppLaunch(true);
+ }
+ }
+
+ @Override
+ public void onTransitionAnimationProgress(float linearProgress) {
+ if (Flags.notificationShadeBlur() && Flags.moveTransitionAnimationLayer()) {
+ mNotificationShadeDepthControllerLazy.get()
+ .onTransitionAnimationProgress(linearProgress);
+ }
}
@Override
public void onTransitionAnimationEnd() {
- mKeyguardViewMediator.setBlursDisabledForAppLaunch(false);
+ if (Flags.notificationShadeBlur() && Flags.moveTransitionAnimationLayer()) {
+ mNotificationShadeDepthControllerLazy.get()
+ .onTransitionAnimationEnd();
+ } else {
+ mKeyguardViewMediator.setBlursDisabledForAppLaunch(false);
+ }
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/StatusBarChipsModernization.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/StatusBarChipsModernization.kt
index 6afcd8a4fad8..0ac87178086f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/StatusBarChipsModernization.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/StatusBarChipsModernization.kt
@@ -52,7 +52,9 @@ object StatusBarChipsModernization {
* the flag is not enabled to ensure that the refactor author catches issues in testing.
*/
@JvmStatic
- inline fun unsafeAssertInNewMode() = RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
+ @Deprecated("Avoid crashing.", ReplaceWith("if (this.isUnexpectedlyInLegacyMode()) return"))
+ inline fun unsafeAssertInNewMode() =
+ RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
/**
* Called to ensure code is only run when the flag is disabled. This will throw an exception if
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ethernet/shared/StatusBarSignalPolicyRefactorEthernet.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ethernet/shared/StatusBarSignalPolicyRefactorEthernet.kt
index 1d1cfd6cdf68..b2d314c3590b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ethernet/shared/StatusBarSignalPolicyRefactorEthernet.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ethernet/shared/StatusBarSignalPolicyRefactorEthernet.kt
@@ -50,7 +50,9 @@ object StatusBarSignalPolicyRefactorEthernet {
* Caution!! Using this check incorrectly will cause crashes in nextfood builds!
*/
@JvmStatic
- inline fun unsafeAssertInNewMode() = RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
+ @Deprecated("Avoid crashing.", ReplaceWith("if (this.isUnexpectedlyInLegacyMode()) return"))
+ inline fun unsafeAssertInNewMode() =
+ RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
/**
* Called to ensure code is only run when the flag is disabled. This will throw an exception if
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTileGrid.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTileGrid.kt
index 16f24f1d5821..5d7ce91b332c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTileGrid.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTileGrid.kt
@@ -34,7 +34,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.systemui.Flags
-import com.android.systemui.qs.panels.ui.compose.PagerDots
+import com.android.systemui.common.ui.compose.PagerDots
import com.android.systemui.statusbar.policy.ui.dialog.viewmodel.ModesDialogViewModel
@Composable
diff --git a/packages/SystemUI/src/com/android/systemui/supervision/shared/DeprecateDpmSupervisionApis.kt b/packages/SystemUI/src/com/android/systemui/supervision/shared/DeprecateDpmSupervisionApis.kt
index 59c05c2493c3..a94d51a21af3 100644
--- a/packages/SystemUI/src/com/android/systemui/supervision/shared/DeprecateDpmSupervisionApis.kt
+++ b/packages/SystemUI/src/com/android/systemui/supervision/shared/DeprecateDpmSupervisionApis.kt
@@ -50,7 +50,9 @@ object DeprecateDpmSupervisionApis {
* Caution!! Using this check incorrectly will cause crashes in nextfood builds!
*/
@JvmStatic
- inline fun unsafeAssertInNewMode() = RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
+ @Deprecated("Avoid crashing.", ReplaceWith("if (this.isUnexpectedlyInLegacyMode()) return"))
+ inline fun unsafeAssertInNewMode() =
+ RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME)
/**
* Called to ensure code is only run when the flag is disabled. This will throw an exception if
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt
index bb8fe46be5b0..3ac6c7bc0c6b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt
@@ -28,7 +28,7 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
import com.android.systemui.volume.Events
-import com.android.systemui.volume.dialog.dagger.VolumeDialogComponent
+import com.android.systemui.volume.dialog.dagger.factory.VolumeDialogComponentFactory
import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor
import javax.inject.Inject
import kotlinx.coroutines.awaitCancellation
@@ -37,7 +37,7 @@ class VolumeDialog
@Inject
constructor(
@Application context: Context,
- private val componentFactory: VolumeDialogComponent.Factory,
+ private val componentFactory: VolumeDialogComponentFactory,
private val visibilityInteractor: VolumeDialogVisibilityInteractor,
) : ComponentDialog(context, R.style.Theme_SystemUI_Dialog_Volume) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/VolumeDialogComponent.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/VolumeDialogComponent.kt
index 434f6b567048..36b2b48ece21 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/VolumeDialogComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/VolumeDialogComponent.kt
@@ -16,6 +16,7 @@
package com.android.systemui.volume.dialog.dagger
+import com.android.systemui.volume.dialog.dagger.factory.VolumeDialogComponentFactory
import com.android.systemui.volume.dialog.dagger.module.VolumeDialogModule
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
@@ -38,9 +39,9 @@ interface VolumeDialogComponent {
fun sliderComponentFactory(): VolumeDialogSliderComponent.Factory
@Subcomponent.Factory
- interface Factory {
+ interface Factory : VolumeDialogComponentFactory {
- fun create(
+ override fun create(
/**
* Provides a coroutine scope to use inside [VolumeDialogScope].
* [com.android.systemui.volume.dialog.VolumeDialogPlugin] manages the lifecycle of this
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/factory/VolumeDialogComponentFactory.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/factory/VolumeDialogComponentFactory.kt
new file mode 100644
index 000000000000..d909f26dd1dc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/factory/VolumeDialogComponentFactory.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.dagger.factory
+
+import com.android.systemui.volume.dialog.dagger.VolumeDialogComponent
+import kotlinx.coroutines.CoroutineScope
+
+/** Common interface for all dagger Subcomponent.Factory providing [VolumeDialogComponent]. */
+interface VolumeDialogComponentFactory {
+
+ fun create(scope: CoroutineScope): VolumeDialogComponent
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/module/VolumeDialogModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/module/VolumeDialogModule.kt
index 7b08317d5265..fcf4d110f269 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/module/VolumeDialogModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/module/VolumeDialogModule.kt
@@ -16,10 +16,12 @@
package com.android.systemui.volume.dialog.dagger.module
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
import com.android.systemui.volume.dialog.ringer.data.repository.VolumeDialogRingerFeedbackRepository
import com.android.systemui.volume.dialog.ringer.data.repository.VolumeDialogRingerFeedbackRepositoryImpl
import com.android.systemui.volume.dialog.ringer.ui.binder.VolumeDialogRingerViewBinder
import com.android.systemui.volume.dialog.settings.ui.binder.VolumeDialogSettingsButtonViewBinder
+import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderComponent
import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSlidersViewBinder
import com.android.systemui.volume.dialog.ui.binder.ViewBinder
import dagger.Binds
@@ -27,7 +29,7 @@ import dagger.Module
import dagger.Provides
/** Dagger module for volume dialog code in the volume package */
-@Module
+@Module(subcomponents = [VolumeDialogSliderComponent::class])
interface VolumeDialogModule {
@Binds
@@ -38,6 +40,7 @@ interface VolumeDialogModule {
companion object {
@Provides
+ @VolumeDialog
fun provideViewBinders(
slidersViewBinder: VolumeDialogSlidersViewBinder,
ringerViewBinder: VolumeDialogRingerViewBinder,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/module/VolumeDialogPluginModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/module/VolumeDialogPluginModule.kt
index 547c51d1cefd..35752ae08260 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/module/VolumeDialogPluginModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/module/VolumeDialogPluginModule.kt
@@ -17,6 +17,7 @@
package com.android.systemui.volume.dialog.dagger.module
import com.android.systemui.volume.dialog.dagger.VolumeDialogComponent
+import com.android.systemui.volume.dialog.dagger.factory.VolumeDialogComponentFactory
import com.android.systemui.volume.dialog.shared.model.CsdWarningConfigModel
import com.android.systemui.volume.dialog.utils.VolumeTracer
import com.android.systemui.volume.dialog.utils.VolumeTracerImpl
@@ -27,6 +28,11 @@ import dagger.Provides
@Module(subcomponents = [VolumeDialogComponent::class])
interface VolumeDialogPluginModule {
+ @Binds
+ fun bindVolumeDialogComponentFactory(
+ factory: VolumeDialogComponent.Factory
+ ): VolumeDialogComponentFactory
+
@Binds fun bindVolumeTracer(volumeTracer: VolumeTracerImpl): VolumeTracer
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt
index 577e47bb3b83..d0ed24d0b86d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt
@@ -19,6 +19,7 @@ package com.android.systemui.volume.dialog.sliders.dagger
import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSliderType
import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogOverscrollViewBinder
import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSliderViewBinder
+import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderViewModel
import dagger.BindsInstance
import dagger.Subcomponent
@@ -34,6 +35,8 @@ interface VolumeDialogSliderComponent {
fun overscrollViewBinder(): VolumeDialogOverscrollViewBinder
+ fun sliderViewModel(): VolumeDialogSliderViewModel
+
@Subcomponent.Factory
interface Factory {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
index 5de8fe54505f..11d9df4294c0 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
@@ -37,6 +37,7 @@ import com.android.systemui.common.ui.view.onApplyWindowInsets
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.res.R
import com.android.systemui.util.kotlin.awaitCancellationThenDispose
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel
import com.android.systemui.volume.dialog.ui.utils.JankListenerFactory
@@ -72,7 +73,7 @@ constructor(
private val viewModel: VolumeDialogViewModel,
private val jankListenerFactory: JankListenerFactory,
private val tracer: VolumeTracer,
- private val viewBinders: List<@JvmSuppressWildcards ViewBinder>,
+ @VolumeDialog private val viewBinders: List<@JvmSuppressWildcards ViewBinder>,
) {
private val halfOpenedOffsetPx: Float =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
index 562481567536..5c893da45b8d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
@@ -28,7 +28,6 @@ import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBO
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SHOWN_EXPANDED;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SHOWN_MINIMIZED;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SWIPE_DISMISSED;
-import static com.android.systemui.flags.Flags.CLIPBOARD_IMAGE_TIMEOUT;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -60,7 +59,6 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastSender;
-import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.screenshot.TimeoutHandler;
import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -102,7 +100,6 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase {
@Mock
private UiEventLogger mUiEventLogger;
private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
- private FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
@Mock
private Animator mAnimator;
@@ -152,8 +149,6 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase {
mSampleClipData = new ClipData("Test", new String[]{"text/plain"},
new ClipData.Item("Test Item"));
-
- mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, true); // turned off for legacy tests
}
/**
@@ -170,13 +165,13 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase {
getFakeBroadcastDispatcher(),
mBroadcastSender,
mTimeoutHandler,
- mFeatureFlags,
mClipboardUtils,
mExecutor,
mClipboardImageLoader,
mClipboardTransitionExecutor,
mClipboardIndicationProvider,
- mUiEventLogger);
+ mUiEventLogger,
+ new ActionIntentCreator());
verify(mClipboardOverlayView).setCallbacks(mOverlayCallbacksCaptor.capture());
mCallbacks = mOverlayCallbacksCaptor.getValue();
}
@@ -193,7 +188,6 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase {
ClipData clipData = new ClipData("", new String[]{"image/png"},
new ClipData.Item(Uri.parse("")));
- mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, false);
mOverlayController.setClipData(clipData, "");
@@ -208,7 +202,6 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase {
ClipData clipData = new ClipData("", new String[]{"resource/png"},
new ClipData.Item(Uri.parse("")));
- mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, false);
mOverlayController.setClipData(clipData, "");
@@ -219,7 +212,6 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase {
@Test
public void test_setClipData_textData_legacy() {
- mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, false);
initController();
mOverlayController.setClipData(mSampleClipData, "abc");
@@ -232,7 +224,6 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase {
@Test
public void test_setClipData_sensitiveTextData_legacy() {
- mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, false);
initController();
ClipDescription description = mSampleClipData.getDescription();
@@ -250,7 +241,6 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase {
@Test
public void test_setClipData_repeatedCalls_legacy() {
when(mAnimator.isRunning()).thenReturn(true);
- mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, false);
initController();
mOverlayController.setClipData(mSampleClipData, "");
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/CombinedConditionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/CombinedConditionTest.kt
index 8418598c256b..116a2caa6dda 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/CombinedConditionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/CombinedConditionTest.kt
@@ -19,9 +19,9 @@ package com.android.systemui.shared.condition
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.shared.condition.Condition.START_EAGERLY
-import com.android.systemui.shared.condition.Condition.START_LAZILY
-import com.android.systemui.shared.condition.Condition.START_WHEN_NEEDED
+import com.android.systemui.shared.condition.Condition.Companion.START_EAGERLY
+import com.android.systemui.shared.condition.Condition.Companion.START_LAZILY
+import com.android.systemui.shared.condition.Condition.Companion.START_WHEN_NEEDED
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -40,7 +40,7 @@ class CombinedConditionTest : SysuiTestCase() {
scope: CoroutineScope,
initialValue: Boolean?,
overriding: Boolean = false,
- @StartStrategy private val startStrategy: Int = START_WHEN_NEEDED,
+ @StartStrategy override val startStrategy: Int = START_WHEN_NEEDED,
) : Condition(scope, initialValue, overriding) {
private var _started = false
val started: Boolean
@@ -54,10 +54,6 @@ class CombinedConditionTest : SysuiTestCase() {
_started = false
}
- override fun getStartStrategy(): Int {
- return startStrategy
- }
-
fun setValue(value: Boolean?) {
value?.also { updateCondition(value) } ?: clearCondition()
}
@@ -75,13 +71,8 @@ class CombinedConditionTest : SysuiTestCase() {
val combinedCondition =
CombinedCondition(
scope = this,
- conditions =
- listOf(
- eagerCondition,
- lazyCondition,
- startWhenNeededCondition,
- ),
- operand = Evaluator.OP_OR
+ conditions = listOf(eagerCondition, lazyCondition, startWhenNeededCondition),
+ operand = Evaluator.OP_OR,
)
val callback = Condition.Callback {}
@@ -124,13 +115,8 @@ class CombinedConditionTest : SysuiTestCase() {
val combinedCondition =
CombinedCondition(
scope = this,
- conditions =
- listOf(
- startWhenNeededCondition,
- lazyCondition,
- eagerCondition,
- ),
- operand = Evaluator.OP_AND
+ conditions = listOf(startWhenNeededCondition, lazyCondition, eagerCondition),
+ operand = Evaluator.OP_AND,
)
val callback = Condition.Callback {}
@@ -175,7 +161,7 @@ class CombinedConditionTest : SysuiTestCase() {
FakeCondition(
scope = this,
initialValue = false,
- startStrategy = START_WHEN_NEEDED
+ startStrategy = START_WHEN_NEEDED,
)
}
.toList()
@@ -214,7 +200,7 @@ class CombinedConditionTest : SysuiTestCase() {
FakeCondition(
scope = this,
initialValue = false,
- startStrategy = START_WHEN_NEEDED
+ startStrategy = START_WHEN_NEEDED,
)
}
.toList()
@@ -262,7 +248,7 @@ class CombinedConditionTest : SysuiTestCase() {
FakeCondition(
scope = this,
initialValue = false,
- startStrategy = START_WHEN_NEEDED
+ startStrategy = START_WHEN_NEEDED,
)
}
.toList()
@@ -300,9 +286,9 @@ class CombinedConditionTest : SysuiTestCase() {
overridingCondition1,
lazyCondition,
startWhenNeededCondition,
- overridingCondition2
+ overridingCondition2,
),
- operand = Evaluator.OP_OR
+ operand = Evaluator.OP_OR,
)
val callback = Condition.Callback {}
@@ -414,11 +400,7 @@ class CombinedConditionTest : SysuiTestCase() {
fun testEmptyConditions() = runSelfCancelingTest {
for (operand in intArrayOf(Evaluator.OP_OR, Evaluator.OP_AND)) {
val combinedCondition =
- CombinedCondition(
- scope = this,
- conditions = emptyList(),
- operand = operand,
- )
+ CombinedCondition(scope = this, conditions = emptyList(), operand = operand)
val callback = Condition.Callback {}
combinedCondition.addCallback(callback)
@@ -435,9 +417,7 @@ class CombinedConditionTest : SysuiTestCase() {
* Executes the given block of execution within the scope of a dedicated [CoroutineScope] which
* is then automatically canceled and cleaned-up.
*/
- private fun runSelfCancelingTest(
- block: suspend CoroutineScope.() -> Unit,
- ) =
+ private fun runSelfCancelingTest(block: suspend CoroutineScope.() -> Unit) =
runBlocking(IMMEDIATE) {
val scope = CoroutineScope(coroutineContext + Job())
block(scope)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
index af89403c5397..623989ec5809 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
@@ -67,6 +67,7 @@ import com.android.systemui.scene.shared.model.sceneDataSource
import com.android.systemui.scene.ui.view.mockWindowRootViewProvider
import com.android.systemui.settings.brightness.data.repository.brightnessMirrorShowingRepository
import com.android.systemui.settings.displayTracker
+import com.android.systemui.shade.data.repository.fakeShadeDisplaysRepository
import com.android.systemui.shade.data.repository.shadeRepository
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shade.domain.interactor.shadeLayoutParams
@@ -203,5 +204,6 @@ class KosmosJavaAdapter() {
val windowRootViewBlurInteractor by lazy { kosmos.windowRootViewBlurInteractor }
val sysuiState by lazy { kosmos.sysUiState }
val displayTracker by lazy { kosmos.displayTracker }
+ val fakeShadeDisplaysRepository by lazy { kosmos.fakeShadeDisplaysRepository }
val sysUIStateDispatcher by lazy { kosmos.sysUIStateDispatcher }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/NotificationActivityStarterKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/NotificationActivityStarterKosmos.kt
index c337ac201b3d..8e98fe31a56a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/NotificationActivityStarterKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/NotificationActivityStarterKosmos.kt
@@ -18,6 +18,10 @@ package com.android.systemui.statusbar.notification
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.statusbar.phone.statusBarNotificationActivityStarter
+import org.mockito.kotlin.mock
var Kosmos.notificationActivityStarter: NotificationActivityStarter by
Kosmos.Fixture { statusBarNotificationActivityStarter }
+
+var Kosmos.mockNotificationActivityStarter: NotificationActivityStarter by
+Kosmos.Fixture { mock<NotificationActivityStarter>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerKosmos.kt
new file mode 100644
index 000000000000..f3cdabb5813e
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * 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.notification.collection.render
+
+import com.android.systemui.kosmos.Kosmos
+import org.mockito.kotlin.mock
+
+var Kosmos.groupMembershipManager by Kosmos.Fixture { mock<GroupMembershipManager>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/people/NotificationPersonExtractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/people/NotificationPersonExtractorKosmos.kt
new file mode 100644
index 000000000000..c263c5d96610
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/people/NotificationPersonExtractorKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * 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.notification.people
+
+import com.android.systemui.kosmos.Kosmos
+import org.mockito.kotlin.mock
+
+val Kosmos.notificationPersonExtractor by Kosmos.Fixture { mock<NotificationPersonExtractor>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifierKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifierKosmos.kt
new file mode 100644
index 000000000000..20982eb43797
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifierKosmos.kt
@@ -0,0 +1,25 @@
+/*
+ * 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.notification.people
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.notification.collection.render.groupMembershipManager
+
+val Kosmos.peopleNotificationIdentifier by
+ Kosmos.Fixture {
+ PeopleNotificationIdentifierImpl(notificationPersonExtractor, groupMembershipManager)
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/EntryAdapterFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/EntryAdapterFactoryKosmos.kt
new file mode 100644
index 000000000000..e99f61e7cd13
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/EntryAdapterFactoryKosmos.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row
+
+import com.android.internal.logging.metricsLogger
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.notification.collection.EntryAdapterFactoryImpl
+import com.android.systemui.statusbar.notification.collection.coordinator.visualStabilityCoordinator
+import com.android.systemui.statusbar.notification.mockNotificationActivityStarter
+import com.android.systemui.statusbar.notification.people.peopleNotificationIdentifier
+import com.android.systemui.statusbar.notification.row.icon.notificationIconStyleProvider
+
+val Kosmos.entryAdapterFactory by
+Kosmos.Fixture {
+ EntryAdapterFactoryImpl(
+ mockNotificationActivityStarter,
+ metricsLogger,
+ peopleNotificationIdentifier,
+ notificationIconStyleProvider,
+ visualStabilityCoordinator,
+ )
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
index 771e1a5dccc3..ff4fbf9f0e94 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
@@ -53,8 +53,11 @@ import com.android.systemui.statusbar.SmartReplyController
import com.android.systemui.statusbar.notification.ColorUpdateLogger
import com.android.systemui.statusbar.notification.ConversationNotificationManager
import com.android.systemui.statusbar.notification.ConversationNotificationProcessor
+import com.android.systemui.statusbar.notification.NotificationActivityStarter
+import com.android.systemui.statusbar.notification.collection.EntryAdapterFactoryImpl
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider
@@ -76,6 +79,7 @@ import com.android.systemui.statusbar.notification.row.NotificationRowContentBin
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag
import com.android.systemui.statusbar.notification.row.icon.AppIconProviderImpl
+import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider
import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProviderImpl
import com.android.systemui.statusbar.notification.row.icon.NotificationRowIconViewInflaterFactory
import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor
@@ -364,11 +368,22 @@ class ExpandableNotificationRowBuilder(
)
val row = rowInflaterTask.inflateSynchronously(context, null, entry)
+ val entryAdapter =
+ EntryAdapterFactoryImpl(
+ Mockito.mock(NotificationActivityStarter::class.java),
+ Mockito.mock(MetricsLogger::class.java),
+ Mockito.mock(PeopleNotificationIdentifier::class.java),
+ Mockito.mock(NotificationIconStyleProvider::class.java),
+ Mockito.mock(VisualStabilityCoordinator::class.java),
+ )
+ .create(entry)
+
entry.row = row
mIconManager.createIcons(entry)
mBindPipelineEntryListener.onEntryInit(entry)
mBindPipeline.manageRow(entry, row)
row.initialize(
+ entryAdapter,
entry,
Mockito.mock(RemoteInputViewSubcomponent.Factory::class.java, STUB_ONLY),
APP_NAME,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponentKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponentKosmos.kt
index 4ca044d60f3f..34218646b818 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponentKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponentKosmos.kt
@@ -33,6 +33,8 @@ import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSlide
import com.android.systemui.volume.dialog.sliders.domain.model.volumeDialogSliderType
import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogOverscrollViewBinder
import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSliderViewBinder
+import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderViewModel
+import com.android.systemui.volume.dialog.sliders.ui.viewmodel.volumeDialogSliderViewModel
import com.android.systemui.volume.dialog.sliders.ui.volumeDialogOverscrollViewBinder
import com.android.systemui.volume.dialog.sliders.ui.volumeDialogSliderViewBinder
import com.android.systemui.volume.mediaControllerRepository
@@ -60,6 +62,9 @@ fun Kosmos.volumeDialogSliderComponent(type: VolumeDialogSliderType): VolumeDial
}
}
+ override fun sliderViewModel(): VolumeDialogSliderViewModel =
+ localKosmos.volumeDialogSliderViewModel
+
override fun sliderViewBinder(): VolumeDialogSliderViewBinder =
localKosmos.volumeDialogSliderViewBinder
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 296f7cfe93ba..f54027ec0272 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -514,6 +514,7 @@ final class UiModeManagerService extends SystemService {
mCarModeEnabled = mDockState == Intent.EXTRA_DOCK_STATE_CAR;
registerVrStateListener();
// register listeners
+ // LINT.IfChange(fi_cb)
context.getContentResolver()
.registerContentObserver(Secure.getUriFor(Secure.UI_NIGHT_MODE),
false, mDarkThemeObserver, 0);
@@ -523,6 +524,7 @@ final class UiModeManagerService extends SystemService {
Secure.getUriFor(ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED),
false, mForceInvertStateObserver, UserHandle.USER_ALL);
}
+ // LINT.ThenChange(/core/java/android/view/ViewRootImpl.java:fi_cb)
context.getContentResolver().registerContentObserver(
Secure.getUriFor(Secure.CONTRAST_LEVEL), false,
mContrastObserver, UserHandle.USER_ALL);
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 1503d889c298..eea667ef2f39 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -1443,17 +1443,32 @@ class ProcessRecord implements WindowProcessListener {
void onProcessFrozen() {
mProfile.onProcessFrozen();
- if (mThread != null) mThread.onProcessPaused();
+ final ApplicationThreadDeferred t;
+ synchronized (mService) {
+ t = mThread;
+ }
+ // Release the lock before calling the notifier, in case that calls back into AM.
+ if (t != null) t.onProcessPaused();
}
void onProcessUnfrozen() {
- if (mThread != null) mThread.onProcessUnpaused();
+ final ApplicationThreadDeferred t;
+ synchronized (mService) {
+ t = mThread;
+ }
+ // Release the lock before calling the notifier, in case that calls back into AM.
+ if (t != null) t.onProcessUnpaused();
mProfile.onProcessUnfrozen();
mServices.onProcessUnfrozen();
}
void onProcessFrozenCancelled() {
- if (mThread != null) mThread.onProcessPausedCancelled();
+ final ApplicationThreadDeferred t;
+ synchronized (mService) {
+ t = mThread;
+ }
+ // Release the lock before calling the notifier, in case that calls back into AM.
+ if (t != null) t.onProcessPausedCancelled();
mServices.onProcessFrozenCancelled();
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index af726bd28718..68ad8f7e9433 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -2902,12 +2902,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
final long ident = Binder.clearCallingIdentity();
try {
if (windowPerceptible != null && !windowPerceptible) {
- if ((vis & InputMethodService.IME_VISIBLE) != 0) {
- vis &= ~InputMethodService.IME_VISIBLE;
- vis |= InputMethodService.IME_VISIBLE_IMPERCEPTIBLE;
- }
- } else {
- vis &= ~InputMethodService.IME_VISIBLE_IMPERCEPTIBLE;
+ vis &= ~InputMethodService.IME_VISIBLE;
}
final var curId = bindingController.getCurId();
// TODO(b/305849394): Make mMenuController multi-user aware.
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 6ab30595e46b..098f113dc11a 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -897,7 +897,7 @@ final class DefaultPermissionGrantPolicy {
SearchManager.INTENT_ACTION_GLOBAL_SEARCH, userId);
grantPermissionsToSystemPackage(pm, voiceSearchPackage,
userId, PHONE_PERMISSIONS, CALENDAR_PERMISSIONS, NEARBY_DEVICES_PERMISSIONS,
- COARSE_BACKGROUND_LOCATION_PERMISSIONS);
+ COARSE_BACKGROUND_LOCATION_PERMISSIONS, CONTACTS_PERMISSIONS);
revokeRuntimePermissions(pm, voiceSearchPackage,
FINE_LOCATION_PERMISSIONS, false, userId);
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index d620e98d3437..bac732637d8d 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1173,12 +1173,19 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
return;
}
- final ComponentName wpService = mWallpaper.getComponent();
// The broadcast of package update could be delayed after service disconnected. Try
// to re-bind the service for 10 seconds.
mWallpaper.mBindSource = BindSource.CONNECTION_TRY_TO_REBIND;
- if (bindWallpaperComponentLocked(
- wpService, true, false, mWallpaper, null)) {
+ boolean success;
+ if (liveWallpaperContentHandling()) {
+ success = bindWallpaperDescriptionLocked(
+ mWallpaper.getDescription(), /* force= */ true,
+ /* fromUser= */ false, mWallpaper, /* reply= */ null);
+ } else {
+ success = bindWallpaperComponentLocked(mWallpaper.getComponent(), /* force= */
+ true, /* fromUser= */false, mWallpaper, /* reply= */ null);
+ }
+ if (success) {
mWallpaper.connection.scheduleTimeoutLocked();
} else if (SystemClock.uptimeMillis() - mWallpaper.lastDiedTime
< WALLPAPER_RECONNECT_TIMEOUT_MS) {
@@ -1189,7 +1196,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
// Timeout
Slog.w(TAG, "Reverting to built-in wallpaper!");
clearWallpaperLocked(mWallpaper.mWhich, mWallpaper.userId, false, null);
- final String flattened = wpService.flattenToString();
+ final String flattened = mWallpaper.getComponent().flattenToString();
EventLog.writeEvent(EventLogTags.WP_WALLPAPER_CRASHED,
flattened.substring(0, Math.min(flattened.length(),
MAX_WALLPAPER_COMPONENT_LOG_LENGTH)));
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 36686fc086f1..2287a687700c 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -288,9 +288,10 @@ public class BackgroundActivityStartController {
}
private boolean isHomeApp(int uid, @Nullable String packageName) {
- if (getService().mHomeProcess != null) {
- // Fast check
- return uid == getService().mHomeProcess.mUid;
+ WindowProcessController homeProcess = getService().mHomeProcess;
+ if (homeProcess != null && (homeProcess.mUid != SYSTEM_UID || packageName == null)) {
+ // Fast check (skip if sharing system UID and we have a package name)
+ return uid == homeProcess.mUid;
}
if (packageName == null) {
return false;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 65efa310658f..353ccd5836c8 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3284,6 +3284,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
/**
+ * Whether the display is allowed to switch the content mode between extended and mirroring.
+ * If the content mode is extended, the display will start home activity and show system
+ * decorations, such as wallpapaer, status bar and navigation bar.
+ * If the content mode is mirroring, the display will not show home activity or system
+ * decorations.
+ * The content mode is switched when {@link Display#canHostTasks()} changes.
+ *
* Note that we only allow displays that are able to show system decorations to use the content
* mode switch; however, not all displays that are able to show system decorations are allowed
* to use the content mode switch.
diff --git a/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index d7f91e009c92..2093ccc86653 100644
--- a/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -156,19 +156,6 @@ class SeamlessAppRotationTest(flicker: LegacyFlickerTest) : RotationTransition(f
flicker.assertLayers { isVisible(testApp) }
}
- /** Checks that [testApp] layer covers the entire screen during the whole transition */
- @Presubmit
- @Test
- fun appLayerRotates() {
- flicker.assertLayers {
- this.invoke("entireScreenCovered") { entry ->
- entry.entry.displays.map { display ->
- entry.visibleRegion(testApp).coversExactly(display.layerStackSpace)
- }
- }
- }
- }
-
/** {@inheritDoc} */
@Test
@Ignore("Not applicable to this CUJ. App is full screen")
@@ -225,7 +212,6 @@ class SeamlessAppRotationTest(flicker: LegacyFlickerTest) : RotationTransition(f
visibleLayersShownMoreThanOneConsecutiveEntry()
visibleWindowsShownMoreThanOneConsecutiveEntry()
- runAndIgnoreAssumptionViolation { appLayerRotates() }
runAndIgnoreAssumptionViolation { appLayerAlwaysVisible() }
runAndIgnoreAssumptionViolation { navBarLayerIsVisibleAtStartAndEnd() }
runAndIgnoreAssumptionViolation { navBarWindowIsAlwaysVisible() }
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index 7c24a4adca3d..98af8b2f53b5 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -39,7 +39,6 @@
android:value="true" />
<activity android:name=".SimpleActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.SimpleActivity"
- android:theme="@style/CutoutShortEdges"
android:label="SimpleActivity"
android:exported="true">
<intent-filter>
@@ -49,7 +48,6 @@
</activity>
<activity android:name=".ImeActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.ImeActivity"
- android:theme="@style/CutoutShortEdges"
android:label="ImeActivity"
android:exported="true">
<intent-filter>
@@ -58,7 +56,6 @@
</intent-filter>
</activity>
<activity android:name=".ImeActivityAutoFocus"
- android:theme="@style/CutoutShortEdges"
android:taskAffinity="com.android.server.wm.flicker.testapp.ImeActivityAutoFocus"
android:windowSoftInputMode="stateVisible"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
@@ -81,7 +78,6 @@
</activity>
<activity android:name=".SeamlessRotationActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.SeamlessRotationActivity"
- android:theme="@style/CutoutShortEdges"
android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize"
android:showWhenLocked="true"
android:label="SeamlessActivity"
@@ -92,7 +88,6 @@
</intent-filter>
</activity>
<activity android:name=".NonResizeableActivity"
- android:theme="@style/CutoutShortEdges"
android:resizeableActivity="false"
android:taskAffinity="com.android.server.wm.flicker.testapp.NonResizeableActivity"
android:label="NonResizeableActivity"
@@ -174,7 +169,6 @@
</activity>
<activity android:name=".LaunchNewActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.LaunchNewActivity"
- android:theme="@style/CutoutShortEdges"
android:configChanges="orientation|screenSize"
android:label="LaunchNewActivity"
android:exported="true">
@@ -185,7 +179,6 @@
</activity>
<activity android:name=".LaunchNewTaskActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.LaunchNewTaskActivity"
- android:theme="@style/CutoutShortEdges"
android:configChanges="orientation|screenSize"
android:label="LaunchNewTaskActivity"
android:exported="true">
@@ -207,7 +200,6 @@
</activity>
<activity android:name=".PortraitOnlyActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.PortraitOnlyActivity"
- android:theme="@style/CutoutShortEdges"
android:screenOrientation="portrait"
android:configChanges="orientation|screenSize"
android:exported="true">
@@ -219,7 +211,6 @@
<activity android:name=".ImeEditorPopupDialogActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.ImeEditorPopupDialogActivity"
android:configChanges="orientation|screenSize"
- android:theme="@style/CutoutShortEdges"
android:label="ImeEditorPopupDialogActivity"
android:exported="true">
<intent-filter>
@@ -229,7 +220,6 @@
</activity>
<activity android:name=".ShowWhenLockedActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.ShowWhenLockedActivity"
- android:theme="@style/CutoutShortEdges"
android:configChanges="orientation|screenSize"
android:label="ShowWhenLockedActivity"
android:showWhenLocked="true"
@@ -241,7 +231,6 @@
</activity>
<activity android:name=".NotificationActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.NotificationActivity"
- android:theme="@style/CutoutShortEdges"
android:configChanges="orientation|screenSize"
android:label="NotificationActivity"
android:exported="true">
@@ -254,7 +243,6 @@
android:name=".ActivityEmbeddingMainActivity"
android:label="ActivityEmbedding Main"
android:taskAffinity="com.android.server.wm.flicker.testapp.ActivityEmbedding"
- android:theme="@style/CutoutShortEdges"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
android:exported="true">
<intent-filter>
@@ -266,7 +254,6 @@
android:name=".ActivityEmbeddingTrampolineActivity"
android:label="ActivityEmbedding Trampoline"
android:taskAffinity="com.android.server.wm.flicker.testapp.ActivityEmbedding"
- android:theme="@style/CutoutShortEdges"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
android:exported="false">
</activity>
@@ -274,7 +261,6 @@
android:name=".ActivityEmbeddingSecondaryActivity"
android:label="ActivityEmbedding Secondary"
android:taskAffinity="com.android.server.wm.flicker.testapp.ActivityEmbedding"
- android:theme="@style/CutoutShortEdges"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
android:supportsPictureInPicture="true"
android:exported="false"/>
@@ -282,21 +268,18 @@
android:name=".ActivityEmbeddingThirdActivity"
android:label="ActivityEmbedding Third"
android:taskAffinity="com.android.server.wm.flicker.testapp.ActivityEmbedding"
- android:theme="@style/CutoutShortEdges"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
android:exported="false"/>
<activity
android:name=".ActivityEmbeddingAlwaysExpandActivity"
android:label="ActivityEmbedding AlwaysExpand"
android:taskAffinity="com.android.server.wm.flicker.testapp.ActivityEmbedding"
- android:theme="@style/CutoutShortEdges"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
android:exported="false"/>
<activity
android:name=".ActivityEmbeddingPlaceholderPrimaryActivity"
android:label="ActivityEmbedding Placeholder Primary"
android:taskAffinity="com.android.server.wm.flicker.testapp.ActivityEmbedding"
- android:theme="@style/CutoutShortEdges"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
android:exported="false">
</activity>
@@ -304,7 +287,6 @@
android:name=".ActivityEmbeddingPlaceholderSecondaryActivity"
android:label="ActivityEmbedding Placeholder Secondary"
android:taskAffinity="com.android.server.wm.flicker.testapp.ActivityEmbedding"
- android:theme="@style/CutoutShortEdges"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
android:exported="false"/>
<activity android:name=".MailActivity"
@@ -334,7 +316,6 @@
android:supportsPictureInPicture="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
android:taskAffinity="com.android.server.wm.flicker.testapp.PipActivity"
- android:theme="@style/CutoutShortEdges"
android:launchMode="singleTop"
android:label="PipActivity"
android:exported="true">
@@ -350,7 +331,6 @@
<activity android:name=".BottomHalfPipLaunchingActivity"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
android:taskAffinity="com.android.server.wm.flicker.testapp.BottomHalfPipLaunchingActivity"
- android:theme="@style/CutoutShortEdges"
android:label="BottomHalfPipLaunchingActivity"
android:exported="true">
<intent-filter>
@@ -371,7 +351,6 @@
<activity android:name=".SplitScreenActivity"
android:resizeableActivity="true"
android:taskAffinity="com.android.server.wm.flicker.testapp.SplitScreenActivity"
- android:theme="@style/CutoutShortEdges"
android:label="SplitScreenPrimaryActivity"
android:exported="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation">
@@ -383,7 +362,6 @@
<activity android:name=".SplitScreenSecondaryActivity"
android:resizeableActivity="true"
android:taskAffinity="com.android.server.wm.flicker.testapp.SplitScreenSecondaryActivity"
- android:theme="@style/CutoutShortEdges"
android:label="SplitScreenSecondaryActivity"
android:exported="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation">
@@ -396,7 +374,6 @@
</activity>
<activity android:name=".SendNotificationActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.SendNotificationActivity"
- android:theme="@style/CutoutShortEdges"
android:label="SendNotificationActivity"
android:exported="true">
<intent-filter>
@@ -408,7 +385,6 @@
android:name=".LaunchBubbleActivity"
android:label="LaunchBubbleActivity"
android:exported="true"
- android:theme="@style/CutoutShortEdges"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
@@ -420,7 +396,6 @@
android:name=".BubbleActivity"
android:label="BubbleActivity"
android:exported="false"
- android:theme="@style/CutoutShortEdges"
android:resizeableActivity="true"/>
<activity
android:name=".TransferSplashscreenActivity"
@@ -468,4 +443,4 @@
</service>
</application>
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION"/>
-</manifest>
+</manifest> \ No newline at end of file
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_bottom_half_pip.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_bottom_half_pip.xml
index 2f9c3aa82057..15e2a798b2cf 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_bottom_half_pip.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_bottom_half_pip.xml
@@ -18,6 +18,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:fitsSystemWindows="true"
android:orientation="vertical"
android:background="@android:color/holo_blue_bright">
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_bubble.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_bubble.xml
index 7c7b2cacaefb..4bc70996eba8 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_bubble.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_bubble.xml
@@ -16,6 +16,7 @@
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fitsSystemWindows="true"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_base_layout.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_base_layout.xml
index f0dfdfce035f..e98ffd045d3d 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_base_layout.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_base_layout.xml
@@ -19,5 +19,6 @@
android:id="@+id/root_activity_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:fitsSystemWindows="true"
android:orientation="vertical">
</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml
index 939ba81a47ea..16c906d9ee42 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml
@@ -18,6 +18,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:fitsSystemWindows="true"
android:background="@android:color/holo_orange_light">
<LinearLayout
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_secondary_activity_layout.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_secondary_activity_layout.xml
index 6d4de995bd73..eedb910b2b4e 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_secondary_activity_layout.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_secondary_activity_layout.xml
@@ -19,6 +19,7 @@
android:id="@+id/secondary_activity_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:fitsSystemWindows="true"
android:orientation="vertical">
<Button
@@ -49,4 +50,4 @@
android:layout_height="48dp"
android:text="Enter pip" />
-</LinearLayout> \ No newline at end of file
+</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
index 507c1b622613..5e5203cb0545 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
@@ -17,6 +17,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_green_light"
+ android:fitsSystemWindows="true"
android:focusableInTouchMode="true"
android:orientation="vertical">
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_launch_new.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_launch_new.xml
index fe7bced690f9..2ab5fe7b9d6c 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_launch_new.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_launch_new.xml
@@ -18,6 +18,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:fitsSystemWindows="true"
android:background="@android:color/holo_orange_light">
<Button
android:id="@+id/launch_second_activity"
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_mail.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_mail.xml
index 8a97433ede04..fcef791afe2e 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_mail.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_mail.xml
@@ -19,6 +19,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
+ android:fitsSystemWindows="true"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_non_resizeable.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_non_resizeable.xml
index 6d5a9dd29248..aeb8423680a3 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_non_resizeable.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_non_resizeable.xml
@@ -18,6 +18,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:fitsSystemWindows="true"
android:orientation="vertical"
android:background="@android:color/holo_orange_light">
@@ -29,4 +30,4 @@
android:text="NonResizeableActivity"
android:textAppearance="?android:attr/textAppearanceLarge"/>
-</LinearLayout> \ No newline at end of file
+</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml
index 365a0ea017f6..d66b3d74e114 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml
@@ -18,6 +18,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:fitsSystemWindows="true"
android:orientation="vertical"
android:background="@android:color/holo_blue_bright">
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_simple.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_simple.xml
index 5d94e5177dcc..42213d6812da 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_simple.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_simple.xml
@@ -18,6 +18,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:fitsSystemWindows="true"
android:background="@android:color/holo_orange_light">
</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_splitscreen.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_splitscreen.xml
index 79e88e49b99e..16c3bc07d55c 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_splitscreen.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_splitscreen.xml
@@ -18,6 +18,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:fitsSystemWindows="true"
android:orientation="vertical"
android:background="@android:color/holo_green_light">
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_splitscreen_secondary.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_splitscreen_secondary.xml
index ed9feaf1eade..7643cf5de216 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_splitscreen_secondary.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_splitscreen_secondary.xml
@@ -18,6 +18,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:fitsSystemWindows="true"
android:orientation="vertical"
android:background="@android:color/holo_blue_light">
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_start_media_projection.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_start_media_projection.xml
index c34d2003ef42..79e7bb5de96e 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_start_media_projection.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_start_media_projection.xml
@@ -18,6 +18,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:fitsSystemWindows="true"
android:gravity="center"
android:orientation="vertical"
android:background="@android:color/holo_orange_light">
@@ -39,4 +40,4 @@
android:gravity="center_vertical|center_horizontal"
android:text="Start Media Projection with extra intent"
android:textAppearance="?android:attr/textAppearanceLarge"/>
-</LinearLayout> \ No newline at end of file
+</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_surfaceview.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_surfaceview.xml
index 0b4693dec6e1..4d12168ca8db 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_surfaceview.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_surfaceview.xml
@@ -19,6 +19,7 @@
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:fitsSystemWindows="true"
android:background="@android:color/holo_orange_light">
<SurfaceView
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_transparent.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_transparent.xml
index 0730ded66ce4..32df5f015414 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_transparent.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_transparent.xml
@@ -15,6 +15,7 @@
~ limitations under the License.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fitsSystemWindows="true"
android:layout_width="match_parent"
android:layout_height="match_parent">
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_transparent_launch.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_transparent_launch.xml
index ff4ead95f16e..a0c87fd17fc3 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_transparent_launch.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_transparent_launch.xml
@@ -17,6 +17,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:fitsSystemWindows="true"
android:orientation="vertical"
android:background="@android:color/black">
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/notification_button.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/notification_button.xml
index 78072006f681..da58b3f43c73 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/notification_button.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/notification_button.xml
@@ -18,10 +18,12 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:fitsSystemWindows="true"
android:background="@android:color/holo_orange_light">
<Button
android:id="@+id/post_notification"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal|center_vertical"
android:text="Post Notification" />
</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/task_button.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/task_button.xml
index 8f75d175d00a..ec3135c0a42d 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/task_button.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/task_button.xml
@@ -18,6 +18,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:fitsSystemWindows="true"
android:background="@android:color/holo_orange_light">
<Button
android:id="@+id/launch_new_task"
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
index 837d050b73ff..ca3244820893 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
@@ -36,10 +36,6 @@
<item name="android:windowLayoutInDisplayCutoutMode">default</item>
</style>
- <style name="CutoutShortEdges" parent="@style/DefaultTheme">
- <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
- </style>
-
<style name="CutoutNever" parent="@style/DefaultTheme">
<item name="android:windowLayoutInDisplayCutoutMode">never</item>
</style>
@@ -78,4 +74,4 @@
<!-- Here we want to match the duration of our AVD -->
<item name="android:windowSplashScreenAnimationDuration">900</item>
</style>
-</resources>
+</resources> \ No newline at end of file
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivity.java
index 4418b5a5ff82..30bf616cfe74 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivity.java
@@ -30,7 +30,6 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.util.Log;
-import android.view.WindowManager;
public class ImeActivity extends Activity {
@@ -64,10 +63,6 @@ public class ImeActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- WindowManager.LayoutParams p = getWindow().getAttributes();
- p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
- .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
- getWindow().setAttributes(p);
setContentView(R.layout.activity_ime);
final var filter = new IntentFilter();
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeEditorPopupDialogActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeEditorPopupDialogActivity.java
index 95f933f97bb2..887a15c9ea90 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeEditorPopupDialogActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeEditorPopupDialogActivity.java
@@ -30,8 +30,6 @@ public class ImeEditorPopupDialogActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
WindowManager.LayoutParams p = getWindow().getAttributes();
- p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
- .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
p.softInputMode = SOFT_INPUT_STATE_ALWAYS_HIDDEN;
getWindow().setAttributes(p);
LinearLayout layout = new LinearLayout(this);
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchNewActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchNewActivity.java
index e5710c850f07..97d7a64d5ebf 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchNewActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchNewActivity.java
@@ -19,17 +19,12 @@ package com.android.server.wm.flicker.testapp;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
-import android.view.WindowManager;
import android.widget.Button;
public class LaunchNewActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- WindowManager.LayoutParams p = getWindow().getAttributes();
- p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
- .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
- getWindow().setAttributes(p);
setContentView(R.layout.activity_launch_new);
Button button = findViewById(R.id.launch_second_activity);
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchNewTaskActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchNewTaskActivity.java
index 1809781b33e6..402a393e7028 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchNewTaskActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchNewTaskActivity.java
@@ -21,17 +21,12 @@ import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
-import android.view.WindowManager;
import android.widget.Button;
public class LaunchNewTaskActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- WindowManager.LayoutParams p = getWindow().getAttributes();
- p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
- .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
- getWindow().setAttributes(p);
setContentView(R.layout.task_button);
Button button = findViewById(R.id.launch_new_task);
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java
index d6427abcc65a..61254385e980 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java
@@ -45,14 +45,10 @@ public class NotificationActivity extends Activity {
requestPermissions(new String[] { POST_NOTIFICATIONS }, 0);
}
- WindowManager.LayoutParams p = getWindow().getAttributes();
- p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
- .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
- getWindow().setAttributes(p);
setContentView(R.layout.notification_button);
- Button button = findViewById(R.id.post_notification);
- button.setOnClickListener(v -> postNotification());
+ ((Button) findViewById(R.id.post_notification))
+ .setOnClickListener(v -> postNotification());
createNotificationChannel();
}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
index ee25ab2fb66c..e030dcf0db9b 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
@@ -47,8 +47,6 @@ import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Rational;
import android.view.View;
-import android.view.Window;
-import android.view.WindowManager;
import android.widget.CheckBox;
import android.widget.RadioButton;
@@ -145,12 +143,6 @@ public class PipActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- final Window window = getWindow();
- final WindowManager.LayoutParams layoutParams = window.getAttributes();
- layoutParams.layoutInDisplayCutoutMode = WindowManager.LayoutParams
- .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
- window.setAttributes(layoutParams);
-
setContentView(R.layout.activity_pip);
findViewById(R.id.media_session_start)
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PortraitOnlyActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PortraitOnlyActivity.java
index b1876b5e5511..552d843676bb 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PortraitOnlyActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PortraitOnlyActivity.java
@@ -18,16 +18,11 @@ package com.android.server.wm.flicker.testapp;
import android.app.Activity;
import android.os.Bundle;
-import android.view.WindowManager;
public class PortraitOnlyActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- WindowManager.LayoutParams p = getWindow().getAttributes();
- p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
- .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
- getWindow().setAttributes(p);
setContentView(R.layout.activity_simple);
}
}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SeamlessRotationActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SeamlessRotationActivity.java
index ce7a0059fa2d..e98c34db0cd9 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SeamlessRotationActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SeamlessRotationActivity.java
@@ -61,8 +61,6 @@ public class SeamlessRotationActivity extends Activity {
private void enableSeamlessRotation() {
WindowManager.LayoutParams p = getWindow().getAttributes();
p.rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
- p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
- .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ShowWhenLockedActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ShowWhenLockedActivity.java
index 6f94b74ccf41..a533c9052574 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ShowWhenLockedActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ShowWhenLockedActivity.java
@@ -18,16 +18,11 @@ package com.android.server.wm.flicker.testapp;
import android.app.Activity;
import android.os.Bundle;
-import android.view.WindowManager;
public class ShowWhenLockedActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- WindowManager.LayoutParams p = getWindow().getAttributes();
- p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
- .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
- getWindow().setAttributes(p);
setContentView(R.layout.activity_simple);
}
}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SimpleActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SimpleActivity.java
index 699abf87d341..c56eefe32189 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SimpleActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SimpleActivity.java
@@ -18,16 +18,11 @@ package com.android.server.wm.flicker.testapp;
import android.app.Activity;
import android.os.Bundle;
-import android.view.WindowManager;
public class SimpleActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- WindowManager.LayoutParams p = getWindow().getAttributes();
- p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
- .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
- getWindow().setAttributes(p);
setContentView(R.layout.activity_simple);
}
}