summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AconfigFlags.bp16
-rw-r--r--Android.bp1
-rw-r--r--apct-tests/perftests/core/src/android/os/TracePerfTest.java25
-rw-r--r--boot/boot-image-profile.txt1
-rw-r--r--boot/preloaded-classes1
-rw-r--r--cmds/uinput/src/com/android/commands/uinput/EvemuParser.java33
-rw-r--r--config/preloaded-classes1
-rw-r--r--core/api/module-lib-current.txt2
-rw-r--r--core/api/system-current.txt4
-rw-r--r--core/java/android/app/ApplicationStartInfo.java15
-rw-r--r--core/java/android/app/LocaleConfig.java27
-rw-r--r--core/java/android/app/notification.aconfig10
-rw-r--r--core/java/android/content/pm/PackageParser.java18
-rw-r--r--core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java33
-rw-r--r--core/java/android/hardware/camera2/CameraManager.java3
-rw-r--r--core/java/android/hardware/input/input_framework.aconfig9
-rw-r--r--core/java/android/hardware/serial/flags/flags.aconfig2
-rw-r--r--core/java/android/net/VpnManager.java26
-rw-r--r--core/java/android/net/flags.aconfig8
-rw-r--r--core/java/android/os/CombinedMessageQueue/MessageQueue.java4
-rw-r--r--core/java/android/os/PerfettoTrace.java15
-rw-r--r--core/java/android/os/PerfettoTrackEventExtra.java67
-rw-r--r--core/java/android/os/image/flags/trade_in_mode_flags.aconfig9
-rw-r--r--core/java/android/util/ArrayMap.java2
-rw-r--r--core/java/android/view/OrientationEventListener.java10
-rw-r--r--core/java/android/view/WindowManagerImpl.java2
-rw-r--r--core/java/android/widget/DatePickerCalendarDelegate.java48
-rw-r--r--core/java/android/widget/TimePickerClockDelegate.java52
-rw-r--r--core/java/android/window/DesktopModeFlags.java4
-rw-r--r--core/java/com/android/internal/jank/DisplayResolutionTracker.java17
-rw-r--r--core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java10
-rw-r--r--core/java/com/android/internal/protolog/common/LogDataType.java4
-rw-r--r--core/java/com/android/internal/widget/ConversationLayout.java20
-rw-r--r--core/java/com/android/internal/widget/MessagingLayout.java8
-rw-r--r--core/java/com/android/server/servicewatcher/OWNERS2
-rw-r--r--core/jni/Android.bp2
-rw-r--r--core/jni/AndroidRuntime.cpp2
-rw-r--r--core/jni/android_media_AudioSystem.cpp30
-rw-r--r--core/jni/android_media_AudioVolumeGroupCallback.cpp176
-rw-r--r--core/jni/android_media_AudioVolumeGroupCallback.h44
-rw-r--r--core/res/res/values/config.xml4
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_restart.xml28
-rw-r--r--libs/WindowManager/Shell/res/drawable/desktop_windowing_transition_background.xml6
-rw-r--r--libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml7
-rw-r--r--libs/WindowManager/Shell/res/values/colors.xml2
-rw-r--r--libs/WindowManager/Shell/res/values/dimen.xml13
-rw-r--r--libs/WindowManager/Shell/res/values/strings.xml2
-rw-r--r--libs/WindowManager/Shell/shared/res/values/dimen.xml4
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetView.kt6
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java28
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImeHandler.kt51
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt30
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt70
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt21
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/DefaultHomePackageSupplier.kt12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DeskTransition.kt23
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserver.kt105
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java31
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt81
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt89
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ThemeUtils.kt3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt48
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt41
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt88
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayControllerTests.java21
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt24
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt131
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserverTest.kt30
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java19
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java23
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt49
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolderTest.kt5
-rw-r--r--libs/androidfw/AssetManager2.cpp4
-rw-r--r--libs/androidfw/ResourceTypes.cpp16
-rw-r--r--libs/androidfw/include/androidfw/ResourceTypes.h5
-rw-r--r--libs/hwui/jni/Bitmap.cpp4
-rw-r--r--libs/hwui/jni/GIFMovie.cpp10
-rw-r--r--libs/hwui/renderthread/VulkanManager.cpp13
-rw-r--r--libs/hwui/renderthread/VulkanManager.h8
-rw-r--r--location/api/system-current.txt8
-rw-r--r--location/java/android/location/BeidouSatelliteEphemeris.java22
-rw-r--r--location/java/android/location/GalileoAssistance.java16
-rw-r--r--media/java/android/media/AudioManager.java96
-rw-r--r--media/java/android/media/AudioSystem.java21
-rw-r--r--media/java/android/media/IAudioService.aidl7
-rw-r--r--media/java/android/media/audiopolicy/AudioVolumeGroupChangeHandler.java166
-rw-r--r--media/java/android/media/audiopolicy/IAudioVolumeChangeDispatcher.aidl31
-rw-r--r--media/java/android/media/projection/TEST_MAPPING6
-rw-r--r--media/java/android/media/quality/MediaQualityContract.java38
-rw-r--r--media/tests/AudioPolicyTest/AndroidManifest.xml1
-rw-r--r--media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupChangeHandlerTest.java211
-rw-r--r--media/tests/projection/TEST_MAPPING7
-rw-r--r--packages/SettingsLib/AdaptiveIcon/Android.bp3
-rw-r--r--packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIconShapeDrawable.java3
-rw-r--r--packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java2
-rw-r--r--packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/AndroidSecureSettings.java4
-rw-r--r--packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManager.kt5
-rw-r--r--packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerImpl.kt11
-rw-r--r--packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerProvider.kt53
-rw-r--r--packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java71
-rw-r--r--packages/SettingsLib/SettingsTheme/res/layout/settingslib_expressive_collapsable_textview.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/CollapsableTextView.kt1
-rw-r--r--packages/SettingsLib/Spa/build.gradle.kts6
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt2
-rw-r--r--packages/SettingsLib/Spa/gradle/libs.versions.toml4
-rw-r--r--packages/SettingsLib/Spa/spa/build.gradle.kts8
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/AmbientVolumeUiController.java1
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java5
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt25
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt7
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerProviderTest.kt89
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java4
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CsipDeviceManagerTest.java4
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/data/repository/ZenModeRepositoryTest.kt10
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java5
-rw-r--r--packages/SystemUI/Android.bp3
-rw-r--r--packages/SystemUI/accessibility/accessibilitymenu/Android.bp1
-rw-r--r--packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java13
-rw-r--r--packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java5
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig31
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt5
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimFlingBehavior.kt73
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt19
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/session/ui/composable/Session.kt92
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt13
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt21
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java13
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationSettingsControllerTest.java8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java16
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java13
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java13
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractorTest.kt20
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorTest.kt218
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt11
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt12
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/util/MediaDiffUtilTest.kt52
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt14
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt5
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt12
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/power/PowerUITest.java8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelTest.kt12
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt10
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerSwipeDetectorTest.kt24
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java5
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt37
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt5
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt153
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt214
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt101
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt42
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt212
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractorTest.kt16
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java30
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt18
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryKairosAdapterTest.kt70
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt86
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorKairosAdapterTest.kt141
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt55
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorKairosAdapterTest.kt223
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt67
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/topwindoweffects/TopLevelWindowEffectsTest.kt11
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractorTest.kt1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModelTest.kt82
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java7
-rw-r--r--packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml3
-rw-r--r--packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml3
-rw-r--r--packages/SystemUI/res/layout/volume_dialog.xml7
-rw-r--r--packages/SystemUI/res/layout/volume_dialog_slider_floating.xml4
-rw-r--r--packages/SystemUI/res/layout/volume_dialog_top_section.xml6
-rw-r--r--packages/SystemUI/res/values/dimens.xml19
-rw-r--r--packages/SystemUI/shared/biometrics/Android.bp3
-rw-r--r--packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt8
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java3
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/ScreenDecorations.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java45
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/MagnificationSettingsController.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/MirrorWindowControl.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/SimpleMirrorWindowControl.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractView.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistManager.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayWindow.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/DevicePosturingListener.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractor.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/posturing/data/model/PositionState.kt48
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/NoOpPosturingRepository.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/PosturingRepository.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractor.kt202
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/posturing/shared/model/PosturedState.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java50
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/ui/view/UdfpsAccessibilityOverlay.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/UdfpsAccessibilityOverlayViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/docking/binder/KeyboardDockingIndicationViewBinder.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/SideFpsProgressBar.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaDataLoadingModel.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt96
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelCallback.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelListUpdateCallback.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCommonViewModel.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterBase.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterLegacy.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningView.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/PowerUI.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ModesDndTile.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerSwipeDetector.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotWindow.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelper.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/model/NotificationChipModel.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStore.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotWindowController.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt73
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt63
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/AODPromotedNotificationInteractor.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractor.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/topwindoweffects/TopLevelWindowEffects.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/display/DisplayHelper.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/util/RingerDrawerConstraintsUtils.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java32
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentControllerTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsReceiverTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt45
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionKairosAdapterTelephonySmokeTests.kt69
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryKairosAdapterTest.kt72
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt127
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt73
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryKairosAdapterTest.kt126
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt727
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/utils/WindowManagerProviderImplTest.kt53
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java7
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/data/repository/FakePosturingRepository.kt16
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorKosmos.kt23
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelKosmos.kt5
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStoreKosmos.kt10
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/PrivacyDotWindowControllerKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationContentExtractor.kt10
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentBuilder.kt33
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallTestHelper.kt6
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerFactory.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStoreKosmos.kt11
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/utils/windowmanager/FakeWindowManagerProvider.kt28
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCsdWarningInteractorKosmos.kt (renamed from packages/SystemUI/tests/utils/src/com/android/app/viewcapture/ViewCaptureAwareWindowManagerKosmos.kt)22
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModelKosmos.kt41
-rw-r--r--packages/SystemUI/utils/Android.bp2
-rw-r--r--packages/SystemUI/utils/src/com/android/systemui/utils/windowmanager/WindowManagerProvider.kt56
-rw-r--r--packages/SystemUI/utils/src/com/android/systemui/utils/windowmanager/WindowManagerProviderImpl.kt28
-rw-r--r--packages/SystemUI/utils/src/com/android/systemui/utils/windowmanager/WindowManagerUtils.kt49
-rw-r--r--services/core/Android.bp1
-rw-r--r--services/core/java/com/android/server/adb/AdbService.java26
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java47
-rw-r--r--services/core/java/com/android/server/audio/AudioSystemAdapter.java24
-rw-r--r--services/core/java/com/android/server/audio/AudioVolumeChangeHandler.java103
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java90
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java5
-rw-r--r--services/core/java/com/android/server/locksettings/Android.bp11
-rw-r--r--services/core/java/com/android/server/locksettings/RebootEscrowManager.java66
-rw-r--r--services/core/java/com/android/server/locksettings/flags.aconfig9
-rw-r--r--services/core/java/com/android/server/media/projection/Android.bp21
-rw-r--r--services/core/java/com/android/server/media/projection/TEST_MAPPING28
-rw-r--r--services/core/java/com/android/server/media/quality/MediaQualityUtils.java258
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerService.java4
-rw-r--r--services/core/java/com/android/server/pm/ComputerEngine.java3
-rw-r--r--services/core/java/com/android/server/pm/DeletePackageHelper.java2
-rw-r--r--services/core/java/com/android/server/pm/InstallRequest.java3
-rw-r--r--services/core/java/com/android/server/pm/UserManagerInternal.java13
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java20
-rw-r--r--services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java281
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java8
-rw-r--r--services/core/java/com/android/server/wm/AppCompatCameraOverrides.java3
-rw-r--r--services/core/java/com/android/server/wm/AppCompatCameraPolicy.java4
-rw-r--r--services/core/java/com/android/server/wm/DeviceStateAutoRotateSettingIssueLogger.java42
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp5
-rw-r--r--services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt7
-rw-r--r--services/supervision/java/com/android/server/supervision/SupervisionService.java25
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java3
-rw-r--r--services/tests/servicestests/Android.bp1
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java13
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/AudioVolumeChangeHandlerTest.java142
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java61
-rw-r--r--services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt50
-rw-r--r--services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java43
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DeviceStateAutoRotateSettingIssueLoggerTests.java43
-rw-r--r--tests/Input/src/com/android/server/input/InputManagerServiceTests.kt25
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/Exceptions.kt (renamed from tools/protologtool/src/com/android/protolog/tool/exceptions.kt)25
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessorImpl.kt14
-rw-r--r--tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorImplTest.kt37
428 files changed, 7231 insertions, 3588 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index f9cc1259a375..b9537b357517 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -50,6 +50,7 @@ aconfig_declarations_group {
"android.hardware.devicestate.feature.flags-aconfig-java",
"android.hardware.flags-aconfig-java",
"android.hardware.radio.flags-aconfig-java",
+ "android.hardware.serial.flags-aconfig-java",
"android.hardware.usb.flags-aconfig-java",
"android.location.flags-aconfig-java",
"android.media.codec-aconfig-java",
@@ -1962,3 +1963,18 @@ java_aconfig_library {
aconfig_declarations: "android.service.selinux.flags-aconfig",
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+
+// Serial
+aconfig_declarations {
+ name: "android.hardware.serial.flags-aconfig",
+ exportable: true,
+ package: "android.hardware.serial.flags",
+ container: "system",
+ srcs: ["core/java/android/hardware/serial/flags/*.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.hardware.serial.flags-aconfig-java",
+ aconfig_declarations: "android.hardware.serial.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/Android.bp b/Android.bp
index 127556f8e075..e7c20418bcef 100644
--- a/Android.bp
+++ b/Android.bp
@@ -427,6 +427,7 @@ java_defaults {
"framework-permission-aidl-java",
"spatializer-aidl-java",
"audiopolicy-aidl-java",
+ "volumegroupcallback-aidl-java",
"sounddose-aidl-java",
"modules-utils-expresslog",
"perfetto_trace_javastream_protos_jarjar",
diff --git a/apct-tests/perftests/core/src/android/os/TracePerfTest.java b/apct-tests/perftests/core/src/android/os/TracePerfTest.java
index 00e1c1fdbf4b..3df708d1a5cd 100644
--- a/apct-tests/perftests/core/src/android/os/TracePerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/TracePerfTest.java
@@ -46,6 +46,7 @@ public class TracePerfTest {
private static final String FOO = "foo";
private static final Category FOO_CATEGORY = new Category(FOO);
+ private static final Category UNREGISTERED_CATEGORY = new Category("unregistered");
private static PerfettoTrace.Session sPerfettoSession;
@BeforeClass
@@ -163,6 +164,30 @@ public class TracePerfTest {
}
}
+ @Test
+ public void testInstantPerfettoWithProtoUnregistered() {
+ PerfettoTrace.begin(UNREGISTERED_CATEGORY, "message_queue_receive")
+ .beginProto()
+ .beginNested(2004 /* message_queue */)
+ .addField(1 /* sending_thread_name */, "foo")
+ .endNested()
+ .endProto()
+ .setTerminatingFlow(5)
+ .emit();
+
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ PerfettoTrace.begin(UNREGISTERED_CATEGORY, "message_queue_receive")
+ .beginProto()
+ .beginNested(2004 /* message_queue */)
+ .addField(1 /* sending_thread_name */, "foo")
+ .endNested()
+ .endProto()
+ .setTerminatingFlow(5)
+ .emit();
+ }
+ }
+
private static TraceConfig getTraceConfig(String cat) {
BufferConfig bufferConfig = BufferConfig.newBuilder().setSizeKb(1024).build();
TrackEventConfig trackEventConfig = TrackEventConfig
diff --git a/boot/boot-image-profile.txt b/boot/boot-image-profile.txt
index d7c409fd54b4..4c3e5dce7a95 100644
--- a/boot/boot-image-profile.txt
+++ b/boot/boot-image-profile.txt
@@ -28829,7 +28829,6 @@ Landroid/media/audiopolicy/AudioProductStrategy$AudioAttributesGroup;
Landroid/media/audiopolicy/AudioProductStrategy;
Landroid/media/audiopolicy/AudioVolumeGroup$1;
Landroid/media/audiopolicy/AudioVolumeGroup;
-Landroid/media/audiopolicy/AudioVolumeGroupChangeHandler;
Landroid/media/audiopolicy/IAudioPolicyCallback$Stub$Proxy;
Landroid/media/audiopolicy/IAudioPolicyCallback$Stub;
Landroid/media/audiopolicy/IAudioPolicyCallback;
diff --git a/boot/preloaded-classes b/boot/preloaded-classes
index 7f4b3244c164..048687781774 100644
--- a/boot/preloaded-classes
+++ b/boot/preloaded-classes
@@ -5509,7 +5509,6 @@ android.media.audiopolicy.AudioProductStrategy$AudioAttributesGroup
android.media.audiopolicy.AudioProductStrategy
android.media.audiopolicy.AudioVolumeGroup$1
android.media.audiopolicy.AudioVolumeGroup
-android.media.audiopolicy.AudioVolumeGroupChangeHandler
android.media.audiopolicy.IAudioPolicyCallback$Stub$Proxy
android.media.audiopolicy.IAudioPolicyCallback$Stub
android.media.audiopolicy.IAudioPolicyCallback
diff --git a/cmds/uinput/src/com/android/commands/uinput/EvemuParser.java b/cmds/uinput/src/com/android/commands/uinput/EvemuParser.java
index da991624e685..d3e62d5351f0 100644
--- a/cmds/uinput/src/com/android/commands/uinput/EvemuParser.java
+++ b/cmds/uinput/src/com/android/commands/uinput/EvemuParser.java
@@ -48,12 +48,17 @@ public class EvemuParser implements EventParser {
private static class CommentAwareReader {
private final LineNumberReader mReader;
- private String mPreviousLine;
- private String mNextLine;
+ /** The previous line of the file, or {@code null} if we're at the start of the file. */
+ private @Nullable String mPreviousLine;
+ /**
+ * The next line of the file to be returned from {@link #peekLine()}, or {@code null} if we
+ * haven't peeked since the last {@link #advance()} or are at the end of the file.
+ */
+ private @Nullable String mNextLine;
+ private boolean mAtEndOfFile = false;
- CommentAwareReader(LineNumberReader in) throws IOException {
+ CommentAwareReader(LineNumberReader in) {
mReader = in;
- mNextLine = findNextLine();
}
private @Nullable String findNextLine() throws IOException {
@@ -61,7 +66,7 @@ public class EvemuParser implements EventParser {
while (line != null && line.length() == 0) {
String unstrippedLine = mReader.readLine();
if (unstrippedLine == null) {
- // End of file.
+ mAtEndOfFile = true;
return null;
}
line = stripComments(unstrippedLine);
@@ -85,22 +90,28 @@ public class EvemuParser implements EventParser {
* {@code null} if the end of the file is reached. However, it does not advance to the
* next line of the file.
*/
- public @Nullable String peekLine() {
+ public @Nullable String peekLine() throws IOException {
+ if (mNextLine == null && !mAtEndOfFile) {
+ mNextLine = findNextLine();
+ }
return mNextLine;
}
/** Moves to the next line of the file. */
- public void advance() throws IOException {
+ public void advance() {
mPreviousLine = mNextLine;
- mNextLine = findNextLine();
+ mNextLine = null;
}
public boolean isAtEndOfFile() {
- return mNextLine == null;
+ return mAtEndOfFile;
}
- /** Returns the previous line, for error messages. */
- public String getPreviousLine() {
+ /**
+ * Returns the previous line, for error messages. Will be {@code null} if we're at the start
+ * of the file.
+ */
+ public @Nullable String getPreviousLine() {
return mPreviousLine;
}
diff --git a/config/preloaded-classes b/config/preloaded-classes
index 707acb00b102..3f0e00be75a7 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -5514,7 +5514,6 @@ android.media.audiopolicy.AudioProductStrategy$AudioAttributesGroup
android.media.audiopolicy.AudioProductStrategy
android.media.audiopolicy.AudioVolumeGroup$1
android.media.audiopolicy.AudioVolumeGroup
-android.media.audiopolicy.AudioVolumeGroupChangeHandler
android.media.audiopolicy.IAudioPolicyCallback$Stub$Proxy
android.media.audiopolicy.IAudioPolicyCallback$Stub
android.media.audiopolicy.IAudioPolicyCallback
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 526a213a6003..98570172e43c 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -304,6 +304,8 @@ package android.net {
field public static final int TYPE_VPN_LEGACY = 3; // 0x3
field public static final int TYPE_VPN_NONE = -1; // 0xffffffff
field public static final int TYPE_VPN_OEM = 4; // 0x4
+ field @FlaggedApi("android.net.platform.flags.vpn_type_oem_service_and_legacy") public static final int TYPE_VPN_OEM_LEGACY = 6; // 0x6
+ field @FlaggedApi("android.net.platform.flags.vpn_type_oem_service_and_legacy") public static final int TYPE_VPN_OEM_SERVICE = 5; // 0x5
field public static final int TYPE_VPN_PLATFORM = 2; // 0x2
field public static final int TYPE_VPN_SERVICE = 1; // 0x1
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 746035462329..95b9b49dae3d 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -7442,7 +7442,7 @@ package android.media {
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void muteAwaitConnection(@NonNull int[], @NonNull android.media.AudioDeviceAttributes, long, @NonNull java.util.concurrent.TimeUnit) throws java.lang.IllegalStateException;
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int registerAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void registerMuteAwaitConnectionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.MuteAwaitConnectionCallback);
- method public void registerVolumeGroupCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.VolumeGroupCallback);
+ method @FlaggedApi("android.media.audio.register_volume_callback_api_hardening") @RequiresPermission("Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED") public void registerVolumeGroupCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.VolumeGroupCallback);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeAssistantServicesUids(@NonNull int[]);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean removeDeviceAsNonDefaultForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull android.media.AudioDeviceAttributes);
method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, "android.permission.QUERY_AUDIO_STATE"}) public void removeOnDevicesForAttributesChangedListener(@NonNull android.media.AudioManager.OnDevicesForAttributesChangedListener);
@@ -7473,7 +7473,7 @@ package android.media {
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicyAsync(@NonNull android.media.audiopolicy.AudioPolicy);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterMuteAwaitConnectionCallback(@NonNull android.media.AudioManager.MuteAwaitConnectionCallback);
- method public void unregisterVolumeGroupCallback(@NonNull android.media.AudioManager.VolumeGroupCallback);
+ method @FlaggedApi("android.media.audio.register_volume_callback_api_hardening") @RequiresPermission("Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED") public void unregisterVolumeGroupCallback(@NonNull android.media.AudioManager.VolumeGroupCallback);
field public static final String ACTION_VOLUME_CHANGED = "android.media.VOLUME_CHANGED_ACTION";
field public static final int AUDIOFOCUS_FLAG_DELAY_OK = 1; // 0x1
field public static final int AUDIOFOCUS_FLAG_LOCK = 4; // 0x4
diff --git a/core/java/android/app/ApplicationStartInfo.java b/core/java/android/app/ApplicationStartInfo.java
index 2559bd036039..e2af7c03b5fa 100644
--- a/core/java/android/app/ApplicationStartInfo.java
+++ b/core/java/android/app/ApplicationStartInfo.java
@@ -1082,6 +1082,15 @@ public final class ApplicationStartInfo implements Parcelable {
final ApplicationStartInfo o = (ApplicationStartInfo) other;
+ boolean intentEquals = true;
+ if (android.content.flags.Flags.intentSaveToXmlPackage()) {
+ if (mStartIntent == null) {
+ intentEquals = o.mStartIntent == null;
+ } else {
+ intentEquals = mStartIntent.filterEquals(o.mStartIntent);
+ }
+ }
+
return mPid == o.mPid
&& mRealUid == o.mRealUid
&& mPackageUid == o.mPackageUid
@@ -1095,14 +1104,16 @@ public final class ApplicationStartInfo implements Parcelable {
&& timestampsEquals(o)
&& mWasForceStopped == o.mWasForceStopped
&& mMonotonicCreationTimeMs == o.mMonotonicCreationTimeMs
- && mStartComponent == o.mStartComponent;
+ && mStartComponent == o.mStartComponent
+ && intentEquals;
}
@Override
public int hashCode() {
return Objects.hash(mPid, mRealUid, mPackageUid, mDefiningUid, mReason, mStartupState,
mStartType, mLaunchMode, mPackageName, mProcessName, mStartupTimestampsNs,
- mMonotonicCreationTimeMs, mStartComponent);
+ mMonotonicCreationTimeMs, mStartComponent,
+ android.content.flags.Flags.intentSaveToXmlPackage() ? mStartIntent : null);
}
private boolean timestampsEquals(@NonNull ApplicationStartInfo other) {
diff --git a/core/java/android/app/LocaleConfig.java b/core/java/android/app/LocaleConfig.java
index f56bf4d434e7..cbfd7fccb7c6 100644
--- a/core/java/android/app/LocaleConfig.java
+++ b/core/java/android/app/LocaleConfig.java
@@ -144,15 +144,12 @@ public class LocaleConfig implements Parcelable {
}
}
Resources res = context.getResources();
- //Get the resource id
int resId = context.getApplicationInfo().getLocaleConfigRes();
if (resId == 0) {
mStatus = STATUS_NOT_SPECIFIED;
return;
}
- try {
- //Get the parser to read XML data
- XmlResourceParser parser = res.getXml(resId);
+ try (XmlResourceParser parser = res.getXml(resId)) {
parseLocaleConfig(parser, res);
} catch (Resources.NotFoundException e) {
Slog.w(TAG, "The resource file pointed to by the given resource ID isn't found.");
@@ -208,22 +205,22 @@ public class LocaleConfig implements Parcelable {
String defaultLocale = null;
if (android.content.res.Flags.defaultLocale()) {
// Read the defaultLocale attribute of the LocaleConfig element
- TypedArray att = res.obtainAttributes(
- attrs, com.android.internal.R.styleable.LocaleConfig);
- defaultLocale = att.getString(
- R.styleable.LocaleConfig_defaultLocale);
- att.recycle();
+ try (TypedArray att = res.obtainAttributes(
+ attrs, com.android.internal.R.styleable.LocaleConfig)) {
+ defaultLocale = att.getString(
+ R.styleable.LocaleConfig_defaultLocale);
+ }
}
Set<String> localeNames = new HashSet<>();
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
if (TAG_LOCALE.equals(parser.getName())) {
- final TypedArray attributes = res.obtainAttributes(
- attrs, com.android.internal.R.styleable.LocaleConfig_Locale);
- String nameAttr = attributes.getString(
- com.android.internal.R.styleable.LocaleConfig_Locale_name);
- localeNames.add(nameAttr);
- attributes.recycle();
+ try (TypedArray attributes = res.obtainAttributes(
+ attrs, com.android.internal.R.styleable.LocaleConfig_Locale)) {
+ String nameAttr = attributes.getString(
+ com.android.internal.R.styleable.LocaleConfig_Locale_name);
+ localeNames.add(nameAttr);
+ }
} else {
XmlUtils.skipCurrentTag(parser);
}
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index 6f0eafe487af..d764c58b2b1e 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -385,3 +385,13 @@ flag {
description: "Shows summarized notifications in the UI"
bug: "390217880"
}
+
+flag {
+ name: "nm_collapsed_lines"
+ namespace: "systemui"
+ description: "Shows 2 lines for collapsed notifications by default"
+ bug: "390217880"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index b1ea6e9b68eb..219b20428d7a 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2628,15 +2628,6 @@ public class PackageParser {
return Build.VERSION_CODES.CUR_DEVELOPMENT;
}
- // STOPSHIP: hack for the pre-release SDK
- if (platformSdkCodenames.length == 0
- && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
- targetCode)) {
- Slog.w(TAG, "Package requires development platform " + targetCode
- + ", returning current version " + Build.VERSION.SDK_INT);
- return Build.VERSION.SDK_INT;
- }
-
// Otherwise, we're looking at an incompatible pre-release SDK.
if (platformSdkCodenames.length > 0) {
outError[0] = "Requires development platform " + targetCode
@@ -2708,15 +2699,6 @@ public class PackageParser {
return Build.VERSION_CODES.CUR_DEVELOPMENT;
}
- // STOPSHIP: hack for the pre-release SDK
- if (platformSdkCodenames.length == 0
- && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
- minCode)) {
- Slog.w(TAG, "Package requires min development platform " + minCode
- + ", returning current version " + Build.VERSION.SDK_INT);
- return Build.VERSION.SDK_INT;
- }
-
// Otherwise, we're looking at an incompatible pre-release SDK.
if (platformSdkCodenames.length > 0) {
outError[0] = "Requires development platform " + minCode
diff --git a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
index c7403c0ea98c..153dd9a93490 100644
--- a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
@@ -316,15 +316,6 @@ public class FrameworkParsingPackageUtils {
return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
}
- // STOPSHIP: hack for the pre-release SDK
- if (platformSdkCodenames.length == 0
- && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
- minCode)) {
- Slog.w(TAG, "Parsed package requires min development platform " + minCode
- + ", returning current version " + Build.VERSION.SDK_INT);
- return input.success(Build.VERSION.SDK_INT);
- }
-
// Otherwise, we're looking at an incompatible pre-release SDK.
if (platformSdkCodenames.length > 0) {
return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
@@ -377,27 +368,19 @@ public class FrameworkParsingPackageUtils {
return input.success(targetVers);
}
- // If it's a pre-release SDK and the codename matches this platform, it
- // definitely targets this SDK.
- if (matchTargetCode(platformSdkCodenames, targetCode)) {
- return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
- }
-
- // STOPSHIP: hack for the pre-release SDK
- if (platformSdkCodenames.length == 0
- && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
- targetCode)) {
- Slog.w(TAG, "Parsed package requires development platform " + targetCode
- + ", returning current version " + Build.VERSION.SDK_INT);
- return input.success(Build.VERSION.SDK_INT);
- }
-
try {
if (allowUnknownCodenames && UnboundedSdkLevel.isAtMost(targetCode)) {
return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
}
} catch (IllegalArgumentException e) {
- return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, "Bad package SDK");
+ // isAtMost() throws it when encountering an older SDK codename
+ return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, e.getMessage());
+ }
+
+ // If it's a pre-release SDK and the codename matches this platform, it
+ // definitely targets this SDK.
+ if (matchTargetCode(platformSdkCodenames, targetCode)) {
+ return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
}
// Otherwise, we're looking at an incompatible pre-release SDK.
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 6e9dcf5a83a1..335448bf131e 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -79,6 +79,7 @@ import android.util.Log;
import android.util.Pair;
import android.util.Size;
import android.view.Display;
+import android.window.DesktopModeFlags;
import com.android.internal.camera.flags.Flags;
import com.android.internal.util.ArrayUtils;
@@ -1685,7 +1686,7 @@ public final class CameraManager {
*/
public static int getRotationOverride(@Nullable Context context,
@Nullable PackageManager packageManager, @Nullable String packageName) {
- if (com.android.window.flags.Flags.enableCameraCompatForDesktopWindowing()) {
+ if (DesktopModeFlags.ENABLE_CAMERA_COMPAT_SIMULATE_REQUESTED_ORIENTATION.isTrue()) {
return getRotationOverrideInternal(context, packageManager, packageName);
} else {
return shouldOverrideToPortrait(packageManager, packageName)
diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig
index 6c2ce3685b30..c41a5ce02e61 100644
--- a/core/java/android/hardware/input/input_framework.aconfig
+++ b/core/java/android/hardware/input/input_framework.aconfig
@@ -213,3 +213,12 @@ flag {
is_fixed_read_only: true
}
+flag {
+ name: "fix_search_modifier_fallbacks"
+ namespace: "input"
+ description: "Fixes a bug in which fallbacks from Search based key combinations were not activating."
+ bug: "384113980"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/hardware/serial/flags/flags.aconfig b/core/java/android/hardware/serial/flags/flags.aconfig
index d8244ba55fcc..bdb8b40af2bf 100644
--- a/core/java/android/hardware/serial/flags/flags.aconfig
+++ b/core/java/android/hardware/serial/flags/flags.aconfig
@@ -1,4 +1,4 @@
-package: "android.hardware.serial"
+package: "android.hardware.serial.flags"
container: "system"
flag {
diff --git a/core/java/android/net/VpnManager.java b/core/java/android/net/VpnManager.java
index c50bc569de72..4ef293a90a80 100644
--- a/core/java/android/net/VpnManager.java
+++ b/core/java/android/net/VpnManager.java
@@ -20,6 +20,7 @@ import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
import static com.android.internal.util.Preconditions.checkNotNull;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -32,6 +33,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
+import android.net.platform.flags.Flags;
import android.os.RemoteException;
import com.android.internal.net.LegacyVpnInfo;
@@ -85,13 +87,33 @@ public class VpnManager {
public static final int TYPE_VPN_LEGACY = 3;
/**
- * An VPN created by OEM code through other means than {@link VpnService} or {@link VpnManager}.
+ * A VPN created by OEM code through other means than {@link VpnService} or {@link VpnManager}.
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
public static final int TYPE_VPN_OEM = 4;
/**
+ * A VPN created by OEM code using {@link VpnService}, and which OEM code desires to
+ * differentiate from other VPN types. The core networking stack will treat this VPN type
+ * similarly to {@link #TYPE_VPN_SERVICE}.
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_VPN_TYPE_OEM_SERVICE_AND_LEGACY)
+ @SystemApi(client = MODULE_LIBRARIES)
+ public static final int TYPE_VPN_OEM_SERVICE = 5;
+
+ /**
+ * A VPN created by OEM code using the legacy VPN mechanisms, and which OEM code desires to
+ * differentiate from other VPN types. The core networking stack will treat this VPN type
+ * similarly to {@link #TYPE_VPN_LEGACY}.
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_VPN_TYPE_OEM_SERVICE_AND_LEGACY)
+ @SystemApi(client = MODULE_LIBRARIES)
+ public static final int TYPE_VPN_OEM_LEGACY = 6;
+
+ /**
* Channel for VPN notifications.
* @hide
*/
@@ -308,7 +330,7 @@ public class VpnManager {
/** @hide */
@IntDef(value = {TYPE_VPN_NONE, TYPE_VPN_SERVICE, TYPE_VPN_PLATFORM, TYPE_VPN_LEGACY,
- TYPE_VPN_OEM})
+ TYPE_VPN_OEM, TYPE_VPN_OEM_SERVICE, TYPE_VPN_OEM_LEGACY})
@Retention(RetentionPolicy.SOURCE)
public @interface VpnType {}
diff --git a/core/java/android/net/flags.aconfig b/core/java/android/net/flags.aconfig
index 8d12b76e23ff..519729bc1c88 100644
--- a/core/java/android/net/flags.aconfig
+++ b/core/java/android/net/flags.aconfig
@@ -37,3 +37,11 @@ flag {
description: "Flag for MDNS quality, reliability and performance improvement in 25Q2"
bug: "373270045"
}
+
+flag {
+ name: "vpn_type_oem_service_and_legacy"
+ namespace: "android_core_networking"
+ is_exported: false
+ description: "Flags the TYPE_VPN_OEM_SERVICE and TYPE_VPN_OEM_LEGACY VpnManager API constants"
+ bug: "389829981"
+}
diff --git a/core/java/android/os/CombinedMessageQueue/MessageQueue.java b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
index c21959b16fbb..c3c28b20d649 100644
--- a/core/java/android/os/CombinedMessageQueue/MessageQueue.java
+++ b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
@@ -34,8 +34,6 @@ import android.util.Printer;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
-import com.android.internal.ravenwood.RavenwoodEnvironment;
-
import dalvik.annotation.optimization.NeverCompile;
import java.io.FileDescriptor;
@@ -1121,7 +1119,6 @@ public final class MessageQueue {
msg.markInUse();
msg.arg1 = token;
- incAndTraceMessageCount(msg, when);
if (!enqueueMessageUnchecked(msg, when)) {
Log.wtf(TAG_C, "Unexpected error while adding sync barrier!");
@@ -1137,7 +1134,6 @@ public final class MessageQueue {
msg.markInUse();
msg.when = when;
msg.arg1 = token;
- incAndTraceMessageCount(msg, when);
if (Flags.messageQueueTailTracking() && mLast != null && mLast.when <= when) {
/* Message goes to tail of list */
diff --git a/core/java/android/os/PerfettoTrace.java b/core/java/android/os/PerfettoTrace.java
index 932836f8a050..8b96b89c949a 100644
--- a/core/java/android/os/PerfettoTrace.java
+++ b/core/java/android/os/PerfettoTrace.java
@@ -232,7 +232,8 @@ public final class PerfettoTrace {
* @param eventName The event name to appear in the trace.
*/
public static PerfettoTrackEventExtra.Builder instant(Category category, String eventName) {
- return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_INSTANT, category)
+ return PerfettoTrackEventExtra.builder(category.isEnabled())
+ .init(PERFETTO_TE_TYPE_INSTANT, category)
.setEventName(eventName);
}
@@ -243,7 +244,8 @@ public final class PerfettoTrace {
* @param eventName The event name to appear in the trace.
*/
public static PerfettoTrackEventExtra.Builder begin(Category category, String eventName) {
- return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_SLICE_BEGIN, category)
+ return PerfettoTrackEventExtra.builder(category.isEnabled())
+ .init(PERFETTO_TE_TYPE_SLICE_BEGIN, category)
.setEventName(eventName);
}
@@ -253,7 +255,8 @@ public final class PerfettoTrace {
* @param category The perfetto category.
*/
public static PerfettoTrackEventExtra.Builder end(Category category) {
- return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_SLICE_END, category);
+ return PerfettoTrackEventExtra.builder(category.isEnabled())
+ .init(PERFETTO_TE_TYPE_SLICE_END, category);
}
/**
@@ -263,7 +266,8 @@ public final class PerfettoTrace {
* @param value The value of the counter.
*/
public static PerfettoTrackEventExtra.Builder counter(Category category, long value) {
- return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_COUNTER, category)
+ return PerfettoTrackEventExtra.builder(category.isEnabled())
+ .init(PERFETTO_TE_TYPE_COUNTER, category)
.setCounter(value);
}
@@ -286,7 +290,8 @@ public final class PerfettoTrace {
* @param value The value of the counter.
*/
public static PerfettoTrackEventExtra.Builder counter(Category category, double value) {
- return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_COUNTER, category)
+ return PerfettoTrackEventExtra.builder(category.isEnabled())
+ .init(PERFETTO_TE_TYPE_COUNTER, category)
.setCounter(value);
}
diff --git a/core/java/android/os/PerfettoTrackEventExtra.java b/core/java/android/os/PerfettoTrackEventExtra.java
index 07b44a87ef88..5fc7cf7be246 100644
--- a/core/java/android/os/PerfettoTrackEventExtra.java
+++ b/core/java/android/os/PerfettoTrackEventExtra.java
@@ -37,6 +37,7 @@ import java.util.function.Supplier;
public final class PerfettoTrackEventExtra {
private static final boolean DEBUG = false;
private static final int DEFAULT_EXTRA_CACHE_SIZE = 5;
+ private static final Builder NO_OP_BUILDER = new Builder(/* extra= */ null, /* isCategoryEnabled= */ false);
private static final ThreadLocal<PerfettoTrackEventExtra> sTrackEventExtra =
new ThreadLocal<PerfettoTrackEventExtra>() {
@Override
@@ -153,8 +154,8 @@ public final class PerfettoTrackEventExtra {
private Builder mParent;
private FieldContainer mCurrentContainer;
- private boolean mIsCategoryEnabled;
+ private final boolean mIsCategoryEnabled;
private final CounterInt64 mCounterInt64;
private final CounterDouble mCounterDouble;
private final Proto mProto;
@@ -175,24 +176,29 @@ public final class PerfettoTrackEventExtra {
private final Pool<Builder> mBuilderCache;
private Builder() {
- mExtra = sTrackEventExtra.get();
- mNamedTrackCache = mExtra.mNamedTrackCache;
- mCounterTrackCache = mExtra.mCounterTrackCache;
- mArgInt64Cache = mExtra.mArgInt64Cache;
- mArgDoubleCache = mExtra.mArgDoubleCache;
- mArgBoolCache = mExtra.mArgBoolCache;
- mArgStringCache = mExtra.mArgStringCache;
- mFieldInt64Cache = mExtra.mFieldInt64Cache;
- mFieldDoubleCache = mExtra.mFieldDoubleCache;
- mFieldStringCache = mExtra.mFieldStringCache;
- mFieldNestedCache = mExtra.mFieldNestedCache;
- mBuilderCache = mExtra.mBuilderCache;
-
- mCounterInt64 = mExtra.getCounterInt64();
- mCounterDouble = mExtra.getCounterDouble();
- mProto = mExtra.getProto();
- mFlow = mExtra.getFlow();
- mTerminatingFlow = mExtra.getTerminatingFlow();
+ this(sTrackEventExtra.get(), true);
+ }
+
+ public Builder(PerfettoTrackEventExtra extra, boolean isCategoryEnabled) {
+ mIsCategoryEnabled = isCategoryEnabled;
+ mExtra = extra;
+ mNamedTrackCache = mExtra == null ? null : mExtra.mNamedTrackCache;
+ mCounterTrackCache = mExtra == null ? null : mExtra.mCounterTrackCache;
+ mArgInt64Cache = mExtra == null ? null : mExtra.mArgInt64Cache;
+ mArgDoubleCache = mExtra == null ? null : mExtra.mArgDoubleCache;
+ mArgBoolCache = mExtra == null ? null : mExtra.mArgBoolCache;
+ mArgStringCache = mExtra == null ? null : mExtra.mArgStringCache;
+ mFieldInt64Cache = mExtra == null ? null : mExtra.mFieldInt64Cache;
+ mFieldDoubleCache = mExtra == null ? null : mExtra.mFieldDoubleCache;
+ mFieldStringCache = mExtra == null ? null : mExtra.mFieldStringCache;
+ mFieldNestedCache = mExtra == null ? null : mExtra.mFieldNestedCache;
+ mBuilderCache = mExtra == null ? null : mExtra.mBuilderCache;
+
+ mCounterInt64 = mExtra == null ? null : mExtra.getCounterInt64();
+ mCounterDouble = mExtra == null ? null : mExtra.getCounterDouble();
+ mProto = mExtra == null ? null : mExtra.getProto();
+ mFlow = mExtra == null ? null : mExtra.getFlow();
+ mTerminatingFlow = mExtra == null ? null : mExtra.getTerminatingFlow();
}
/**
@@ -214,6 +220,10 @@ public final class PerfettoTrackEventExtra {
* Initialize the builder for a new trace event.
*/
public Builder init(int traceType, PerfettoTrace.Category category) {
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
+
mTraceType = traceType;
mCategory = category;
mEventName = "";
@@ -225,7 +235,7 @@ public final class PerfettoTrackEventExtra {
mExtra.reset();
// Reset after on init in case the thread created builders without calling emit
- return initInternal(this, null, category.isEnabled());
+ return initInternal(this, null);
}
/**
@@ -529,7 +539,7 @@ public final class PerfettoTrackEventExtra {
}
mProto.clearFields();
mExtra.addPerfettoPointer(mProto);
- return mBuilderCache.get(sBuilderSupplier).initInternal(this, mProto, true);
+ return mBuilderCache.get(sBuilderSupplier).initInternal(this, mProto);
}
/**
@@ -560,7 +570,7 @@ public final class PerfettoTrackEventExtra {
FieldNested field = mFieldNestedCache.get(sFieldNestedSupplier);
field.setId(id);
mExtra.addPerfettoPointer(mCurrentContainer, field);
- return mBuilderCache.get(sBuilderSupplier).initInternal(this, field, true);
+ return mBuilderCache.get(sBuilderSupplier).initInternal(this, field);
}
/**
@@ -577,11 +587,9 @@ public final class PerfettoTrackEventExtra {
}
- private Builder initInternal(Builder parent, FieldContainer field,
- boolean isCategoryEnabled) {
+ private Builder initInternal(Builder parent, FieldContainer field) {
mParent = parent;
mCurrentContainer = field;
- mIsCategoryEnabled = isCategoryEnabled;
mIsBuilt = false;
return this;
@@ -613,9 +621,12 @@ public final class PerfettoTrackEventExtra {
/**
* Start a {@link Builder} to build a {@link PerfettoTrackEventExtra}.
*/
- public static Builder builder() {
- return sTrackEventExtra.get().mBuilderCache.get(sBuilderSupplier).initInternal(null, null,
- false);
+ public static Builder builder(boolean isCategoryEnabled) {
+ if (isCategoryEnabled) {
+ return sTrackEventExtra.get().mBuilderCache.get(sBuilderSupplier)
+ .initInternal(null, null);
+ }
+ return NO_OP_BUILDER;
}
private final RingBuffer<NamedTrack> mNamedTrackCache =
diff --git a/core/java/android/os/image/flags/trade_in_mode_flags.aconfig b/core/java/android/os/image/flags/trade_in_mode_flags.aconfig
index e2e56ef70d62..c1adfe50db9e 100644
--- a/core/java/android/os/image/flags/trade_in_mode_flags.aconfig
+++ b/core/java/android/os/image/flags/trade_in_mode_flags.aconfig
@@ -9,3 +9,12 @@ flag {
bug: "332683751"
is_fixed_read_only: true
}
+
+flag {
+ name: "trade_in_mode_2025q4"
+ is_exported: true
+ namespace: "phoenix"
+ description: "Enable Trade-in Mode 2025Q4 changes"
+ bug: "397154502"
+ is_fixed_read_only: true
+}
diff --git a/core/java/android/util/ArrayMap.java b/core/java/android/util/ArrayMap.java
index 7ee0ff15c5ad..c59907937d6a 100644
--- a/core/java/android/util/ArrayMap.java
+++ b/core/java/android/util/ArrayMap.java
@@ -129,7 +129,7 @@ public final class ArrayMap<K, V> implements Map<K, V> {
return ContainerHelpers.binarySearch(hashes, N, hash);
} catch (ArrayIndexOutOfBoundsException e) {
if (CONCURRENT_MODIFICATION_EXCEPTIONS) {
- throw new ConcurrentModificationException();
+ throw new ConcurrentModificationException(e);
} else {
throw e; // the cache is poisoned at this point, there's not much we can do
}
diff --git a/core/java/android/view/OrientationEventListener.java b/core/java/android/view/OrientationEventListener.java
index 2feb44ffc3ae..892b80bd0213 100644
--- a/core/java/android/view/OrientationEventListener.java
+++ b/core/java/android/view/OrientationEventListener.java
@@ -26,8 +26,7 @@ import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.util.Log;
-
-import com.android.window.flags.Flags;
+import android.window.DesktopModeFlags;
/**
* Helper class for receiving notifications from the SensorManager when
@@ -77,9 +76,10 @@ public abstract class OrientationEventListener {
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
if (mSensor != null) {
// Create listener only if sensors do exist.
- mSensorEventListener = Flags.enableCameraCompatForDesktopWindowing()
- ? new CompatSensorEventListenerImpl(new SensorEventListenerImpl())
- : new SensorEventListenerImpl();
+ mSensorEventListener =
+ DesktopModeFlags.ENABLE_CAMERA_COMPAT_SIMULATE_REQUESTED_ORIENTATION.isTrue()
+ ? new CompatSensorEventListenerImpl(new SensorEventListenerImpl())
+ : new SensorEventListenerImpl();
}
}
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 97cf8fc748e8..8944c3fa0b19 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -120,7 +120,7 @@ public class WindowManagerImpl implements WindowManager {
this(context, null /* parentWindow */, null /* clientToken */);
}
- private WindowManagerImpl(Context context, Window parentWindow,
+ public WindowManagerImpl(Context context, Window parentWindow,
@Nullable IBinder windowContextToken) {
mContext = context;
mParentWindow = parentWindow;
diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java
index 536b81f77174..4f7eef007a68 100644
--- a/core/java/android/widget/DatePickerCalendarDelegate.java
+++ b/core/java/android/widget/DatePickerCalendarDelegate.java
@@ -34,6 +34,7 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.DayPickerView.OnDaySelectedListener;
import android.widget.YearPickerView.OnYearSelectedListener;
@@ -76,10 +77,6 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate {
private DayPickerView mDayPickerView;
private YearPickerView mYearPickerView;
- // Accessibility strings.
- private String mSelectDay;
- private String mSelectYear;
-
private int mCurrentView = UNINITIALIZED;
private final Calendar mTempDate;
@@ -118,8 +115,15 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate {
final ViewGroup header = mContainer.findViewById(R.id.date_picker_header);
mHeaderYear = header.findViewById(R.id.date_picker_header_year);
mHeaderYear.setOnClickListener(mOnHeaderClickListener);
+ mHeaderYear.setAccessibilityDelegate(
+ new ClickActionDelegate(context, R.string.select_year));
+ mHeaderYear.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE);
+
mHeaderMonthDay = header.findViewById(R.id.date_picker_header_date);
mHeaderMonthDay.setOnClickListener(mOnHeaderClickListener);
+ mHeaderMonthDay.setAccessibilityDelegate(
+ new ClickActionDelegate(context, R.string.select_day));
+ mHeaderMonthDay.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE);
// For the sake of backwards compatibility, attempt to extract the text
// color from the header month text appearance. If it's set, we'll let
@@ -170,10 +174,6 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate {
mYearPickerView.setYear(mCurrentDate.get(Calendar.YEAR));
mYearPickerView.setOnYearSelectedListener(mOnYearSelectedListener);
- // Set up content descriptions.
- mSelectDay = res.getString(R.string.select_day);
- mSelectYear = res.getString(R.string.select_year);
-
// Initialize for current locale. This also initializes the date, so no
// need to call onDateChanged.
onLocaleChanged(mCurrentLocale);
@@ -230,6 +230,22 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate {
return srcRgb | (dstAlpha << 24);
}
+ private static class ClickActionDelegate extends View.AccessibilityDelegate {
+ private final AccessibilityNodeInfo.AccessibilityAction mClickAction;
+
+ ClickActionDelegate(Context context, int resId) {
+ mClickAction = new AccessibilityNodeInfo.AccessibilityAction(
+ AccessibilityNodeInfo.ACTION_CLICK, context.getString(resId));
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+
+ info.addAction(mClickAction);
+ }
+ }
+
/**
* Listener called when the user selects a day in the day picker view.
*/
@@ -310,10 +326,10 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate {
mYearFormat = DateFormat.getInstanceForSkeleton("y", locale);
// Update the header text.
- onCurrentDateChanged(false);
+ onCurrentDateChanged();
}
- private void onCurrentDateChanged(boolean announce) {
+ private void onCurrentDateChanged() {
if (mHeaderYear == null) {
// Abort, we haven't initialized yet. This method will get called
// again later after everything has been set up.
@@ -325,11 +341,6 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate {
final String monthDay = mMonthDayFormat.format(mCurrentDate.getTime());
mHeaderMonthDay.setText(monthDay);
-
- // TODO: This should use live regions.
- if (announce) {
- mAnimator.announceForAccessibility(getFormattedCurrentDate());
- }
}
private void setCurrentView(final int viewIndex) {
@@ -343,8 +354,6 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate {
mAnimator.setDisplayedChild(VIEW_MONTH_DAY);
mCurrentView = viewIndex;
}
-
- mAnimator.announceForAccessibility(mSelectDay);
break;
case VIEW_YEAR:
final int year = mCurrentDate.get(Calendar.YEAR);
@@ -364,7 +373,6 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate {
mCurrentView = viewIndex;
}
- mAnimator.announceForAccessibility(mSelectYear);
break;
}
}
@@ -409,7 +417,7 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate {
mDayPickerView.setDate(mCurrentDate.getTimeInMillis());
mYearPickerView.setYear(year);
- onCurrentDateChanged(fromUser);
+ onCurrentDateChanged();
if (fromUser) {
tryVibrate();
@@ -564,7 +572,7 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate {
mMinDate.setTimeInMillis(ss.getMinDate());
mMaxDate.setTimeInMillis(ss.getMaxDate());
- onCurrentDateChanged(false);
+ onCurrentDateChanged();
final int currentView = ss.getCurrentView();
setCurrentView(currentView);
diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java
index a453c2818566..ebb0f99e988b 100644
--- a/core/java/android/widget/TimePickerClockDelegate.java
+++ b/core/java/android/widget/TimePickerClockDelegate.java
@@ -121,12 +121,8 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
// Localization data.
private boolean mHourFormatShowLeadingZero;
- private boolean mHourFormatStartsAtZero;
-
- // Most recent time announcement values for accessibility.
- private CharSequence mLastAnnouncedText;
- private boolean mLastAnnouncedIsHour;
+ private boolean mHourFormatStartsAtZero;
public TimePickerClockDelegate(TimePicker delegator, Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
super(delegator, context);
@@ -155,6 +151,7 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
mHourView.setOnDigitEnteredListener(mDigitEnteredListener);
mHourView.setAccessibilityDelegate(
new ClickActionDelegate(context, R.string.select_hours));
+ mHourView.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE);
mSeparatorView = (TextView) mainView.findViewById(R.id.separator);
mMinuteView = (NumericTextView) mainView.findViewById(R.id.minutes);
mMinuteView.setOnClickListener(mClickListener);
@@ -162,6 +159,7 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
mMinuteView.setOnDigitEnteredListener(mDigitEnteredListener);
mMinuteView.setAccessibilityDelegate(
new ClickActionDelegate(context, R.string.select_minutes));
+ mMinuteView.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE);
mMinuteView.setRange(0, 59);
// Set up AM/PM labels.
@@ -435,7 +433,7 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
private void updateRadialPicker(int index) {
mRadialTimePickerView.initialize(mCurrentHour, mCurrentMinute, mIs24Hour);
- setCurrentItemShowing(index, false, true);
+ setCurrentItemShowing(index, false);
}
private void updateHeaderAmPm() {
@@ -786,18 +784,10 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
private void updateHeaderHour(int hourOfDay, boolean announce) {
final int localizedHour = getLocalizedHour(hourOfDay);
mHourView.setValue(localizedHour);
-
- if (announce) {
- tryAnnounceForAccessibility(mHourView.getText(), true);
- }
}
private void updateHeaderMinute(int minuteOfHour, boolean announce) {
mMinuteView.setValue(minuteOfHour);
-
- if (announce) {
- tryAnnounceForAccessibility(mMinuteView.getText(), false);
- }
}
/**
@@ -876,31 +866,12 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
return -1;
}
- private void tryAnnounceForAccessibility(CharSequence text, boolean isHour) {
- if (mLastAnnouncedIsHour != isHour || !text.equals(mLastAnnouncedText)) {
- // TODO: Find a better solution, potentially live regions?
- mDelegator.announceForAccessibility(text);
- mLastAnnouncedText = text;
- mLastAnnouncedIsHour = isHour;
- }
- }
-
/**
* Show either Hours or Minutes.
*/
- private void setCurrentItemShowing(int index, boolean animateCircle, boolean announce) {
+ private void setCurrentItemShowing(int index, boolean animateCircle) {
mRadialTimePickerView.setCurrentItemShowing(index, animateCircle);
- if (index == HOUR_INDEX) {
- if (announce) {
- mDelegator.announceForAccessibility(mSelectHours);
- }
- } else {
- if (announce) {
- mDelegator.announceForAccessibility(mSelectMinutes);
- }
- }
-
mHourView.setActivated(index == HOUR_INDEX);
mMinuteView.setActivated(index == MINUTE_INDEX);
}
@@ -930,10 +901,7 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
final boolean isTransition = mAllowAutoAdvance && autoAdvance;
setHourInternal(newValue, FROM_RADIAL_PICKER, !isTransition, true);
if (isTransition) {
- setCurrentItemShowing(MINUTE_INDEX, true, false);
-
- final int localizedHour = getLocalizedHour(newValue);
- mDelegator.announceForAccessibility(localizedHour + ". " + mSelectMinutes);
+ setCurrentItemShowing(MINUTE_INDEX, true);
}
break;
case RadialTimePickerView.MINUTES:
@@ -1030,10 +998,10 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
setAmOrPm(PM);
break;
case R.id.hours:
- setCurrentItemShowing(HOUR_INDEX, true, true);
+ setCurrentItemShowing(HOUR_INDEX, true);
break;
case R.id.minutes:
- setCurrentItemShowing(MINUTE_INDEX, true, true);
+ setCurrentItemShowing(MINUTE_INDEX, true);
break;
default:
// Failed to handle this click, don't vibrate.
@@ -1058,10 +1026,10 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
setAmOrPm(PM);
break;
case R.id.hours:
- setCurrentItemShowing(HOUR_INDEX, true, true);
+ setCurrentItemShowing(HOUR_INDEX, true);
break;
case R.id.minutes:
- setCurrentItemShowing(MINUTE_INDEX, true, true);
+ setCurrentItemShowing(MINUTE_INDEX, true);
break;
default:
// Failed to handle this click, don't vibrate.
diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java
index 4bd7d1679dcc..d2893a46d3f3 100644
--- a/core/java/android/window/DesktopModeFlags.java
+++ b/core/java/android/window/DesktopModeFlags.java
@@ -48,6 +48,8 @@ public enum DesktopModeFlags {
DISABLE_NON_RESIZABLE_APP_SNAP_RESIZE(Flags::disableNonResizableAppSnapResizing, true),
ENABLE_ACCESSIBLE_CUSTOM_HEADERS(Flags::enableAccessibleCustomHeaders, true),
ENABLE_APP_HEADER_WITH_TASK_DENSITY(Flags::enableAppHeaderWithTaskDensity, true),
+ ENABLE_CAMERA_COMPAT_SIMULATE_REQUESTED_ORIENTATION(
+ Flags::enableCameraCompatForDesktopWindowing, true),
ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION(Flags::enableCaptionCompatInsetForceConsumption,
true),
ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS(
@@ -103,7 +105,7 @@ public enum DesktopModeFlags {
ENABLE_DESKTOP_WINDOWING_TASK_LIMIT(Flags::enableDesktopWindowingTaskLimit, true),
ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY(Flags::enableDesktopWindowingWallpaperActivity,
true),
- ENABLE_DRAG_RESIZE_SET_UP_IN_BG_THREAD(Flags::enableDragResizeSetUpInBgThread, false),
+ ENABLE_DRAG_RESIZE_SET_UP_IN_BG_THREAD(Flags::enableDragResizeSetUpInBgThread, true),
ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX(
Flags::enableDragToDesktopIncomingTransitionsBugfix, false),
ENABLE_FULLY_IMMERSIVE_IN_DESKTOP(Flags::enableFullyImmersiveInDesktop, true),
diff --git a/core/java/com/android/internal/jank/DisplayResolutionTracker.java b/core/java/com/android/internal/jank/DisplayResolutionTracker.java
index 5d66b3c10197..74153157df4d 100644
--- a/core/java/com/android/internal/jank/DisplayResolutionTracker.java
+++ b/core/java/com/android/internal/jank/DisplayResolutionTracker.java
@@ -142,14 +142,23 @@ public class DisplayResolutionTracker {
public interface DisplayInterface {
/** Reurns an implementation wrapping {@link DisplayManagerGlobal}. */
static DisplayInterface getDefault(@Nullable Handler handler) {
+ long displayEventsToBeSubscribed;
+ if (com.android.server.display.feature.flags.Flags
+ .displayListenerPerformanceImprovements()
+ && com.android.server.display.feature.flags.Flags
+ .delayImplicitRrRegistrationUntilRrAccessed()) {
+ displayEventsToBeSubscribed = DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED;
+ } else {
+ displayEventsToBeSubscribed = DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE;
+ }
DisplayManagerGlobal manager = DisplayManagerGlobal.getInstance();
return new DisplayInterface() {
@Override
public void registerDisplayListener(DisplayManager.DisplayListener listener) {
- manager.registerDisplayListener(listener, handler,
- DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED
- | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED
- | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE,
+ manager.registerDisplayListener(listener, handler, displayEventsToBeSubscribed,
ActivityThread.currentPackageName());
}
diff --git a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
index 6f7e5ad51b89..7cecc39dacde 100644
--- a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
@@ -66,6 +66,7 @@ import com.android.internal.protolog.IProtoLogConfigurationService.RegisterClien
import com.android.internal.protolog.common.ILogger;
import com.android.internal.protolog.common.IProtoLog;
import com.android.internal.protolog.common.IProtoLogGroup;
+import com.android.internal.protolog.common.InvalidFormatStringException;
import com.android.internal.protolog.common.LogDataType;
import com.android.internal.protolog.common.LogLevel;
@@ -207,7 +208,12 @@ public abstract class PerfettoProtoLogImpl extends IProtoLogClient.Stub implemen
@Override
public void log(LogLevel logLevel, IProtoLogGroup group, String messageString, Object... args) {
- log(logLevel, group, new Message(messageString), args);
+ try {
+ log(logLevel, group, new Message(messageString), args);
+ } catch (InvalidFormatStringException e) {
+ Slog.e(LOG_TAG, "Invalid protolog string format", e);
+ log(logLevel, group, new Message("INVALID MESSAGE"), new Object[0]);
+ }
}
/**
@@ -831,7 +837,7 @@ public abstract class PerfettoProtoLogImpl extends IProtoLogClient.Stub implemen
this.mMessageString = null;
}
- private Message(@NonNull String messageString) {
+ private Message(@NonNull String messageString) throws InvalidFormatStringException {
this.mMessageHash = null;
final List<Integer> argTypes = LogDataType.parseFormatString(messageString);
this.mMessageMask = LogDataType.logDataTypesToBitMask(argTypes);
diff --git a/core/java/com/android/internal/protolog/common/LogDataType.java b/core/java/com/android/internal/protolog/common/LogDataType.java
index c05824a58a77..10b07d9387de 100644
--- a/core/java/com/android/internal/protolog/common/LogDataType.java
+++ b/core/java/com/android/internal/protolog/common/LogDataType.java
@@ -86,8 +86,8 @@ public class LogDataType {
case '%':
break;
default:
- throw new InvalidFormatStringException("Invalid format string field"
- + " %${messageString[i + 1]}");
+ throw new InvalidFormatStringException("Invalid Protolog message format in "
+ + "\"" + messageString + "\" at index " + i + ".");
}
i += 2;
} else {
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index 3da19220248b..822ef34ad925 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -401,9 +401,19 @@ public class ConversationLayout extends FrameLayout
@RemotableViewMethod(asyncImpl = "setIsCollapsedAsync")
public void setIsCollapsed(boolean isCollapsed) {
mIsCollapsed = isCollapsed;
- mMessagingLinearLayout.setMaxDisplayedLines(isCollapsed
- ? TextUtils.isEmpty(mSummarizedContent) ? 1 : MAX_SUMMARIZATION_LINES
- : Integer.MAX_VALUE);
+ int maxLines = Integer.MAX_VALUE;
+ if (isCollapsed) {
+ if (!TextUtils.isEmpty(mSummarizedContent)) {
+ maxLines = MAX_SUMMARIZATION_LINES;
+ } else {
+ if (android.app.Flags.nmCollapsedLines()) {
+ maxLines = 2;
+ } else {
+ maxLines = 1;
+ }
+ }
+ }
+ mMessagingLinearLayout.setMaxDisplayedLines(maxLines);
updateExpandButton();
updateContentEndPaddings();
}
@@ -1177,7 +1187,9 @@ public class ConversationLayout extends FrameLayout
nameOverride = mNameReplacement;
}
newGroup.setShowingAvatar(!mIsOneToOne && !mIsCollapsed);
- newGroup.setSingleLine(mIsCollapsed && TextUtils.isEmpty(mSummarizedContent));
+ newGroup.setSingleLine(mIsCollapsed
+ ? !android.app.Flags.nmCollapsedLines() && TextUtils.isEmpty(mSummarizedContent)
+ : false);
newGroup.setIsCollapsed(mIsCollapsed);
newGroup.setSender(sender, nameOverride);
newGroup.setSending(groupIndex == (groups.size() - 1) && showSpinner);
diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java
index 4cc4b38f95a5..f9c8228e455d 100644
--- a/core/java/com/android/internal/widget/MessagingLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLayout.java
@@ -157,6 +157,10 @@ public class MessagingLayout extends FrameLayout
@RemotableViewMethod(asyncImpl = "setIsCollapsedAsync")
public void setIsCollapsed(boolean isCollapsed) {
mIsCollapsed = isCollapsed;
+ if (mIsCollapsed) {
+ mMessagingLinearLayout.setMaxDisplayedLines(
+ android.app.Flags.nmCollapsedLines() ? 2 : 1);
+ }
}
/**
@@ -549,7 +553,9 @@ public class MessagingLayout extends FrameLayout
if (sender != mUser && mNameReplacement != null) {
nameOverride = mNameReplacement;
}
- newGroup.setSingleLine(mIsCollapsed && TextUtils.isEmpty(mSummarizedContent));
+ newGroup.setSingleLine(mIsCollapsed
+ ? !android.app.Flags.nmCollapsedLines() && TextUtils.isEmpty(mSummarizedContent)
+ : false);
newGroup.setShowingAvatar(!mIsCollapsed);
newGroup.setIsCollapsed(mIsCollapsed);
newGroup.setSender(sender, nameOverride);
diff --git a/core/java/com/android/server/servicewatcher/OWNERS b/core/java/com/android/server/servicewatcher/OWNERS
index ced619f05f1d..dbc598e1547f 100644
--- a/core/java/com/android/server/servicewatcher/OWNERS
+++ b/core/java/com/android/server/servicewatcher/OWNERS
@@ -1,5 +1,5 @@
# Bug component: 25692
-sooniln@google.com
+dnchrist@google.com
wyattriley@google.com
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index bfa0aa9638a9..7ed73d7668b9 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -210,7 +210,6 @@ cc_library_shared_for_libandroid_runtime {
"android_media_AudioAttributes.cpp",
"android_media_AudioProductStrategies.cpp",
"android_media_AudioVolumeGroups.cpp",
- "android_media_AudioVolumeGroupCallback.cpp",
"android_media_DeviceCallback.cpp",
"android_media_MediaMetricsJNI.cpp",
"android_media_MicrophoneInfo.cpp",
@@ -311,6 +310,7 @@ cc_library_shared_for_libandroid_runtime {
"audioflinger-aidl-cpp",
"audiopolicy-types-aidl-cpp",
"spatializer-aidl-cpp",
+ "volumegroupcallback-aidl-cpp",
"av-types-aidl-cpp",
"android.hardware.camera.device@3.2",
"camera_platform_flags_c_lib",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index b2b826391e1d..1ff07745e904 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -101,7 +101,6 @@ extern int register_android_media_AudioTrack(JNIEnv *env);
extern int register_android_media_AudioAttributes(JNIEnv *env);
extern int register_android_media_AudioProductStrategies(JNIEnv *env);
extern int register_android_media_AudioVolumeGroups(JNIEnv *env);
-extern int register_android_media_AudioVolumeGroupChangeHandler(JNIEnv *env);
extern int register_android_media_ImageReader(JNIEnv *env);
extern int register_android_media_ImageWriter(JNIEnv *env);
extern int register_android_media_MicrophoneInfo(JNIEnv *env);
@@ -1660,7 +1659,6 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_media_AudioAttributes),
REG_JNI(register_android_media_AudioProductStrategies),
REG_JNI(register_android_media_AudioVolumeGroups),
- REG_JNI(register_android_media_AudioVolumeGroupChangeHandler),
REG_JNI(register_android_media_ImageReader),
REG_JNI(register_android_media_ImageWriter),
REG_JNI(register_android_media_MediaMetrics),
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index b679688959b1..1bbf811dc373 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -20,16 +20,17 @@
#include <atomic>
#define LOG_TAG "AudioSystem-JNI"
+#include <android-base/properties.h>
#include <android/binder_ibinder_jni.h>
#include <android/binder_libbinder.h>
#include <android/media/AudioVibratorInfo.h>
+#include <android/media/INativeAudioVolumeGroupCallback.h>
#include <android/media/INativeSpatializerCallback.h>
#include <android/media/ISpatializer.h>
#include <android/media/audio/common/AudioConfigBase.h>
#include <android_media_audiopolicy.h>
#include <android_os_Parcel.h>
#include <audiomanager/AudioManager.h>
-#include <android-base/properties.h>
#include <binder/IBinder.h>
#include <jni.h>
#include <media/AidlConversion.h>
@@ -41,14 +42,14 @@
#include <nativehelper/ScopedLocalRef.h>
#include <nativehelper/ScopedPrimitiveArray.h>
#include <nativehelper/jni_macros.h>
+#include <sys/system_properties.h>
#include <system/audio.h>
#include <system/audio_policy.h>
-#include <sys/system_properties.h>
#include <utils/Log.h>
+#include <memory>
#include <optional>
#include <sstream>
-#include <memory>
#include <vector>
#include "android_media_AudioAttributes.h"
@@ -59,8 +60,8 @@
#include "android_media_AudioFormat.h"
#include "android_media_AudioMixerAttributes.h"
#include "android_media_AudioProfile.h"
-#include "android_media_MicrophoneInfo.h"
#include "android_media_JNIUtils.h"
+#include "android_media_MicrophoneInfo.h"
#include "android_util_Binder.h"
#include "core_jni_helpers.h"
@@ -3442,6 +3443,21 @@ static void android_media_AudioSystem_triggerSystemPropertyUpdate(JNIEnv *env,
}
}
+static int android_media_AudioSystem_registerAudioVolumeGroupCallback(
+ JNIEnv *env, jobject thiz, jobject jIAudioVolumeGroupCallback) {
+ sp<media::INativeAudioVolumeGroupCallback> nIAudioVolumeGroupCallback =
+ interface_cast<media::INativeAudioVolumeGroupCallback>(
+ ibinderForJavaObject(env, jIAudioVolumeGroupCallback));
+ return AudioSystem::addAudioVolumeGroupCallback(nIAudioVolumeGroupCallback);
+}
+
+static int android_media_AudioSystem_unregisterAudioVolumeGroupCallback(
+ JNIEnv *env, jobject thiz, jobject jIAudioVolumeGroupCallback) {
+ sp<media::INativeAudioVolumeGroupCallback> nIAudioVolumeGroupCallback =
+ interface_cast<media::INativeAudioVolumeGroupCallback>(
+ ibinderForJavaObject(env, jIAudioVolumeGroupCallback));
+ return AudioSystem::removeAudioVolumeGroupCallback(nIAudioVolumeGroupCallback);
+}
// ----------------------------------------------------------------------------
@@ -3612,6 +3628,12 @@ static const JNINativeMethod gMethods[] = {
MAKE_JNI_NATIVE_METHOD("clearPreferredMixerAttributes",
"(Landroid/media/AudioAttributes;II)I",
android_media_AudioSystem_clearPreferredMixerAttributes),
+ MAKE_JNI_NATIVE_METHOD("registerAudioVolumeGroupCallback",
+ "(Landroid/media/INativeAudioVolumeGroupCallback;)I",
+ android_media_AudioSystem_registerAudioVolumeGroupCallback),
+ MAKE_JNI_NATIVE_METHOD("unregisterAudioVolumeGroupCallback",
+ "(Landroid/media/INativeAudioVolumeGroupCallback;)I",
+ android_media_AudioSystem_unregisterAudioVolumeGroupCallback),
MAKE_AUDIO_SYSTEM_METHOD(supportsBluetoothVariableLatency),
MAKE_AUDIO_SYSTEM_METHOD(setBluetoothVariableLatencyEnabled),
MAKE_AUDIO_SYSTEM_METHOD(isBluetoothVariableLatencyEnabled),
diff --git a/core/jni/android_media_AudioVolumeGroupCallback.cpp b/core/jni/android_media_AudioVolumeGroupCallback.cpp
deleted file mode 100644
index d130a4bc68fa..000000000000
--- a/core/jni/android_media_AudioVolumeGroupCallback.cpp
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#undef ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION // TODO:remove this and fix code
-
-//#define LOG_NDEBUG 0
-
-#define LOG_TAG "AudioVolumeGroupCallback-JNI"
-
-#include <utils/Log.h>
-#include <nativehelper/JNIHelp.h>
-#include "core_jni_helpers.h"
-
-#include "android_media_AudioVolumeGroupCallback.h"
-
-
-// ----------------------------------------------------------------------------
-using namespace android;
-
-static const char* const kAudioVolumeGroupChangeHandlerClassPathName =
- "android/media/audiopolicy/AudioVolumeGroupChangeHandler";
-
-static struct {
- jfieldID mJniCallback;
-} gAudioVolumeGroupChangeHandlerFields;
-
-static struct {
- jmethodID postEventFromNative;
-} gAudioVolumeGroupChangeHandlerMethods;
-
-static Mutex gLock;
-
-JNIAudioVolumeGroupCallback::JNIAudioVolumeGroupCallback(JNIEnv* env,
- jobject thiz,
- jobject weak_thiz)
-{
- jclass clazz = env->GetObjectClass(thiz);
- if (clazz == NULL) {
- ALOGE("Can't find class %s", kAudioVolumeGroupChangeHandlerClassPathName);
- return;
- }
- mClass = (jclass)env->NewGlobalRef(clazz);
-
- // We use a weak reference so the AudioVolumeGroupChangeHandler object can be garbage collected.
- // The reference is only used as a proxy for callbacks.
- mObject = env->NewGlobalRef(weak_thiz);
-}
-
-JNIAudioVolumeGroupCallback::~JNIAudioVolumeGroupCallback()
-{
- // remove global references
- JNIEnv *env = AndroidRuntime::getJNIEnv();
- if (env == NULL) {
- return;
- }
- env->DeleteGlobalRef(mObject);
- env->DeleteGlobalRef(mClass);
-}
-
-void JNIAudioVolumeGroupCallback::onAudioVolumeGroupChanged(volume_group_t group, int flags)
-{
- JNIEnv *env = AndroidRuntime::getJNIEnv();
- if (env == NULL) {
- return;
- }
- ALOGV("%s volume group id %d", __FUNCTION__, group);
- env->CallStaticVoidMethod(mClass,
- gAudioVolumeGroupChangeHandlerMethods.postEventFromNative,
- mObject,
- AUDIOVOLUMEGROUP_EVENT_VOLUME_CHANGED, group, flags, NULL);
- if (env->ExceptionCheck()) {
- ALOGW("An exception occurred while notifying an event.");
- env->ExceptionClear();
- }
-}
-
-void JNIAudioVolumeGroupCallback::onServiceDied()
-{
- JNIEnv *env = AndroidRuntime::getJNIEnv();
- if (env == NULL) {
- return;
- }
- env->CallStaticVoidMethod(mClass,
- gAudioVolumeGroupChangeHandlerMethods.postEventFromNative,
- mObject,
- AUDIOVOLUMEGROUP_EVENT_SERVICE_DIED, 0, 0, NULL);
- if (env->ExceptionCheck()) {
- ALOGW("An exception occurred while notifying an event.");
- env->ExceptionClear();
- }
-}
-
-static
-sp<JNIAudioVolumeGroupCallback> setJniCallback(JNIEnv* env,
- jobject thiz,
- const sp<JNIAudioVolumeGroupCallback>& callback)
-{
- Mutex::Autolock l(gLock);
- sp<JNIAudioVolumeGroupCallback> old = (JNIAudioVolumeGroupCallback*)env->GetLongField(
- thiz, gAudioVolumeGroupChangeHandlerFields.mJniCallback);
- if (callback.get()) {
- callback->incStrong((void*)setJniCallback);
- }
- if (old != 0) {
- old->decStrong((void*)setJniCallback);
- }
- env->SetLongField(thiz, gAudioVolumeGroupChangeHandlerFields.mJniCallback,
- (jlong)callback.get());
- return old;
-}
-
-static void
-android_media_AudioVolumeGroupChangeHandler_eventHandlerSetup(JNIEnv *env,
- jobject thiz,
- jobject weak_this)
-{
- ALOGV("%s", __FUNCTION__);
- sp<JNIAudioVolumeGroupCallback> callback =
- new JNIAudioVolumeGroupCallback(env, thiz, weak_this);
-
- if (AudioSystem::addAudioVolumeGroupCallback(callback) == NO_ERROR) {
- setJniCallback(env, thiz, callback);
- }
-}
-
-static void
-android_media_AudioVolumeGroupChangeHandler_eventHandlerFinalize(JNIEnv *env, jobject thiz)
-{
- ALOGV("%s", __FUNCTION__);
- sp<JNIAudioVolumeGroupCallback> callback = setJniCallback(env, thiz, 0);
- if (callback != 0) {
- AudioSystem::removeAudioVolumeGroupCallback(callback);
- }
-}
-
-/*
- * JNI registration.
- */
-static const JNINativeMethod gMethods[] = {
- {"native_setup", "(Ljava/lang/Object;)V",
- (void *)android_media_AudioVolumeGroupChangeHandler_eventHandlerSetup},
- {"native_finalize", "()V",
- (void *)android_media_AudioVolumeGroupChangeHandler_eventHandlerFinalize},
-};
-
-int register_android_media_AudioVolumeGroupChangeHandler(JNIEnv *env)
-{
- jclass audioVolumeGroupChangeHandlerClass =
- FindClassOrDie(env, kAudioVolumeGroupChangeHandlerClassPathName);
- gAudioVolumeGroupChangeHandlerMethods.postEventFromNative =
- GetStaticMethodIDOrDie(env, audioVolumeGroupChangeHandlerClass, "postEventFromNative",
- "(Ljava/lang/Object;IIILjava/lang/Object;)V");
-
- gAudioVolumeGroupChangeHandlerFields.mJniCallback =
- GetFieldIDOrDie(env, audioVolumeGroupChangeHandlerClass, "mJniCallback", "J");
-
- env->DeleteLocalRef(audioVolumeGroupChangeHandlerClass);
-
- return RegisterMethodsOrDie(env,
- kAudioVolumeGroupChangeHandlerClassPathName,
- gMethods,
- NELEM(gMethods));
-}
-
diff --git a/core/jni/android_media_AudioVolumeGroupCallback.h b/core/jni/android_media_AudioVolumeGroupCallback.h
deleted file mode 100644
index de06549621b9..000000000000
--- a/core/jni/android_media_AudioVolumeGroupCallback.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <system/audio.h>
-#include <media/AudioSystem.h>
-
-namespace android {
-
-// keep in sync with AudioManager.AudioVolumeGroupChangeHandler.java
-#define AUDIOVOLUMEGROUP_EVENT_VOLUME_CHANGED 1000
-#define AUDIOVOLUMEGROUP_EVENT_SERVICE_DIED 1001
-
-class JNIAudioVolumeGroupCallback: public AudioSystem::AudioVolumeGroupCallback
-{
-public:
- JNIAudioVolumeGroupCallback(JNIEnv* env, jobject thiz, jobject weak_thiz);
- ~JNIAudioVolumeGroupCallback();
-
- void onAudioVolumeGroupChanged(volume_group_t group, int flags) override;
- void onServiceDied() override;
-
-private:
- void sendEvent(int event);
-
- jclass mClass; /**< Reference to AudioVolumeGroupChangeHandler class. */
- jobject mObject; /**< Weak ref to AudioVolumeGroupChangeHandler object to call on. */
-};
-
-} // namespace android
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 1a74fe6719e3..7bb799a24ef1 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3209,6 +3209,10 @@
Note that HSUM devices without this enabled will not automatically have a main user. -->
<bool name="config_isMainUserPermanentAdmin">true</bool>
+ <!-- Whether all secondary users (and the system user) are eligible to have profiles.
+ If false, only the MainUser is eligible to have profiles. -->
+ <bool name="config_supportProfilesOnNonMainUser">false</bool>
+
<!-- Whether switch to headless system user is allowed. If allowed,
headless system user can run in the foreground even though it is not a full user. -->
<bool name="config_canSwitchToHeadlessSystemUser">false</bool>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a37ca2847638..2dc5687a1253 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -363,6 +363,7 @@
<java-symbol type="bool" name="config_speed_up_audio_on_mt_calls" />
<java-symbol type="bool" name="config_useFixedVolume" />
<java-symbol type="bool" name="config_isMainUserPermanentAdmin"/>
+ <java-symbol type="bool" name="config_supportProfilesOnNonMainUser"/>
<java-symbol type="bool" name="config_canSwitchToHeadlessSystemUser"/>
<java-symbol type="bool" name="config_enableMultiUserUI"/>
<java-symbol type="bool" name="config_enableMultipleAdmins"/>
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_restart.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_restart.xml
new file mode 100644
index 000000000000..d407884d3fcf
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_restart.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="20dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#1C1C14"
+ android:pathData="M6,13c0,-1.65 0.67,-3.15 1.76,-4.24L6.34,7.34C4.9,8.79 4,10.79 4,13c0,4.08 3.05,7.44 7,7.93v-2.02C8.17,18.43 6,15.97 6,13z"/>
+ <path
+ android:fillColor="#1C1C14"
+ android:pathData="M20,13c0,-4.42 -3.58,-8 -8,-8c-0.06,0 -0.12,0.01 -0.18,0.01v0l1.09,-1.09L11.5,2.5L8,6l3.5,3.5l1.41,-1.41l-1.08,-1.08C11.89,7.01 11.95,7 12,7c3.31,0 6,2.69 6,6c0,2.97 -2.17,5.43 -5,5.91v2.02C16.95,20.44 20,17.08 20,13z"/>
+</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_windowing_transition_background.xml b/libs/WindowManager/Shell/res/drawable/desktop_windowing_transition_background.xml
index dd1a1b1dca13..75ec2ab9f6f9 100644
--- a/libs/WindowManager/Shell/res/drawable/desktop_windowing_transition_background.xml
+++ b/libs/WindowManager/Shell/res/drawable/desktop_windowing_transition_background.xml
@@ -18,15 +18,15 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<item android:id="@+id/indicator_solid">
<shape android:shape="rectangle">
- <solid android:color="@androidprv:color/materialColorPrimaryContainer" />
+ <solid android:color="@androidprv:color/materialColorPrimaryFixed" />
<corners android:radius="28dp" />
</shape>
</item>
<item android:id="@+id/indicator_stroke">
<shape android:shape="rectangle">
<corners android:radius="28dp" />
- <stroke android:width="1dp"
- android:color="@androidprv:color/materialColorPrimaryContainer"/>
+ <stroke android:width="2dp"
+ android:color="@androidprv:color/materialColorPrimaryFixed"/>
</shape>
</item>
</layer-list>
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
index c33669636be4..30acf1ac6eda 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
@@ -163,6 +163,13 @@
android:text="@string/change_aspect_ratio_text"
android:src="@drawable/desktop_mode_ic_handle_menu_change_aspect_ratio"
style="@style/DesktopModeHandleMenuActionButton"/>
+
+ <com.android.wm.shell.windowdecor.HandleMenuActionButton
+ android:id="@+id/handle_menu_restart_button"
+ android:contentDescription="@string/handle_menu_restart_text"
+ android:text="@string/handle_menu_restart_text"
+ android:src="@drawable/desktop_mode_ic_handle_menu_restart"
+ style="@style/DesktopModeHandleMenuActionButton"/>
</LinearLayout>
<LinearLayout
diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml
index 8d18f959951b..5732fc936b47 100644
--- a/libs/WindowManager/Shell/res/values/colors.xml
+++ b/libs/WindowManager/Shell/res/values/colors.xml
@@ -68,8 +68,6 @@
<color name="desktop_mode_caption_button_on_hover_light">#11000000</color>
<color name="desktop_mode_caption_button_on_hover_dark">#11FFFFFF</color>
<color name="desktop_mode_caption_button">#00000000</color>
- <color name="tiling_divider_background_light">#C9C7B6</color>
- <color name="tiling_divider_background_dark">#4A4739</color>
<color name="tiling_handle_background_light">#000000</color>
<color name="tiling_handle_background_dark">#FFFFFF</color>
</resources>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index e68680219349..ca18c97f9127 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -304,6 +304,8 @@
<dimen name="bubble_transform_area_width">140dp</dimen>
<!-- Width of the box at the corner of the screen where drag leads to app moving to bubble -->
<dimen name="bubble_transform_area_height">140dp</dimen>
+ <!-- How much elevation a bubble ui needs when dragged, must be above drop target & dismiss. -->
+ <dimen name="dragged_bubble_elevation">3dp</dimen>
<!-- Bottom and end margin for compat buttons. -->
<dimen name="compat_button_margin">24dp</dimen>
@@ -534,10 +536,10 @@
pill elevation. -->
<dimen name="desktop_mode_handle_menu_width">218dp</dimen>
- <!-- The maximum height of the handle menu in desktop mode. Three pills at 52dp each plus
- additional actions pill 208dp plus 2dp spacing between them plus 4dp top padding
- plus 2dp bottom padding: 52*3 + 52*4 + (4-1)*2 + 4 + 2 = 376 -->
- <dimen name="desktop_mode_handle_menu_height">376dp</dimen>
+ <!-- The maximum height of the handle menu in desktop mode. Three pills at 52dp each,
+ additional actions pill 260dp, plus 2dp spacing between them plus 4dp top padding.
+ 52*3 + 52*5 + (5-1)*2 + 4 = 428 -->
+ <dimen name="desktop_mode_handle_menu_height">428dp</dimen>
<!-- The elevation set on the handle menu pills. -->
<dimen name="desktop_mode_handle_menu_pill_elevation">1dp</dimen>
@@ -566,6 +568,9 @@
<!-- The height of the handle menu's "Change aspect ratio" pill in desktop mode. -->
<dimen name="desktop_mode_handle_menu_change_aspect_ratio_height">52dp</dimen>
+ <!-- The height of the handle menu's "Optimize View" pill in desktop mode. -->
+ <dimen name="desktop_mode_handle_menu_restart_button_height">52dp</dimen>
+
<!-- The margin between pills of the handle menu in desktop mode. -->
<dimen name="desktop_mode_handle_menu_pill_spacing_margin">2dp</dimen>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index 5ef83826840b..1fd4704f7814 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -314,6 +314,8 @@
<string name="manage_windows_text">Manage Windows</string>
<!-- Accessibility text for the handle menu change aspect ratio button [CHAR LIMIT=NONE] -->
<string name="change_aspect_ratio_text">Change aspect ratio</string>
+ <!-- Accessibility text for the handle menu restart button [CHAR LIMIT=NONE] -->
+ <string name="handle_menu_restart_text">Optimize View</string>
<!-- Accessibility text for the handle menu close button [CHAR LIMIT=NONE] -->
<string name="close_text">Close</string>
<!-- Accessibility text for the handle menu close menu button [CHAR LIMIT=NONE] -->
diff --git a/libs/WindowManager/Shell/shared/res/values/dimen.xml b/libs/WindowManager/Shell/shared/res/values/dimen.xml
index 3b504cf713f1..74b6023bde36 100644
--- a/libs/WindowManager/Shell/shared/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/shared/res/values/dimen.xml
@@ -39,9 +39,9 @@
<dimen name="drag_zone_v_split_from_expanded_view_height_fold_short">100dp</dimen>
<!-- Bubble drop target dimensions -->
- <dimen name="drop_target_elevation">1dp</dimen>
+ <dimen name="drop_target_elevation">2dp</dimen>
<dimen name="drop_target_radius">28dp</dimen>
- <dimen name="drop_target_stroke">1dp</dimen>
+ <dimen name="drop_target_stroke">2dp</dimen>
<dimen name="drop_target_full_screen_padding">20dp</dimen>
<dimen name="drop_target_desktop_window_padding_small">100dp</dimen>
<dimen name="drop_target_desktop_window_padding_large">130dp</dimen>
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetView.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetView.kt
index 73277310ffe4..df101fe44b75 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetView.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetView.kt
@@ -30,15 +30,15 @@ import com.android.wm.shell.shared.R
class DropTargetView(context: Context) : View(context) {
private val rectPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
- color = context.getColor(com.android.internal.R.color.materialColorPrimaryContainer)
+ color = context.getColor(com.android.internal.R.color.materialColorPrimaryFixed)
style = Paint.Style.FILL
alpha = (0.35f * 255).toInt()
}
private val strokePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
- color = context.getColor(com.android.internal.R.color.materialColorPrimaryContainer)
+ color = context.getColor(com.android.internal.R.color.materialColorPrimaryFixed)
style = Paint.Style.STROKE
- strokeWidth = 1.dpToPx()
+ strokeWidth = 2.dpToPx()
}
private val cornerRadius = 28.dpToPx()
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
index ed5e0c608675..e5a4cd034e72 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
@@ -278,6 +278,9 @@ public class DesktopModeStatus {
if (!canEnterDesktopMode(context)) {
return false;
}
+ if (!enforceDeviceRestrictions()) {
+ return true;
+ }
if (display.getType() == Display.TYPE_INTERNAL) {
return canInternalDisplayHostDesktops(context);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 5d59af940da0..81cf031994a2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -508,7 +508,11 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
mShouldStartOnNextMoveEvent = false;
} else {
- mShouldStartOnNextMoveEvent = true;
+ if (predictiveBackDelayWmTransition()) {
+ onGestureStarted(touchX, touchY, swipeEdge);
+ } else {
+ mShouldStartOnNextMoveEvent = true;
+ }
}
}
} else if (keyAction == MotionEvent.ACTION_MOVE) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
index 35435569d8b1..44d859dfb9ba 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
@@ -17,9 +17,11 @@
package com.android.wm.shell.bubbles.bar
import android.annotation.SuppressLint
+import android.content.Context
import android.view.MotionEvent
import android.view.View
import androidx.annotation.VisibleForTesting
+import com.android.wm.shell.R
import com.android.wm.shell.bubbles.BubblePositioner
import com.android.wm.shell.shared.bubbles.BubbleBarLocation
import com.android.wm.shell.shared.bubbles.DismissView
@@ -32,6 +34,7 @@ import com.android.wm.shell.shared.magnetictarget.MagnetizedObject
/** Controller for handling drag interactions with [BubbleBarExpandedView] */
@SuppressLint("ClickableViewAccessibility")
class BubbleBarExpandedViewDragController(
+ private val context: Context,
private val expandedView: BubbleBarExpandedView,
private val dismissView: DismissView,
private val animationHelper: BubbleBarAnimationHelper,
@@ -54,6 +57,8 @@ class BubbleBarExpandedViewDragController(
MagnetizedObject.magnetizeView(expandedView)
private val magnetizedDismissTarget: MagnetizedObject.MagneticTarget
+ private val draggedBubbleElevation: Float
+
init {
magnetizedExpandedView.magnetListener = MagnetListener()
magnetizedExpandedView.animateStuckToTarget =
@@ -70,6 +75,8 @@ class BubbleBarExpandedViewDragController(
MagnetizedObject.MagneticTarget(dismissView.circle, dismissView.circle.width)
magnetizedExpandedView.addTarget(magnetizedDismissTarget)
+ draggedBubbleElevation = context.resources.getDimension(
+ R.dimen.dragged_bubble_elevation)
val dragMotionEventHandler = HandleDragListener()
expandedView.handleView.setOnTouchListener { view, event ->
@@ -103,6 +110,7 @@ class BubbleBarExpandedViewDragController(
override fun onDown(v: View, ev: MotionEvent): Boolean {
// While animating, don't allow new touch events
if (expandedView.isAnimating) return false
+ expandedView.z = draggedBubbleElevation
if (dropTargetManager != null && dragZoneFactory != null) {
val draggedObject = DraggedObject.ExpandedView(
if (bubblePositioner.isBubbleBarOnLeft) {
@@ -154,11 +162,13 @@ class BubbleBarExpandedViewDragController(
velX: Float,
velY: Float,
) {
+ v.translationZ = 0f
finishDrag()
}
override fun onCancel(v: View, ev: MotionEvent, viewInitialX: Float, viewInitialY: Float) {
isStuckToDismiss = false
+ v.translationZ = 0f
finishDrag()
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
index 2cc9387bd1e9..dd86725f7944 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
@@ -349,6 +349,7 @@ public class BubbleBarLayerView extends FrameLayout
}
};
mDragController = new BubbleBarExpandedViewDragController(
+ mContext,
mExpandedView,
mDismissView,
mAnimationHelper,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
index 97184c859d4d..46f6e40ec5e8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
@@ -287,7 +287,13 @@ public class DisplayController {
? mContext
: mContext.createDisplayContext(display);
final Context context = perDisplayContext.createConfigurationContext(newConfig);
- dr.setDisplayLayout(context, new DisplayLayout(context, display));
+ final DisplayLayout displayLayout = new DisplayLayout(context, display);
+ if (mDisplayTopology != null) {
+ displayLayout.setGlobalBoundsDp(
+ mDisplayTopology.getAbsoluteBounds().get(
+ displayId, displayLayout.globalBoundsDp()));
+ }
+ dr.setDisplayLayout(context, displayLayout);
for (int i = 0; i < mDisplayChangedListeners.size(); ++i) {
mDisplayChangedListeners.get(i).onDisplayConfigurationChanged(
displayId, newConfig);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java
index cf0ecae7c815..a1e7ff04347f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java
@@ -106,31 +106,37 @@ public class DividerRoundedCorner extends View {
* of non split screen.
*
* @param isSplitScreen Whether the divider is used by split screen or tiling.
- * @param isDarkMode Whether the mode is ui dark mode.
+ * @param color Rounded corner color.
*/
- public void setup(boolean isSplitScreen, boolean isDarkMode) {
+ public void setup(boolean isSplitScreen, int color) {
mIsSplitScreen = isSplitScreen;
if (!isSplitScreen) {
- mDividerBarBackground.setColor(getTilingHandleColor(isDarkMode));
+ mDividerBarBackground.setColor(color);
}
}
/**
- * Notifies the divider of ui mode change.
+ * Notifies the divider of ui mode change and provides a new color.
*
- * @param isDarkMode Whether the mode is ui dark mode.
+ * @param color The new divider rounded corner color.
*/
- public void onUiModeChange(boolean isDarkMode) {
+ public void onUiModeChange(int color) {
if (!mIsSplitScreen) {
- mDividerBarBackground.setColor(getTilingHandleColor(isDarkMode));
+ mDividerBarBackground.setColor(color);
invalidate();
}
}
- private int getTilingHandleColor(boolean isDarkMode) {
- return isDarkMode ? getResources().getColor(
- R.color.tiling_divider_background_dark, null /* theme */) : getResources().getColor(
- R.color.tiling_divider_background_light, null /* theme */);
+ /**
+ * Notifies rounded corner view of color change.
+ *
+ * @param color The new divider rounded corner color.
+ */
+ public void onCornerColorChange(int color) {
+ if (!mIsSplitScreen) {
+ mDividerBarBackground.setColor(color);
+ invalidate();
+ }
}
/**
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 613e78753b66..5fbbb0bf1e78 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
@@ -79,6 +79,7 @@ import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.UserProfileContexts;
import com.android.wm.shell.common.split.SplitState;
+import com.android.wm.shell.compatui.api.CompatUIHandler;
import com.android.wm.shell.compatui.letterbox.LetterboxCommandHandler;
import com.android.wm.shell.compatui.letterbox.LetterboxTransitionObserver;
import com.android.wm.shell.crashhandling.ShellCrashHandler;
@@ -89,6 +90,7 @@ import com.android.wm.shell.desktopmode.DefaultDragToDesktopTransitionHandler;
import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler;
import com.android.wm.shell.desktopmode.DesktopDisplayEventHandler;
import com.android.wm.shell.desktopmode.DesktopDisplayModeController;
+import com.android.wm.shell.desktopmode.DesktopImeHandler;
import com.android.wm.shell.desktopmode.DesktopImmersiveController;
import com.android.wm.shell.desktopmode.DesktopMinimizationTransitionHandler;
import com.android.wm.shell.desktopmode.DesktopMixedTransitionHandler;
@@ -1044,7 +1046,8 @@ public abstract class WMShellModule {
RecentsTransitionHandler recentsTransitionHandler,
DesktopModeCompatPolicy desktopModeCompatPolicy,
DesktopTilingDecorViewModel desktopTilingDecorViewModel,
- MultiDisplayDragMoveIndicatorController multiDisplayDragMoveIndicatorController
+ MultiDisplayDragMoveIndicatorController multiDisplayDragMoveIndicatorController,
+ Optional<CompatUIHandler> compatUI
) {
if (!DesktopModeStatus.canEnterDesktopModeOrShowAppHandle(context)) {
return Optional.empty();
@@ -1062,7 +1065,7 @@ public abstract class WMShellModule {
activityOrientationChangeHandler, focusTransitionObserver, desktopModeEventLogger,
desktopModeUiEventLogger, taskResourceLoader, recentsTransitionHandler,
desktopModeCompatPolicy, desktopTilingDecorViewModel,
- multiDisplayDragMoveIndicatorController));
+ multiDisplayDragMoveIndicatorController, compatUI.orElse(null)));
}
@WMSingleton
@@ -1524,6 +1527,18 @@ public abstract class WMShellModule {
mainHandler));
}
+ @WMSingleton
+ @Provides
+ static Optional<DesktopImeHandler> provideDesktopImeHandler(
+ DisplayImeController displayImeController,
+ Context context,
+ ShellInit shellInit) {
+ if (!DesktopModeStatus.canEnterDesktopMode(context)) {
+ return Optional.empty();
+ }
+ return Optional.of(new DesktopImeHandler(displayImeController, shellInit));
+ }
+
//
// App zoom out
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImeHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImeHandler.kt
new file mode 100644
index 000000000000..93ba71a17937
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImeHandler.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode
+
+import android.view.SurfaceControl
+import com.android.window.flags.Flags
+import com.android.wm.shell.common.DisplayImeController
+import com.android.wm.shell.common.DisplayImeController.ImePositionProcessor.IME_ANIMATION_DEFAULT
+import com.android.wm.shell.sysui.ShellInit
+
+/** Handles the interactions between IME and desktop tasks */
+class DesktopImeHandler(
+ private val displayImeController: DisplayImeController,
+ shellInit: ShellInit,
+) : DisplayImeController.ImePositionProcessor {
+
+ init {
+ shellInit.addInitCallback(::onInit, this)
+ }
+
+ private fun onInit() {
+ if (Flags.enableDesktopImeBugfix()) {
+ displayImeController.addPositionProcessor(this)
+ }
+ }
+
+ override fun onImeStartPositioning(
+ displayId: Int,
+ hiddenTop: Int,
+ shownTop: Int,
+ showing: Boolean,
+ isFloating: Boolean,
+ t: SurfaceControl.Transaction?,
+ ): Int {
+ return IME_ANIMATION_DEFAULT
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt
index b9cb32d8a14f..e0300d688379 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt
@@ -167,7 +167,35 @@ class DesktopModeUiEventLogger(
@UiEvent(doc = "Exit desktop mode education tooltip on the app header menu is clicked")
EXIT_DESKTOP_MODE_EDUCATION_TOOLTIP_CLICKED(2104),
@UiEvent(doc = "Exit desktop mode education tooltip is dismissed by the user")
- EXIT_DESKTOP_MODE_EDUCATION_TOOLTIP_DISMISSED(2105);
+ EXIT_DESKTOP_MODE_EDUCATION_TOOLTIP_DISMISSED(2105),
+ @UiEvent(doc = "A11y service opened app handle menu by selecting handle from fullscreen")
+ A11Y_APP_HANDLE_MENU_OPENED(2156),
+ @UiEvent(doc = "A11y service opened app handle menu through Switch Access actions menu ")
+ A11Y_SYSTEM_ACTION_APP_HANDLE_MENU(2157),
+ @UiEvent(doc = "A11y service selected desktop mode from app handle menu")
+ A11Y_APP_HANDLE_MENU_DESKTOP_VIEW(2158),
+ @UiEvent(doc = "A11y service selected fullscreen mode from app handle menu")
+ A11Y_APP_HANDLE_MENU_FULLSCREEN(2159),
+ @UiEvent(doc = "A11y service selected split screen mode from app handle menu")
+ A11Y_APP_HANDLE_MENU_SPLIT_SCREEN(2160),
+ @UiEvent(doc = "A11y service selected maximize/restore button from app header")
+ A11Y_APP_WINDOW_MAXIMIZE_RESTORE_BUTTON(2161),
+ @UiEvent(doc = "A11y service selected minimize button from app header")
+ A11Y_APP_WINDOW_MINIMIZE_BUTTON(2162),
+ @UiEvent(doc = "A11y service selected close button from app header")
+ A11Y_APP_WINDOW_CLOSE_BUTTON(2163),
+ @UiEvent(doc = "A11y service selected maximize button from app header maximize menu")
+ A11Y_MAXIMIZE_MENU_MAXIMIZE(2164),
+ @UiEvent(doc = "A11y service selected resize left button from app header maximize menu")
+ A11Y_MAXIMIZE_MENU_RESIZE_LEFT(2165),
+ @UiEvent(doc = "A11y service selected resize right button from app header maximize menu")
+ A11Y_MAXIMIZE_MENU_RESIZE_RIGHT(2166),
+ @UiEvent(doc = "A11y service triggered a11y action to maximize/restore app window")
+ A11Y_ACTION_MAXIMIZE_RESTORE(2167),
+ @UiEvent(doc = "A11y service triggered a11y action to resize app window left")
+ A11Y_ACTION_RESIZE_LEFT(2168),
+ @UiEvent(doc = "A11y service triggered a11y action to resize app window right")
+ A11Y_ACTION_RESIZE_RIGHT(2169);
override fun getId(): Int = mId
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
index e77acfb805a6..25fdbb348356 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
@@ -1112,6 +1112,7 @@ class DesktopRepository(
internal fun dump(pw: PrintWriter, prefix: String) {
val innerPrefix = "$prefix "
pw.println("${prefix}DesktopRepository")
+ pw.println("${innerPrefix}userId=$userId")
dumpDesktopTaskData(pw, innerPrefix)
pw.println("${innerPrefix}activeTasksListeners=${activeTasksListeners.size}")
pw.println("${innerPrefix}visibleTasksListeners=${visibleTasksListeners.size}")
@@ -1298,6 +1299,7 @@ class DesktopRepository(
deskByDisplayId[displayId]?.let { sequenceOf(it) } ?: emptySequence()
override fun remove(deskId: Int) {
+ setDeskInactive(deskId)
deskByDisplayId[deskId]?.clear()
}
@@ -1397,6 +1399,7 @@ class DesktopRepository(
desktopDisplays[displayId]?.orderedDesks?.asSequence() ?: emptySequence()
override fun remove(deskId: Int) {
+ setDeskInactive(deskId)
desktopDisplays.forEach { _, display ->
display.orderedDesks.removeIf { it.deskId == deskId }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 093e8ef8bc0e..5849d4af4e7e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -465,6 +465,30 @@ class DesktopTasksController(
return isFreeformDisplay
}
+ /** Called when the recents transition that started while in desktop is finishing. */
+ fun onRecentsInDesktopAnimationFinishing(
+ transition: IBinder,
+ finishWct: WindowContainerTransaction,
+ returnToApp: Boolean,
+ ) {
+ if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) return
+ logV("onRecentsInDesktopAnimationFinishing returnToApp=%b", returnToApp)
+ if (returnToApp) return
+ // Home/Recents only exists in the default display.
+ val activeDesk = taskRepository.getActiveDeskId(DEFAULT_DISPLAY) ?: return
+ // Not going back to the active desk, deactivate it.
+ val runOnTransitStart =
+ performDesktopExitCleanUp(
+ wct = finishWct,
+ deskId = activeDesk,
+ displayId = DEFAULT_DISPLAY,
+ willExitDesktop = true,
+ shouldEndUpAtHome = true,
+ fromRecentsTransition = true,
+ )
+ runOnTransitStart?.invoke(transition)
+ }
+
/** Creates a new desk in the given display. */
fun createDesk(displayId: Int) {
if (displayId == Display.INVALID_DISPLAY) {
@@ -835,20 +859,22 @@ class DesktopTasksController(
val requestRes = transitions.dispatchRequest(Binder(), requestInfo, /* skip= */ null)
wct.merge(requestRes.second, true)
- desktopPipTransitionObserver.get().addPendingPipTransition(
- DesktopPipTransitionObserver.PendingPipTransition(
- token = freeformTaskTransitionStarter.startPipTransition(wct),
- taskId = taskInfo.taskId,
- onSuccess = {
- onDesktopTaskEnteredPip(
- taskId = taskId,
- deskId = deskId,
- displayId = taskInfo.displayId,
- taskIsLastVisibleTaskBeforePip = isLastTask,
- )
- },
+ desktopPipTransitionObserver
+ .get()
+ .addPendingPipTransition(
+ DesktopPipTransitionObserver.PendingPipTransition(
+ token = freeformTaskTransitionStarter.startPipTransition(wct),
+ taskId = taskInfo.taskId,
+ onSuccess = {
+ onDesktopTaskEnteredPip(
+ taskId = taskId,
+ deskId = deskId,
+ displayId = taskInfo.displayId,
+ taskIsLastVisibleTaskBeforePip = isLastTask,
+ )
+ },
+ )
)
- )
} else {
snapEventHandler.removeTaskIfTiled(displayId, taskId)
val willExitDesktop = willExitDesktop(taskId, displayId, forceExitDesktop = false)
@@ -1208,6 +1234,7 @@ class DesktopTasksController(
pendingIntentBackgroundActivityStartMode =
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
launchBounds = bounds
+ launchDisplayId = displayId
if (DesktopModeFlags.ENABLE_SHELL_INITIAL_BOUNDS_REGRESSION_BUG_FIX.isTrue) {
// Sets launch bounds size as flexible so core can recalculate.
flexibleLaunchSize = true
@@ -1937,16 +1964,23 @@ class DesktopTasksController(
displayId: Int,
willExitDesktop: Boolean,
shouldEndUpAtHome: Boolean = true,
+ fromRecentsTransition: Boolean = false,
): RunOnTransitStart? {
if (!willExitDesktop) return null
desktopModeEnterExitTransitionListener?.onExitDesktopModeTransitionStarted(
FULLSCREEN_ANIMATION_DURATION
)
- removeWallpaperActivity(wct, displayId)
- if (shouldEndUpAtHome) {
- // If the transition should end up with user going to home, launch home with a pending
- // intent.
- addLaunchHomePendingIntent(wct, displayId)
+ // No need to clean up the wallpaper / reorder home when coming from a recents transition.
+ if (
+ !fromRecentsTransition ||
+ !DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue
+ ) {
+ removeWallpaperActivity(wct, displayId)
+ if (shouldEndUpAtHome) {
+ // If the transition should end up with user going to home, launch home with a
+ // pending intent.
+ addLaunchHomePendingIntent(wct, displayId)
+ }
}
return prepareDeskDeactivationIfNeeded(wct, deskId)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt
index c10752d36bf9..72758bd538d6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt
@@ -21,6 +21,7 @@ import android.content.Context
import android.content.pm.UserInfo
import android.os.UserManager
import android.util.SparseArray
+import android.window.DesktopExperienceFlags
import android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_HSUM
import androidx.core.util.forEach
import com.android.internal.protolog.ProtoLog
@@ -68,7 +69,10 @@ class DesktopUserRepositories(
if (DesktopModeStatus.canEnterDesktopMode(context)) {
shellInit.addInitCallback(::onInit, this)
}
- if (ENABLE_DESKTOP_WINDOWING_HSUM.isTrue()) {
+ if (
+ ENABLE_DESKTOP_WINDOWING_HSUM.isTrue() &&
+ !DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue
+ ) {
userIdToProfileIdsMap[userId] = userManager.getProfiles(userId).map { it.id }
}
}
@@ -76,6 +80,13 @@ class DesktopUserRepositories(
private fun onInit() {
repositoryInitializer.initialize(this)
shellController.addUserChangeListener(this)
+ if (
+ ENABLE_DESKTOP_WINDOWING_HSUM.isTrue() &&
+ DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue
+ ) {
+ userId = ActivityManager.getCurrentUser()
+ userIdToProfileIdsMap[userId] = userManager.getProfiles(userId).map { it.id }
+ }
}
/** Returns [DesktopRepository] for the parent user id. */
@@ -97,10 +108,10 @@ class DesktopUserRepositories(
/** Dumps [DesktopRepository] for each user. */
fun dump(pw: PrintWriter, prefix: String) {
- desktopRepoByUserId.forEach { key, value ->
- pw.println("${prefix}userId=$key")
- value.dump(pw, prefix)
- }
+ val innerPrefix = "$prefix "
+ pw.println("${prefix}DesktopUserRepositories:")
+ pw.println("${innerPrefix}currentUserId=$userId")
+ desktopRepoByUserId.forEach { key, value -> value.dump(pw, innerPrefix) }
}
override fun onUserChanged(newUserId: Int, userContext: Context) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/DefaultHomePackageSupplier.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/DefaultHomePackageSupplier.kt
index 8ce624e103ef..bf687f2e4f3a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/DefaultHomePackageSupplier.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/DefaultHomePackageSupplier.kt
@@ -20,6 +20,7 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
+import android.content.pm.PackageManager
import android.os.Handler
import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.sysui.ShellInit
@@ -28,6 +29,7 @@ import java.util.function.Supplier
/**
* This supplies the package name of default home in an efficient way. The query to package manager
* only executes on initialization and when the preferred activity (e.g. default home) is changed.
+ * Note that this returns null package name if the default home is setup wizard.
*/
class DefaultHomePackageSupplier(
private val context: Context,
@@ -36,6 +38,7 @@ class DefaultHomePackageSupplier(
) : BroadcastReceiver(), Supplier<String?> {
private var defaultHomePackage: String? = null
+ private var isSetupWizard: Boolean = false
init {
shellInit.addInitCallback({ onInit() }, this)
@@ -52,6 +55,14 @@ class DefaultHomePackageSupplier(
private fun updateDefaultHomePackage(): String? {
defaultHomePackage = context.packageManager.getHomeActivities(ArrayList())?.packageName
+ isSetupWizard =
+ defaultHomePackage != null &&
+ context.packageManager.resolveActivity(
+ Intent()
+ .setPackage(defaultHomePackage)
+ .addCategory(Intent.CATEGORY_SETUP_WIZARD),
+ PackageManager.MATCH_SYSTEM_ONLY,
+ ) != null
return defaultHomePackage
}
@@ -60,6 +71,7 @@ class DefaultHomePackageSupplier(
}
override fun get(): String? {
+ if (isSetupWizard) return null
return defaultHomePackage ?: updateDefaultHomePackage()
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DeskTransition.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DeskTransition.kt
index 9dec96933ee5..454419c805c1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DeskTransition.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DeskTransition.kt
@@ -18,9 +18,12 @@ package com.android.wm.shell.desktopmode.multidesks
import android.os.IBinder
/** Represents shell-started transitions involving desks. */
-sealed class DeskTransition {
+sealed interface DeskTransition {
/** The transition token. */
- abstract val token: IBinder
+ val token: IBinder
+
+ /** Returns a copy of this desk transition with a new transition token. */
+ fun copyWithToken(token: IBinder): DeskTransition
/** A transition to remove a desk and its tasks from a display. */
data class RemoveDesk(
@@ -29,11 +32,15 @@ sealed class DeskTransition {
val deskId: Int,
val tasks: Set<Int>,
val onDeskRemovedListener: OnDeskRemovedListener?,
- ) : DeskTransition()
+ ) : DeskTransition {
+ override fun copyWithToken(token: IBinder): DeskTransition = copy(token)
+ }
/** A transition to activate a desk in its display. */
data class ActivateDesk(override val token: IBinder, val displayId: Int, val deskId: Int) :
- DeskTransition()
+ DeskTransition {
+ override fun copyWithToken(token: IBinder): DeskTransition = copy(token)
+ }
/** A transition to activate a desk by moving an outside task to it. */
data class ActiveDeskWithTask(
@@ -41,8 +48,12 @@ sealed class DeskTransition {
val displayId: Int,
val deskId: Int,
val enterTaskId: Int,
- ) : DeskTransition()
+ ) : DeskTransition {
+ override fun copyWithToken(token: IBinder): DeskTransition = copy(token)
+ }
/** A transition to deactivate a desk. */
- data class DeactivateDesk(override val token: IBinder, val deskId: Int) : DeskTransition()
+ data class DeactivateDesk(override val token: IBinder, val deskId: Int) : DeskTransition {
+ override fun copyWithToken(token: IBinder): DeskTransition = copy(token)
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserver.kt
index b521b2e8c942..588b5c350330 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserver.kt
@@ -39,6 +39,7 @@ class DesksTransitionObserver(
val transitions = deskTransitions[transition.token] ?: mutableSetOf()
transitions += transition
deskTransitions[transition.token] = transitions
+ logD("Added pending desk transition: %s", transition)
}
/**
@@ -51,6 +52,43 @@ class DesksTransitionObserver(
deskTransitions.forEach { deskTransition -> handleDeskTransition(info, deskTransition) }
}
+ /**
+ * Called when a transition is merged with another transition, which may include transitions not
+ * tracked by this observer.
+ */
+ fun onTransitionMerged(merged: IBinder, playing: IBinder) {
+ if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) return
+ val transitions = deskTransitions.remove(merged) ?: return
+ deskTransitions[playing] =
+ transitions
+ .map { deskTransition -> deskTransition.copyWithToken(token = playing) }
+ .toMutableSet()
+ }
+
+ /**
+ * Called when any transition finishes, which may include transitions not tracked by this
+ * observer.
+ *
+ * Most [DeskTransition]s are not handled here because [onTransitionReady] handles them and
+ * removes them from the map. However, there can be cases where the transition was added after
+ * [onTransitionReady] had already been called and they need to be handled here, such as the
+ * swipe-to-home recents transition when there is no book-end transition.
+ */
+ fun onTransitionFinished(transition: IBinder) {
+ if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) return
+ val deskTransitions = deskTransitions.remove(transition) ?: return
+ deskTransitions.forEach { deskTransition ->
+ if (deskTransition is DeskTransition.DeactivateDesk) {
+ handleDeactivateDeskTransition(null, deskTransition)
+ } else {
+ logW(
+ "Unexpected desk transition finished without being handled: %s",
+ deskTransition,
+ )
+ }
+ }
+ }
+
private fun handleDeskTransition(info: TransitionInfo, deskTransition: DeskTransition) {
logD("Desk transition ready: %s", deskTransition)
val desktopRepository = desktopUserRepositories.current
@@ -102,41 +140,54 @@ class DesksTransitionObserver(
)
}
}
- is DeskTransition.DeactivateDesk -> {
- var visibleDeactivation = false
- for (change in info.changes) {
- val isDeskChange = desksOrganizer.isDeskChange(change, deskTransition.deskId)
- if (isDeskChange) {
- visibleDeactivation = true
- continue
- }
- val taskId = change.taskInfo?.taskId ?: continue
- val removedFromDesk =
- desktopRepository.getDeskIdForTask(taskId) == deskTransition.deskId &&
- desksOrganizer.getDeskAtEnd(change) == null
- if (removedFromDesk) {
- desktopRepository.removeTaskFromDesk(
- deskId = deskTransition.deskId,
- taskId = taskId,
- )
- }
- }
- // Always deactivate even if there's no change that confirms the desk was
- // deactivated. Some interactions, such as the desk deactivating because it's
- // occluded by a fullscreen task result in a transition change, but others, such
- // as transitioning from an empty desk to home may not.
- if (!visibleDeactivation) {
- logD("Deactivating desk without transition change")
- }
- desktopRepository.setDeskInactive(deskId = deskTransition.deskId)
+ is DeskTransition.DeactivateDesk -> handleDeactivateDeskTransition(info, deskTransition)
+ }
+ }
+
+ private fun handleDeactivateDeskTransition(
+ info: TransitionInfo?,
+ deskTransition: DeskTransition.DeactivateDesk,
+ ) {
+ logD("handleDeactivateDeskTransition: %s", deskTransition)
+ val desktopRepository = desktopUserRepositories.current
+ var deskChangeFound = false
+
+ val changes = info?.changes ?: emptyList()
+ for (change in changes) {
+ val isDeskChange = desksOrganizer.isDeskChange(change, deskTransition.deskId)
+ if (isDeskChange) {
+ deskChangeFound = true
+ continue
+ }
+ val taskId = change.taskInfo?.taskId ?: continue
+ val removedFromDesk =
+ desktopRepository.getDeskIdForTask(taskId) == deskTransition.deskId &&
+ desksOrganizer.getDeskAtEnd(change) == null
+ if (removedFromDesk) {
+ desktopRepository.removeTaskFromDesk(
+ deskId = deskTransition.deskId,
+ taskId = taskId,
+ )
}
}
+ // Always deactivate even if there's no change that confirms the desk was
+ // deactivated. Some interactions, such as the desk deactivating because it's
+ // occluded by a fullscreen task result in a transition change, but others, such
+ // as transitioning from an empty desk to home may not.
+ if (!deskChangeFound) {
+ logD("Deactivating desk without transition change")
+ }
+ desktopRepository.setDeskInactive(deskId = deskTransition.deskId)
}
private fun logD(msg: String, vararg arguments: Any?) {
ProtoLog.d(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
}
+ private fun logW(msg: String, vararg arguments: Any?) {
+ ProtoLog.w(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+ }
+
private companion object {
private const val TAG = "DesksTransitionObserver"
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
index 0bf2ea61b0a4..e7492f17835a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
@@ -207,6 +207,7 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs
@Override
public void onTransitionMerged(@NonNull IBinder merged, @NonNull IBinder playing) {
+ mDesksTransitionObserver.ifPresent(o -> o.onTransitionMerged(merged, playing));
if (DesktopModeFlags.ENABLE_FULLY_IMMERSIVE_IN_DESKTOP.isTrue()) {
// TODO(b/367268953): Remove when DesktopTaskListener is introduced.
mDesktopImmersiveController.ifPresent(h -> h.onTransitionMerged(merged, playing));
@@ -232,6 +233,7 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs
@Override
public void onTransitionFinished(@NonNull IBinder transition, boolean aborted) {
+ mDesksTransitionObserver.ifPresent(o -> o.onTransitionFinished(transition));
if (DesktopModeFlags.ENABLE_FULLY_IMMERSIVE_IN_DESKTOP.isTrue()) {
// TODO(b/367268953): Remove when DesktopTaskListener is introduced.
mDesktopImmersiveController.ifPresent(h -> h.onTransitionFinished(transition, aborted));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index a0fb62508cc1..e19ad23b2125 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -394,6 +394,9 @@ public class DefaultMixedHandler implements MixedTransitionHandler,
if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) {
((RecentsMixedTransition) mixed).onAnimateRecentsDuringSplitFinishing(
returnToApp, finishWct, finishT);
+ } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_DESKTOP) {
+ ((RecentsMixedTransition) mixed).onAnimateRecentsDuringDesktopFinishing(
+ returnToApp, finishWct);
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java
index 1e926c57ca61..cfefc1f1ac66 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java
@@ -198,6 +198,15 @@ class RecentsMixedTransition extends DefaultMixedHandler.MixedTransition {
}
}
+ /**
+ * Called when the recents animation during desktop is about to finish.
+ */
+ void onAnimateRecentsDuringDesktopFinishing(boolean returnToApp,
+ @NonNull WindowContainerTransaction finishWct) {
+ mDesktopTasksController.onRecentsInDesktopAnimationFinishing(mTransition, finishWct,
+ returnToApp);
+ }
+
@Override
void mergeAnimation(
@NonNull IBinder transition, @NonNull TransitionInfo info,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 800faca830f4..69e1f36dec0b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -108,6 +108,8 @@ import com.android.wm.shell.common.MultiInstanceHelper;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.compatui.CompatUIController;
+import com.android.wm.shell.compatui.api.CompatUIHandler;
+import com.android.wm.shell.compatui.impl.CompatUIRequests;
import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler;
import com.android.wm.shell.desktopmode.DesktopImmersiveController;
import com.android.wm.shell.desktopmode.DesktopModeEventLogger;
@@ -266,6 +268,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
private final DesktopTilingDecorViewModel mDesktopTilingDecorViewModel;
private final MultiDisplayDragMoveIndicatorController mMultiDisplayDragMoveIndicatorController;
private final LatencyTracker mLatencyTracker;
+ private final CompatUIHandler mCompatUI;
public DesktopModeWindowDecorViewModel(
Context context,
@@ -306,7 +309,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
RecentsTransitionHandler recentsTransitionHandler,
DesktopModeCompatPolicy desktopModeCompatPolicy,
DesktopTilingDecorViewModel desktopTilingDecorViewModel,
- MultiDisplayDragMoveIndicatorController multiDisplayDragMoveIndicatorController) {
+ MultiDisplayDragMoveIndicatorController multiDisplayDragMoveIndicatorController,
+ CompatUIHandler compatUI) {
this(
context,
shellExecutor,
@@ -353,7 +357,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
recentsTransitionHandler,
desktopModeCompatPolicy,
desktopTilingDecorViewModel,
- multiDisplayDragMoveIndicatorController);
+ multiDisplayDragMoveIndicatorController,
+ compatUI);
}
@VisibleForTesting
@@ -403,7 +408,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
RecentsTransitionHandler recentsTransitionHandler,
DesktopModeCompatPolicy desktopModeCompatPolicy,
DesktopTilingDecorViewModel desktopTilingDecorViewModel,
- MultiDisplayDragMoveIndicatorController multiDisplayDragMoveIndicatorController) {
+ MultiDisplayDragMoveIndicatorController multiDisplayDragMoveIndicatorController,
+ CompatUIHandler compatUI) {
mContext = context;
mMainExecutor = shellExecutor;
mMainHandler = mainHandler;
@@ -444,6 +450,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
mActivityOrientationChangeHandler = activityOrientationChangeHandler;
mAssistContentRequester = assistContentRequester;
mWindowDecorViewHostSupplier = windowDecorViewHostSupplier;
+ mCompatUI = compatUI;
mOnDisplayChangingListener = (displayId, fromRotation, toRotation, displayAreaInfo, t) -> {
DesktopModeWindowDecoration decoration;
RunningTaskInfo taskInfo;
@@ -1795,6 +1802,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
mMultiInstanceHelper,
mWindowDecorCaptionHandleRepository,
mDesktopModeEventLogger,
+ mDesktopModeUiEventLogger,
mDesktopModeCompatPolicy);
mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
@@ -1864,6 +1872,11 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
CompatUIController.launchUserAspectRatioSettings(mContext, taskInfo);
return Unit.INSTANCE;
});
+ windowDecoration.setOnRestartClickListener(() -> {
+ mCompatUI.sendCompatUIRequest(new CompatUIRequests.DisplayCompatShowRestartDialog(
+ taskInfo.taskId));
+ return Unit.INSTANCE;
+ });
windowDecoration.setOnMaximizeHoverListener(() -> {
if (!windowDecoration.isMaximizeMenuActive()) {
mDesktopModeUiEventLogger.log(taskInfo,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index ae103895d56b..50bc7b5e865b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -93,6 +93,7 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.desktopmode.CaptionState;
import com.android.wm.shell.desktopmode.DesktopModeEventLogger;
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger;
import com.android.wm.shell.desktopmode.DesktopModeUtils;
import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository;
@@ -163,6 +164,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
private Function0<Unit> mOnNewWindowClickListener;
private Function0<Unit> mOnManageWindowsClickListener;
private Function0<Unit> mOnChangeAspectRatioClickListener;
+ private Function0<Unit> mOnRestartClickListener;
private Function0<Unit> mOnMaximizeHoverListener;
private DragPositioningCallback mDragPositioningCallback;
private DragResizeInputListener mDragResizeListener;
@@ -210,6 +212,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
private final MultiInstanceHelper mMultiInstanceHelper;
private final WindowDecorCaptionHandleRepository mWindowDecorCaptionHandleRepository;
private final DesktopUserRepositories mDesktopUserRepositories;
+ private final DesktopModeUiEventLogger mDesktopModeUiEventLogger;
private boolean mIsRecentsTransitionRunning = false;
private boolean mIsDragging = false;
private Runnable mLoadAppInfoRunnable;
@@ -242,6 +245,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
MultiInstanceHelper multiInstanceHelper,
WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
DesktopModeEventLogger desktopModeEventLogger,
+ DesktopModeUiEventLogger desktopModeUiEventLogger,
DesktopModeCompatPolicy desktopModeCompatPolicy) {
this (context, userContext, displayController, taskResourceLoader, splitScreenController,
desktopUserRepositories, taskOrganizer, taskInfo, taskSurface, handler,
@@ -256,7 +260,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
DefaultMaximizeMenuFactory.INSTANCE,
DefaultHandleMenuFactory.INSTANCE, multiInstanceHelper,
windowDecorCaptionHandleRepository, desktopModeEventLogger,
- desktopModeCompatPolicy);
+ desktopModeUiEventLogger, desktopModeCompatPolicy);
}
DesktopModeWindowDecoration(
@@ -293,6 +297,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
MultiInstanceHelper multiInstanceHelper,
WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
DesktopModeEventLogger desktopModeEventLogger,
+ DesktopModeUiEventLogger desktopModeUiEventLogger,
DesktopModeCompatPolicy desktopModeCompatPolicy) {
super(context, userContext, displayController, taskOrganizer, taskInfo,
taskSurface, surfaceControlBuilderSupplier, surfaceControlTransactionSupplier,
@@ -320,6 +325,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mTaskResourceLoader = taskResourceLoader;
mTaskResourceLoader.onWindowDecorCreated(taskInfo);
mDesktopModeCompatPolicy = desktopModeCompatPolicy;
+ mDesktopModeUiEventLogger = desktopModeUiEventLogger;
}
/**
@@ -408,6 +414,11 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mOnChangeAspectRatioClickListener = listener;
}
+ /** Registers a listener to be called when the aspect ratio action is triggered. */
+ void setOnRestartClickListener(Function0<Unit> listener) {
+ mOnRestartClickListener = listener;
+ }
+
/** Registers a listener to be called when the maximize header button is hovered. */
void setOnMaximizeHoverListener(Function0<Unit> listener) {
mOnMaximizeHoverListener = listener;
@@ -889,10 +900,10 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mOnCaptionTouchListener,
mOnCaptionButtonClickListener,
mWindowManagerWrapper,
- mHandler
+ mHandler,
+ mDesktopModeUiEventLogger
);
- } else if (mRelayoutParams.mLayoutResId
- == R.layout.desktop_mode_app_header) {
+ } else if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_app_header) {
return mAppHeaderViewHolderFactory.create(
mResult.mRootView,
mOnCaptionTouchListener,
@@ -902,7 +913,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mOnLeftSnapClickListener,
mOnRightSnapClickListener,
mOnMaximizeOrRestoreClickListener,
- mOnMaximizeHoverListener);
+ mOnMaximizeHoverListener,
+ mDesktopModeUiEventLogger);
}
throw new IllegalArgumentException("Unexpected layout resource id");
}
@@ -1366,7 +1378,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mMaximizeMenu = mMaximizeMenuFactory.create(mSyncQueue, mRootTaskDisplayAreaOrganizer,
mDisplayController, mTaskInfo, mContext,
(width, height) -> calculateMaximizeMenuPosition(width, height),
- mSurfaceControlTransactionSupplier);
+ mSurfaceControlTransactionSupplier, mDesktopModeUiEventLogger);
mMaximizeMenu.show(
/* isTaskInImmersiveMode= */
@@ -1461,6 +1473,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
&& mMinimumInstancesFound;
final boolean shouldShowChangeAspectRatioButton = HandleMenu.Companion
.shouldShowChangeAspectRatioButton(mTaskInfo);
+ final boolean shouldShowRestartButton = HandleMenu.Companion
+ .shouldShowRestartButton(mTaskInfo);
final boolean inDesktopImmersive = mDesktopUserRepositories.getProfile(mTaskInfo.userId)
.isTaskInFullImmersiveState(mTaskInfo.taskId);
final boolean isBrowserApp = isBrowserApp();
@@ -1477,8 +1491,10 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
shouldShowManageWindowsButton,
shouldShowChangeAspectRatioButton,
isDesktopModeSupportedOnDisplay(mContext, mDisplay),
+ shouldShowRestartButton,
isBrowserApp,
isBrowserApp ? getAppLink() : getBrowserLink(),
+ mDesktopModeUiEventLogger,
mResult.mCaptionWidth,
mResult.mCaptionHeight,
mResult.mCaptionX,
@@ -1513,6 +1529,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
}
return Unit.INSTANCE;
},
+ /* onRestartClickListener= */ mOnRestartClickListener,
/* onCloseMenuClickListener= */ () -> {
closeHandleMenu();
return Unit.INSTANCE;
@@ -1963,6 +1980,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
MultiInstanceHelper multiInstanceHelper,
WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
DesktopModeEventLogger desktopModeEventLogger,
+ DesktopModeUiEventLogger desktopModeUiEventLogger,
DesktopModeCompatPolicy desktopModeCompatPolicy) {
return new DesktopModeWindowDecoration(
context,
@@ -1990,6 +2008,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
multiInstanceHelper,
windowDecorCaptionHandleRepository,
desktopModeEventLogger,
+ desktopModeUiEventLogger,
desktopModeCompatPolicy);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
index 2d44395b1340..25cf06b4247c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
@@ -28,6 +28,7 @@ import android.graphics.Bitmap
import android.graphics.Point
import android.graphics.PointF
import android.graphics.Rect
+import android.os.Bundle
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.MotionEvent.ACTION_OUTSIDE
@@ -35,6 +36,7 @@ import android.view.SurfaceControl
import android.view.View
import android.view.WindowInsets.Type.systemBars
import android.view.WindowManager
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction
import android.widget.ImageButton
import android.widget.ImageView
import android.widget.Space
@@ -49,6 +51,10 @@ import androidx.core.view.isGone
import com.android.window.flags.Flags
import com.android.wm.shell.R
import com.android.wm.shell.bubbles.ContextUtils.isRtl
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_APP_HANDLE_MENU_DESKTOP_VIEW
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_APP_HANDLE_MENU_FULLSCREEN
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_APP_HANDLE_MENU_SPLIT_SCREEN
import com.android.wm.shell.shared.annotations.ShellBackgroundThread
import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper
@@ -94,8 +100,10 @@ class HandleMenu(
private val shouldShowManageWindowsButton: Boolean,
private val shouldShowChangeAspectRatioButton: Boolean,
private val shouldShowDesktopModeButton: Boolean,
+ private val shouldShowRestartButton: Boolean,
private val isBrowserApp: Boolean,
private val openInAppOrBrowserIntent: Intent?,
+ private val desktopModeUiEventLogger: DesktopModeUiEventLogger,
private val captionWidth: Int,
private val captionHeight: Int,
captionX: Int,
@@ -138,7 +146,8 @@ class HandleMenu(
private val shouldShowMoreActionsPill: Boolean
get() = SHOULD_SHOW_SCREENSHOT_BUTTON || shouldShowNewWindowButton ||
- shouldShowManageWindowsButton || shouldShowChangeAspectRatioButton
+ shouldShowManageWindowsButton || shouldShowChangeAspectRatioButton ||
+ shouldShowRestartButton
private var loadAppInfoJob: Job? = null
@@ -156,6 +165,7 @@ class HandleMenu(
onChangeAspectRatioClickListener: () -> Unit,
openInAppOrBrowserClickListener: (Intent) -> Unit,
onOpenByDefaultClickListener: () -> Unit,
+ onRestartClickListener: () -> Unit,
onCloseMenuClickListener: () -> Unit,
onOutsideTouchListener: () -> Unit,
forceShowSystemBars: Boolean = false,
@@ -175,6 +185,7 @@ class HandleMenu(
onChangeAspectRatioClickListener = onChangeAspectRatioClickListener,
openInAppOrBrowserClickListener = openInAppOrBrowserClickListener,
onOpenByDefaultClickListener = onOpenByDefaultClickListener,
+ onRestartClickListener = onRestartClickListener,
onCloseMenuClickListener = onCloseMenuClickListener,
onOutsideTouchListener = onOutsideTouchListener,
forceShowSystemBars = forceShowSystemBars,
@@ -197,12 +208,14 @@ class HandleMenu(
onChangeAspectRatioClickListener: () -> Unit,
openInAppOrBrowserClickListener: (Intent) -> Unit,
onOpenByDefaultClickListener: () -> Unit,
+ onRestartClickListener: () -> Unit,
onCloseMenuClickListener: () -> Unit,
onOutsideTouchListener: () -> Unit,
forceShowSystemBars: Boolean = false,
) {
val handleMenuView = HandleMenuView(
context = context,
+ desktopModeUiEventLogger = desktopModeUiEventLogger,
menuWidth = menuWidth,
captionHeight = captionHeight,
shouldShowWindowingPill = shouldShowWindowingPill,
@@ -211,6 +224,7 @@ class HandleMenu(
shouldShowManageWindowsButton = shouldShowManageWindowsButton,
shouldShowChangeAspectRatioButton = shouldShowChangeAspectRatioButton,
shouldShowDesktopModeButton = shouldShowDesktopModeButton,
+ shouldShowRestartButton = shouldShowRestartButton,
isBrowserApp = isBrowserApp
).apply {
bind(taskInfo, shouldShowMoreActionsPill)
@@ -224,6 +238,7 @@ class HandleMenu(
this.onOpenInAppOrBrowserClickListener = {
openInAppOrBrowserClickListener.invoke(openInAppOrBrowserIntent!!)
}
+ this.onRestartClickListener = onRestartClickListener
this.onOpenByDefaultClickListener = onOpenByDefaultClickListener
this.onCloseMenuClickListener = onCloseMenuClickListener
this.onOutsideTouchListener = onOutsideTouchListener
@@ -430,6 +445,10 @@ class HandleMenu(
R.dimen.desktop_mode_handle_menu_change_aspect_ratio_height
)
}
+ if (!shouldShowRestartButton) {
+ menuHeight -= loadDimensionPixelSize(
+ R.dimen.desktop_mode_handle_menu_restart_button_height)
+ }
if (!shouldShowMoreActionsPill) {
menuHeight -= pillTopMargin
}
@@ -464,6 +483,7 @@ class HandleMenu(
@SuppressLint("ClickableViewAccessibility")
class HandleMenuView(
private val context: Context,
+ private val desktopModeUiEventLogger: DesktopModeUiEventLogger,
menuWidth: Int,
captionHeight: Int,
private val shouldShowWindowingPill: Boolean,
@@ -472,6 +492,7 @@ class HandleMenu(
private val shouldShowManageWindowsButton: Boolean,
private val shouldShowChangeAspectRatioButton: Boolean,
private val shouldShowDesktopModeButton: Boolean,
+ private val shouldShowRestartButton: Boolean,
private val isBrowserApp: Boolean
) {
val rootView = LayoutInflater.from(context)
@@ -549,6 +570,8 @@ class HandleMenu(
.requireViewById<HandleMenuActionButton>(R.id.manage_windows_button)
private val changeAspectRatioBtn = moreActionsPill
.requireViewById<HandleMenuActionButton>(R.id.change_aspect_ratio_button)
+ private val restartBtn = moreActionsPill
+ .requireViewById<HandleMenuActionButton>(R.id.handle_menu_restart_button)
// Open in Browser/App Pill.
private val openInAppOrBrowserPill = rootView.requireViewById<View>(
@@ -574,6 +597,7 @@ class HandleMenu(
var onChangeAspectRatioClickListener: (() -> Unit)? = null
var onOpenInAppOrBrowserClickListener: (() -> Unit)? = null
var onOpenByDefaultClickListener: (() -> Unit)? = null
+ var onRestartClickListener: (() -> Unit)? = null
var onCloseMenuClickListener: (() -> Unit)? = null
var onOutsideTouchListener: (() -> Unit)? = null
@@ -590,6 +614,7 @@ class HandleMenu(
newWindowBtn.setOnClickListener { onNewWindowClickListener?.invoke() }
manageWindowBtn.setOnClickListener { onManageWindowsClickListener?.invoke() }
changeAspectRatioBtn.setOnClickListener { onChangeAspectRatioClickListener?.invoke() }
+ restartBtn.setOnClickListener { onRestartClickListener?.invoke() }
rootView.setOnTouchListener { _, event ->
if (event.actionMasked == ACTION_OUTSIDE) {
@@ -599,6 +624,45 @@ class HandleMenu(
return@setOnTouchListener true
}
+ desktopBtn.accessibilityDelegate = object : View.AccessibilityDelegate() {
+ override fun performAccessibilityAction(
+ host: View,
+ action: Int,
+ args: Bundle?
+ ): Boolean {
+ if (action == AccessibilityAction.ACTION_CLICK.id) {
+ desktopModeUiEventLogger.log(taskInfo, A11Y_APP_HANDLE_MENU_DESKTOP_VIEW)
+ }
+ return super.performAccessibilityAction(host, action, args)
+ }
+ }
+
+ fullscreenBtn.accessibilityDelegate = object : View.AccessibilityDelegate() {
+ override fun performAccessibilityAction(
+ host: View,
+ action: Int,
+ args: Bundle?
+ ): Boolean {
+ if (action == AccessibilityAction.ACTION_CLICK.id) {
+ desktopModeUiEventLogger.log(taskInfo, A11Y_APP_HANDLE_MENU_FULLSCREEN)
+ }
+ return super.performAccessibilityAction(host, action, args)
+ }
+ }
+
+ splitscreenBtn.accessibilityDelegate = object : View.AccessibilityDelegate() {
+ override fun performAccessibilityAction(
+ host: View,
+ action: Int,
+ args: Bundle?
+ ): Boolean {
+ if (action == AccessibilityAction.ACTION_CLICK.id) {
+ desktopModeUiEventLogger.log(taskInfo, A11Y_APP_HANDLE_MENU_SPLIT_SCREEN)
+ }
+ return super.performAccessibilityAction(host, action, args)
+ }
+ }
+
with(context) {
// Update a11y announcement out to say "double tap to enter Fullscreen"
ViewCompat.replaceAccessibilityAction(
@@ -762,6 +826,7 @@ class HandleMenu(
floatingBtn.isEnabled = !taskInfo.isPinned
floatingBtn.imageTintList = style.windowingButtonColor
desktopBtn.isGone = !shouldShowDesktopModeButton
+ desktopBtnSpace.isGone = !shouldShowDesktopModeButton
desktopBtn.isSelected = taskInfo.isFreeform
desktopBtn.isEnabled = !taskInfo.isFreeform
desktopBtn.imageTintList = style.windowingButtonColor
@@ -807,6 +872,7 @@ class HandleMenu(
newWindowBtn to shouldShowNewWindowButton,
manageWindowBtn to shouldShowManageWindowsButton,
changeAspectRatioBtn to shouldShowChangeAspectRatioButton,
+ restartBtn to shouldShowRestartButton,
).forEach { (button, shouldShow) ->
button.apply {
isGone = !shouldShow
@@ -871,6 +937,13 @@ class HandleMenu(
fun shouldShowChangeAspectRatioButton(taskInfo: RunningTaskInfo): Boolean =
taskInfo.appCompatTaskInfo.eligibleForUserAspectRatioButton() &&
taskInfo.windowingMode == WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+
+ /**
+ * Returns whether the restart button should be shown for the task. It usually means that
+ * the task has moved to a different display.
+ */
+ fun shouldShowRestartButton(taskInfo: RunningTaskInfo): Boolean =
+ taskInfo.appCompatTaskInfo.isRestartMenuEnabledForDisplayMove
}
}
@@ -889,8 +962,10 @@ interface HandleMenuFactory {
shouldShowManageWindowsButton: Boolean,
shouldShowChangeAspectRatioButton: Boolean,
shouldShowDesktopModeButton: Boolean,
+ shouldShowRestartButton: Boolean,
isBrowserApp: Boolean,
openInAppOrBrowserIntent: Intent?,
+ desktopModeUiEventLogger: DesktopModeUiEventLogger,
captionWidth: Int,
captionHeight: Int,
captionX: Int,
@@ -913,8 +988,10 @@ object DefaultHandleMenuFactory : HandleMenuFactory {
shouldShowManageWindowsButton: Boolean,
shouldShowChangeAspectRatioButton: Boolean,
shouldShowDesktopModeButton: Boolean,
+ shouldShowRestartButton: Boolean,
isBrowserApp: Boolean,
openInAppOrBrowserIntent: Intent?,
+ desktopModeUiEventLogger: DesktopModeUiEventLogger,
captionWidth: Int,
captionHeight: Int,
captionX: Int,
@@ -933,8 +1010,10 @@ object DefaultHandleMenuFactory : HandleMenuFactory {
shouldShowManageWindowsButton,
shouldShowChangeAspectRatioButton,
shouldShowDesktopModeButton,
+ shouldShowRestartButton,
isBrowserApp,
openInAppOrBrowserIntent,
+ desktopModeUiEventLogger,
captionWidth,
captionHeight,
captionX,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
index 5d1a7a0cc3a2..a10826fca3e2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
@@ -60,14 +60,16 @@ import android.window.TaskConstants
import androidx.compose.material3.ColorScheme
import androidx.compose.ui.graphics.toArgb
import androidx.core.animation.addListener
-import androidx.core.view.ViewCompat
-import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat
import androidx.core.view.isGone
import androidx.core.view.isVisible
import com.android.wm.shell.R
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_MAXIMIZE_MENU_MAXIMIZE
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_MAXIMIZE_MENU_RESIZE_LEFT
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_MAXIMIZE_MENU_RESIZE_RIGHT
import com.android.wm.shell.desktopmode.isTaskMaximized
import com.android.wm.shell.shared.animation.Interpolators.EMPHASIZED_DECELERATE
import com.android.wm.shell.shared.animation.Interpolators.FAST_OUT_LINEAR_IN
@@ -85,13 +87,14 @@ import java.util.function.Supplier
* to the right or left half of the screen.
*/
class MaximizeMenu(
- private val syncQueue: SyncTransactionQueue,
- private val rootTdaOrganizer: RootTaskDisplayAreaOrganizer,
- private val displayController: DisplayController,
- private val taskInfo: RunningTaskInfo,
- private val decorWindowContext: Context,
- private val positionSupplier: (Int, Int) -> Point,
- private val transactionSupplier: Supplier<Transaction> = Supplier { Transaction() }
+ private val syncQueue: SyncTransactionQueue,
+ private val rootTdaOrganizer: RootTaskDisplayAreaOrganizer,
+ private val displayController: DisplayController,
+ private val taskInfo: RunningTaskInfo,
+ private val decorWindowContext: Context,
+ private val positionSupplier: (Int, Int) -> Point,
+ private val transactionSupplier: Supplier<Transaction> = Supplier { Transaction() },
+ private val desktopModeUiEventLogger: DesktopModeUiEventLogger
) {
private var maximizeMenu: AdditionalViewHostViewContainer? = null
private var maximizeMenuView: MaximizeMenuView? = null
@@ -132,7 +135,7 @@ class MaximizeMenu(
onLeftSnapClickListener = onLeftSnapClickListener,
onRightSnapClickListener = onRightSnapClickListener,
onHoverListener = onHoverListener,
- onOutsideTouchListener = onOutsideTouchListener
+ onOutsideTouchListener = onOutsideTouchListener,
)
maximizeMenuView?.let { view ->
view.animateOpenMenu(onEnd = {
@@ -167,7 +170,7 @@ class MaximizeMenu(
onLeftSnapClickListener: () -> Unit,
onRightSnapClickListener: () -> Unit,
onHoverListener: (Boolean) -> Unit,
- onOutsideTouchListener: () -> Unit
+ onOutsideTouchListener: () -> Unit,
) {
val t = transactionSupplier.get()
val builder = SurfaceControl.Builder()
@@ -186,6 +189,7 @@ class MaximizeMenu(
"MaximizeMenu")
maximizeMenuView = MaximizeMenuView(
context = decorWindowContext,
+ desktopModeUiEventLogger = desktopModeUiEventLogger,
sizeToggleDirection = getSizeToggleDirection(),
immersiveConfig = if (showImmersiveOption) {
MaximizeMenuView.ImmersiveConfig.Visible(
@@ -265,6 +269,7 @@ class MaximizeMenu(
*/
class MaximizeMenuView(
context: Context,
+ private val desktopModeUiEventLogger: DesktopModeUiEventLogger,
private val sizeToggleDirection: SizeToggleDirection,
immersiveConfig: ImmersiveConfig,
showSnapOptions: Boolean,
@@ -425,7 +430,10 @@ class MaximizeMenu(
) {
super.onInitializeAccessibilityNodeInfo(host, info)
- info.addAction(AccessibilityAction.ACTION_CLICK)
+ info.addAction(AccessibilityAction(
+ AccessibilityAction.ACTION_CLICK.id,
+ context.getString(R.string.maximize_menu_talkback_action_maximize_restore_text)
+ ))
host.isClickable = true
}
@@ -435,6 +443,7 @@ class MaximizeMenu(
args: Bundle?
): Boolean {
if (action == AccessibilityAction.ACTION_CLICK.id) {
+ desktopModeUiEventLogger.log(taskInfo, A11Y_MAXIMIZE_MENU_MAXIMIZE)
onMaximizeClickListener?.invoke()
}
return super.performAccessibilityAction(host, action, args)
@@ -447,7 +456,10 @@ class MaximizeMenu(
info: AccessibilityNodeInfo
) {
super.onInitializeAccessibilityNodeInfo(host, info)
- info.addAction(AccessibilityAction.ACTION_CLICK)
+ info.addAction(AccessibilityAction(
+ AccessibilityAction.ACTION_CLICK.id,
+ context.getString(R.string.maximize_menu_talkback_action_snap_left_text)
+ ))
host.isClickable = true
}
@@ -457,6 +469,7 @@ class MaximizeMenu(
args: Bundle?
): Boolean {
if (action == AccessibilityAction.ACTION_CLICK.id) {
+ desktopModeUiEventLogger.log(taskInfo, A11Y_MAXIMIZE_MENU_RESIZE_LEFT)
onLeftSnapClickListener?.invoke()
}
return super.performAccessibilityAction(host, action, args)
@@ -469,7 +482,10 @@ class MaximizeMenu(
info: AccessibilityNodeInfo
) {
super.onInitializeAccessibilityNodeInfo(host, info)
- info.addAction(AccessibilityAction.ACTION_CLICK)
+ info.addAction(AccessibilityAction(
+ AccessibilityAction.ACTION_CLICK.id,
+ context.getString(R.string.maximize_menu_talkback_action_snap_right_text)
+ ))
host.isClickable = true
}
@@ -479,35 +495,13 @@ class MaximizeMenu(
args: Bundle?
): Boolean {
if (action == AccessibilityAction.ACTION_CLICK.id) {
+ desktopModeUiEventLogger.log(taskInfo, A11Y_MAXIMIZE_MENU_RESIZE_RIGHT)
onRightSnapClickListener?.invoke()
}
return super.performAccessibilityAction(host, action, args)
}
}
- with(context.resources) {
- ViewCompat.replaceAccessibilityAction(
- snapLeftButton,
- AccessibilityActionCompat.ACTION_CLICK,
- getString(R.string.maximize_menu_talkback_action_snap_left_text),
- null
- )
-
- ViewCompat.replaceAccessibilityAction(
- snapRightButton,
- AccessibilityActionCompat.ACTION_CLICK,
- getString(R.string.maximize_menu_talkback_action_snap_right_text),
- null
- )
-
- ViewCompat.replaceAccessibilityAction(
- sizeToggleButton,
- AccessibilityActionCompat.ACTION_CLICK,
- getString(R.string.maximize_menu_talkback_action_maximize_restore_text),
- null
- )
- }
-
// Maximize/restore button.
val sizeToggleBtnTextId = if (sizeToggleDirection == SizeToggleDirection.RESTORE)
R.string.desktop_mode_maximize_menu_restore_button_text
@@ -792,15 +786,15 @@ class MaximizeMenu(
}
/** Measure width of the root view of this menu. */
- fun measureWidth() : Int {
- rootView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
- return rootView.getMeasuredWidth()
+ fun measureWidth(): Int {
+ rootView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
+ return rootView.measuredWidth
}
/** Measure height of the root view of this menu. */
- fun measureHeight() : Int {
- rootView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
- return rootView.getMeasuredHeight()
+ fun measureHeight(): Int {
+ rootView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
+ return rootView.measuredHeight
}
private fun deactivateSnapOptions() {
@@ -1048,7 +1042,8 @@ interface MaximizeMenuFactory {
taskInfo: RunningTaskInfo,
decorWindowContext: Context,
positionSupplier: (Int, Int) -> Point,
- transactionSupplier: Supplier<Transaction>
+ transactionSupplier: Supplier<Transaction>,
+ desktopModeUiEventLogger: DesktopModeUiEventLogger,
): MaximizeMenu
}
@@ -1061,7 +1056,8 @@ object DefaultMaximizeMenuFactory : MaximizeMenuFactory {
taskInfo: RunningTaskInfo,
decorWindowContext: Context,
positionSupplier: (Int, Int) -> Point,
- transactionSupplier: Supplier<Transaction>
+ transactionSupplier: Supplier<Transaction>,
+ desktopModeUiEventLogger: DesktopModeUiEventLogger,
): MaximizeMenu {
return MaximizeMenu(
syncQueue,
@@ -1070,7 +1066,8 @@ object DefaultMaximizeMenuFactory : MaximizeMenuFactory {
taskInfo,
decorWindowContext,
positionSupplier,
- transactionSupplier
+ transactionSupplier,
+ desktopModeUiEventLogger
)
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ThemeUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ThemeUtils.kt
index c5057aa3cc18..f09f22fb1951 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ThemeUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ThemeUtils.kt
@@ -92,4 +92,7 @@ internal class DecorThemeUtil(private val context: Context) {
Theme.LIGHT -> lightColors
Theme.DARK -> darkColors
}
+
+ fun getColorScheme(isDarkMode: Boolean): ColorScheme =
+ if (isDarkMode) darkColors else lightColors
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt
index 57f8046065b4..ab6b72947586 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt
@@ -198,6 +198,11 @@ class DesktopTilingDividerWindowManager(
tilingDividerView?.onUiModeChange(isDarkMode)
}
+ /** Notifies the divider view of task info change and possible color change. */
+ fun onTaskInfoChange() {
+ tilingDividerView?.onTaskInfoChange()
+ }
+
/** Hides the divider bar. */
fun hideDividerBar() {
if (!dividerShown) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
index 854d9e1deef1..3e92d7d994a6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
@@ -375,6 +375,7 @@ class DesktopTilingWindowDecoration(
fun onTaskInfoChange(taskInfo: RunningTaskInfo) {
val isCurrentTaskInDarkMode = isTaskInDarkMode(taskInfo)
+ desktopTilingDividerWindowManager?.onTaskInfoChange()
if (isCurrentTaskInDarkMode == isDarkMode || !isTilingManagerInitialised) return
isDarkMode = isCurrentTaskInDarkMode
desktopTilingDividerWindowManager?.onUiModeChange(isDarkMode)
@@ -432,15 +433,38 @@ class DesktopTilingWindowDecoration(
startTransaction: Transaction,
finishTransaction: Transaction,
) {
+ var leftTaskBroughtToFront = false
+ var rightTaskBroughtToFront = false
+
for (change in info.changes) {
change.taskInfo?.let {
if (it.isFullscreen || isMinimized(change.mode, info.type)) {
removeTaskIfTiled(it.taskId, /* taskVanished= */ false, it.isFullscreen)
} else if (isEnteringPip(change, info.type)) {
removeTaskIfTiled(it.taskId, /* taskVanished= */ true, it.isFullscreen)
+ } else if (isTransitionToFront(change.mode, info.type)) {
+ handleTaskBroughtToFront(it.taskId)
+ leftTaskBroughtToFront =
+ leftTaskBroughtToFront ||
+ it.taskId == leftTaskResizingHelper?.taskInfo?.taskId
+ rightTaskBroughtToFront =
+ rightTaskBroughtToFront ||
+ it.taskId == rightTaskResizingHelper?.taskInfo?.taskId
}
}
}
+
+ if (leftTaskBroughtToFront && rightTaskBroughtToFront) {
+ desktopTilingDividerWindowManager?.showDividerBar()
+ }
+ }
+
+ private fun handleTaskBroughtToFront(taskId: Int) {
+ if (taskId == leftTaskResizingHelper?.taskInfo?.taskId) {
+ leftTaskResizingHelper?.onAppBecomingVisible()
+ } else if (taskId == rightTaskResizingHelper?.taskInfo?.taskId) {
+ rightTaskResizingHelper?.onAppBecomingVisible()
+ }
}
private fun isMinimized(changeMode: Int, infoType: Int): Boolean {
@@ -471,6 +495,9 @@ class DesktopTilingWindowDecoration(
return false
}
+ private fun isTransitionToFront(changeMode: Int, transitionType: Int): Boolean =
+ changeMode == TRANSIT_TO_FRONT && transitionType == TRANSIT_TO_FRONT
+
class AppResizingHelper(
val taskInfo: RunningTaskInfo,
val desktopModeWindowDecoration: DesktopModeWindowDecoration,
@@ -484,6 +511,7 @@ class DesktopTilingWindowDecoration(
) {
var isInitialised = false
var newBounds = Rect(bounds)
+ var visibilityCallback: (() -> Unit)? = null
private lateinit var resizeVeil: ResizeVeil
private val displayContext = displayController.getDisplayContext(taskInfo.displayId)
private val userContext =
@@ -521,6 +549,11 @@ class DesktopTilingWindowDecoration(
fun updateVeil(t: Transaction) = resizeVeil.updateTransactionWithResizeVeil(t, newBounds)
+ fun onAppBecomingVisible() {
+ visibilityCallback?.invoke()
+ visibilityCallback = null
+ }
+
fun hideVeil() = resizeVeil.hideVeil()
private fun createIconFactory(context: Context, dimensions: Int): BaseIconFactory {
@@ -593,11 +626,16 @@ class DesktopTilingWindowDecoration(
removeTask(leftTaskResizingHelper, taskVanished, shouldDelayUpdate)
leftTaskResizingHelper = null
val taskId = rightTaskResizingHelper?.taskInfo?.taskId
- if (taskId != null && taskRepository.isVisibleTask(taskId)) {
+ val callback: (() -> Unit)? = {
rightTaskResizingHelper
?.desktopModeWindowDecoration
?.updateDisabledResizingEdge(NONE, shouldDelayUpdate)
}
+ if (taskId != null && taskRepository.isVisibleTask(taskId)) {
+ callback?.invoke()
+ } else if (rightTaskResizingHelper != null) {
+ rightTaskResizingHelper?.visibilityCallback = callback
+ }
tearDownTiling()
return
}
@@ -607,11 +645,17 @@ class DesktopTilingWindowDecoration(
removeTask(rightTaskResizingHelper, taskVanished, shouldDelayUpdate)
rightTaskResizingHelper = null
val taskId = leftTaskResizingHelper?.taskInfo?.taskId
- if (taskId != null && taskRepository.isVisibleTask(taskId)) {
+ val callback: (() -> Unit)? = {
leftTaskResizingHelper
?.desktopModeWindowDecoration
?.updateDisabledResizingEdge(NONE, shouldDelayUpdate)
}
+ if (taskId != null && taskRepository.isVisibleTask(taskId)) {
+ callback?.invoke()
+ } else if (leftTaskResizingHelper != null) {
+ leftTaskResizingHelper?.visibilityCallback = callback
+ }
+
tearDownTiling()
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt
index 54dcd2d082dc..e9bd6f8ef85c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt
@@ -29,6 +29,7 @@ import android.view.RoundedCorner
import android.view.View
import android.view.ViewConfiguration
import android.widget.FrameLayout
+import androidx.compose.ui.graphics.toArgb
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
import com.android.wm.shell.R
@@ -36,6 +37,7 @@ import com.android.wm.shell.common.split.DividerHandleView
import com.android.wm.shell.common.split.DividerRoundedCorner
import com.android.wm.shell.shared.animation.Interpolators
import com.android.wm.shell.windowdecor.DragDetector
+import com.android.wm.shell.windowdecor.common.DecorThemeUtil
/** Divider for tiling split screen, currently mostly a copy of [DividerView]. */
class TilingDividerView : FrameLayout, View.OnTouchListener, DragDetector.MotionEventHandler {
@@ -56,6 +58,9 @@ class TilingDividerView : FrameLayout, View.OnTouchListener, DragDetector.Motion
@VisibleForTesting var handleY: IntRange = 0..0
private var canResize = false
private var resized = false
+ private var isDarkMode = false
+ private var decorThemeUtil = DecorThemeUtil(context)
+
/**
* Tracks divider bar visible bounds in screen-based coordination. Used to calculate with
* insets.
@@ -90,10 +95,12 @@ class TilingDividerView : FrameLayout, View.OnTouchListener, DragDetector.Motion
) {
callback = dividerMoveCallback
this.dividerBounds.set(dividerBounds)
+ this.isDarkMode = isDarkMode
+ paint.color = decorThemeUtil.getColorScheme(isDarkMode).outlineVariant.toArgb()
handle.setIsLeftRightSplit(true)
handle.setup(/* isSplitScreen= */ false, isDarkMode)
corners.setIsLeftRightSplit(true)
- corners.setup(/* isSplitScreen= */ false, isDarkMode)
+ corners.setup(/* isSplitScreen= */ false, paint.color)
handleRegionHeight = handleRegionSize.height
handleRegionWidth = handleRegionSize.width
cornersRadius =
@@ -108,17 +115,22 @@ class TilingDividerView : FrameLayout, View.OnTouchListener, DragDetector.Motion
}
fun onUiModeChange(isDarkMode: Boolean) {
+ this.isDarkMode = isDarkMode
handle.onUiModeChange(isDarkMode)
- corners.onUiModeChange(isDarkMode)
- paint.color =
- if (isDarkMode) {
- resources.getColor(R.color.tiling_divider_background_dark, null /* theme */)
- } else {
- resources.getColor(R.color.tiling_divider_background_light, null /* theme */)
- }
+ paint.color = decorThemeUtil.getColorScheme(isDarkMode).outlineVariant.toArgb()
+ corners.onUiModeChange(paint.color)
invalidate()
}
+ fun onTaskInfoChange() {
+ decorThemeUtil = DecorThemeUtil(context)
+ if (paint.color != decorThemeUtil.getColorScheme(isDarkMode).outlineVariant.toArgb()) {
+ paint.color = decorThemeUtil.getColorScheme(isDarkMode).outlineVariant.toArgb()
+ corners.onCornerColorChange(paint.color)
+ invalidate()
+ }
+ }
+
override fun onFinishInflate() {
super.onFinishInflate()
dividerBar = requireViewById(R.id.divider_bar)
@@ -128,15 +140,10 @@ class TilingDividerView : FrameLayout, View.OnTouchListener, DragDetector.Motion
resources.getDimensionPixelSize(R.dimen.docked_stack_divider_lift_elevation)
setOnTouchListener(this)
setWillNotDraw(false)
- paint.color =
- if (
- context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK ==
- Configuration.UI_MODE_NIGHT_YES
- ) {
- resources.getColor(R.color.tiling_divider_background_dark, /* theme= */null)
- } else {
- resources.getColor(R.color.tiling_divider_background_light, /* theme= */ null)
- }
+ val isDarkMode =
+ context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK ==
+ Configuration.UI_MODE_NIGHT_YES
+ paint.color = decorThemeUtil.getColorScheme(isDarkMode).outlineVariant.toArgb()
paint.isAntiAlias = true
paint.style = Paint.Style.FILL
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
index 9d16be59ba34..f09c500dcdd0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
@@ -39,6 +39,8 @@ import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.Accessibilit
import com.android.internal.policy.SystemBarUtils
import com.android.window.flags.Flags
import com.android.wm.shell.R
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_APP_HANDLE_MENU_OPENED
import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper
import com.android.wm.shell.windowdecor.AppHandleAnimator
import com.android.wm.shell.windowdecor.WindowManagerWrapper
@@ -53,7 +55,8 @@ class AppHandleViewHolder(
onCaptionTouchListener: View.OnTouchListener,
onCaptionButtonClickListener: OnClickListener,
private val windowManagerWrapper: WindowManagerWrapper,
- private val handler: Handler
+ private val handler: Handler,
+ private val desktopModeUiEventLogger: DesktopModeUiEventLogger,
) : WindowDecorationViewHolder<AppHandleViewHolder.HandleData>(rootView) {
data class HandleData(
@@ -201,6 +204,7 @@ class AppHandleViewHolder(
// Passthrough the a11y click action so the caption handle, so that app handle menu
// is opened on a11y click, similar to a real click
if (action == AccessibilityAction.ACTION_CLICK.id) {
+ desktopModeUiEventLogger.log(taskInfo, A11Y_APP_HANDLE_MENU_OPENED)
captionHandle.performClick()
}
return super.performAccessibilityAction(host, action, args)
@@ -216,10 +220,13 @@ class AppHandleViewHolder(
}
}
- // Update a11y action text so that Talkback announces "Press double tap to open app handle
- // menu" while focused on status bar input layer
+ // Update a11y action text so that Talkback announces "Press double tap to open menu"
+ // while focused on status bar input layer
ViewCompat.replaceAccessibilityAction(
- view, AccessibilityActionCompat.ACTION_CLICK, "Open app handle menu", null
+ view,
+ AccessibilityActionCompat.ACTION_CLICK,
+ context.getString(R.string.app_handle_chip_accessibility_announce),
+ null
)
}
@@ -297,12 +304,14 @@ class AppHandleViewHolder(
onCaptionButtonClickListener: OnClickListener,
windowManagerWrapper: WindowManagerWrapper,
handler: Handler,
+ desktopModeUiEventLogger: DesktopModeUiEventLogger,
): AppHandleViewHolder = AppHandleViewHolder(
rootView,
onCaptionTouchListener,
onCaptionButtonClickListener,
windowManagerWrapper,
handler,
+ desktopModeUiEventLogger,
)
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt
index 0e2698d0b6fa..d2e3a07f4651 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt
@@ -49,6 +49,13 @@ import com.android.internal.R.color.materialColorSurfaceContainerHigh
import com.android.internal.R.color.materialColorSurfaceContainerLow
import com.android.internal.R.color.materialColorSurfaceDim
import com.android.wm.shell.R
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_ACTION_MAXIMIZE_RESTORE
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_ACTION_RESIZE_LEFT
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_ACTION_RESIZE_RIGHT
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_APP_WINDOW_CLOSE_BUTTON
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_APP_WINDOW_MAXIMIZE_RESTORE_BUTTON
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_APP_WINDOW_MINIMIZE_BUTTON
import com.android.wm.shell.windowdecor.MaximizeButtonView
import com.android.wm.shell.windowdecor.common.DecorThemeUtil
import com.android.wm.shell.windowdecor.common.DrawableInsets
@@ -75,6 +82,7 @@ class AppHeaderViewHolder(
mOnRightSnapClickListener: () -> Unit,
mOnMaximizeOrRestoreClickListener: () -> Unit,
onMaximizeHoverAnimationFinishedListener: () -> Unit,
+ private val desktopModeUiEventLogger: DesktopModeUiEventLogger,
) : WindowDecorationViewHolder<AppHeaderViewHolder.HeaderData>(rootView) {
data class HeaderData(
@@ -151,6 +159,8 @@ class AppHeaderViewHolder(
private lateinit var a11yTextMaximize: String
private lateinit var a11yTextRestore: String
+ private lateinit var currentTaskInfo: RunningTaskInfo
+
init {
captionView.setOnTouchListener(onCaptionTouchListener)
captionHandle.setOnTouchListener(onCaptionTouchListener)
@@ -197,9 +207,18 @@ class AppHeaderViewHolder(
args: Bundle?
): Boolean {
when (action) {
- R.id.action_snap_left -> mOnLeftSnapClickListener.invoke()
- R.id.action_snap_right -> mOnRightSnapClickListener.invoke()
- R.id.action_maximize_restore -> mOnMaximizeOrRestoreClickListener.invoke()
+ R.id.action_snap_left -> {
+ desktopModeUiEventLogger.log(currentTaskInfo, A11Y_ACTION_RESIZE_LEFT)
+ mOnLeftSnapClickListener.invoke()
+ }
+ R.id.action_snap_right -> {
+ desktopModeUiEventLogger.log(currentTaskInfo, A11Y_ACTION_RESIZE_RIGHT)
+ mOnRightSnapClickListener.invoke()
+ }
+ R.id.action_maximize_restore -> {
+ desktopModeUiEventLogger.log(currentTaskInfo, A11Y_ACTION_MAXIMIZE_RESTORE)
+ mOnMaximizeOrRestoreClickListener.invoke()
+ }
}
return super.performAccessibilityAction(host, action, args)
@@ -224,10 +243,56 @@ class AppHeaderViewHolder(
args: Bundle?
): Boolean {
when (action) {
- AccessibilityAction.ACTION_CLICK.id -> host.performClick()
- R.id.action_snap_left -> mOnLeftSnapClickListener.invoke()
- R.id.action_snap_right -> mOnRightSnapClickListener.invoke()
- R.id.action_maximize_restore -> mOnMaximizeOrRestoreClickListener.invoke()
+ AccessibilityAction.ACTION_CLICK.id -> {
+ desktopModeUiEventLogger.log(
+ currentTaskInfo, A11Y_APP_WINDOW_MAXIMIZE_RESTORE_BUTTON
+ )
+ host.performClick()
+ }
+ R.id.action_snap_left -> {
+ desktopModeUiEventLogger.log(currentTaskInfo, A11Y_ACTION_RESIZE_LEFT)
+ mOnLeftSnapClickListener.invoke()
+ }
+ R.id.action_snap_right -> {
+ desktopModeUiEventLogger.log(currentTaskInfo, A11Y_ACTION_RESIZE_RIGHT)
+ mOnRightSnapClickListener.invoke()
+ }
+ R.id.action_maximize_restore -> {
+ desktopModeUiEventLogger.log(currentTaskInfo, A11Y_ACTION_MAXIMIZE_RESTORE)
+ mOnMaximizeOrRestoreClickListener.invoke()
+ }
+ }
+
+ return super.performAccessibilityAction(host, action, args)
+ }
+ }
+
+ closeWindowButton.accessibilityDelegate = object : View.AccessibilityDelegate() {
+ override fun performAccessibilityAction(
+ host: View,
+ action: Int,
+ args: Bundle?
+ ): Boolean {
+ when (action) {
+ AccessibilityAction.ACTION_CLICK.id -> desktopModeUiEventLogger.log(
+ currentTaskInfo, A11Y_APP_WINDOW_CLOSE_BUTTON
+ )
+ }
+
+ return super.performAccessibilityAction(host, action, args)
+ }
+ }
+
+ minimizeWindowButton.accessibilityDelegate = object : View.AccessibilityDelegate() {
+ override fun performAccessibilityAction(
+ host: View,
+ action: Int,
+ args: Bundle?
+ ): Boolean {
+ when (action) {
+ AccessibilityAction.ACTION_CLICK.id -> desktopModeUiEventLogger.log(
+ currentTaskInfo, A11Y_APP_WINDOW_MINIMIZE_BUTTON
+ )
}
return super.performAccessibilityAction(host, action, args)
@@ -310,7 +375,8 @@ class AppHeaderViewHolder(
enableMaximizeLongClick: Boolean,
isCaptionVisible: Boolean,
) {
- if (DesktopModeFlags.ENABLE_THEMED_APP_HEADERS.isTrue()) {
+ currentTaskInfo = taskInfo
+ if (DesktopModeFlags.ENABLE_THEMED_APP_HEADERS.isTrue) {
bindDataWithThemedHeaders(
taskInfo,
isTaskMaximized,
@@ -361,7 +427,7 @@ class AppHeaderViewHolder(
minimizeWindowButton.background = getDrawable(1)
}
maximizeButtonView.setAnimationTints(isDarkMode())
- minimizeWindowButton.isGone = !DesktopModeFlags.ENABLE_MINIMIZE_BUTTON.isTrue()
+ minimizeWindowButton.isGone = !DesktopModeFlags.ENABLE_MINIMIZE_BUTTON.isTrue
}
private fun bindDataWithThemedHeaders(
@@ -417,7 +483,7 @@ class AppHeaderViewHolder(
drawableInsets = minimizeDrawableInsets
)
}
- minimizeWindowButton.isGone = !DesktopModeFlags.ENABLE_MINIMIZE_BUTTON.isTrue()
+ minimizeWindowButton.isGone = !DesktopModeFlags.ENABLE_MINIMIZE_BUTTON.isTrue
// Maximize button.
maximizeButtonView.apply {
setAnimationTints(
@@ -761,6 +827,7 @@ class AppHeaderViewHolder(
mOnRightSnapClickListener: () -> Unit,
mOnMaximizeOrRestoreClickListener: () -> Unit,
onMaximizeHoverAnimationFinishedListener: () -> Unit,
+ desktopModeUiEventLogger: DesktopModeUiEventLogger
): AppHeaderViewHolder = AppHeaderViewHolder(
rootView,
onCaptionTouchListener,
@@ -771,6 +838,7 @@ class AppHeaderViewHolder(
mOnRightSnapClickListener,
mOnMaximizeOrRestoreClickListener,
onMaximizeHoverAnimationFinishedListener,
+ desktopModeUiEventLogger,
)
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayControllerTests.java
index 3d5e9495e29d..1e459d55ed77 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayControllerTests.java
@@ -18,6 +18,7 @@ package com.android.wm.shell.common;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -231,4 +232,24 @@ public class DisplayControllerTests extends ShellTestCase {
assertEquals(DISPLAY_ABS_BOUNDS_1,
mController.getDisplayLayout(DISPLAY_ID_1).globalBoundsDp());
}
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WINDOW_DRAG)
+ public void onDisplayConfigurationChanged_reInitDisplayLayout()
+ throws RemoteException {
+ ExtendedMockito.doReturn(true)
+ .when(() -> DesktopModeStatus.canEnterDesktopMode(any()));
+ mController.onInit();
+ mController.addDisplayWindowListener(mListener);
+
+ mCapturedTopologyListener.accept(mMockTopology);
+
+ DisplayLayout displayLayoutBefore = mController.getDisplayLayout(DISPLAY_ID_0);
+ mDisplayContainerListener.onDisplayConfigurationChanged(DISPLAY_ID_0, new Configuration());
+ DisplayLayout displayLayoutAfter = mController.getDisplayLayout(DISPLAY_ID_0);
+
+ assertNotSame(displayLayoutBefore, displayLayoutAfter);
+ assertEquals(DISPLAY_ABS_BOUNDS_0,
+ mController.getDisplayLayout(DISPLAY_ID_0).globalBoundsDp());
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
index 3fb008377d6e..6ede990df15e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
@@ -1280,6 +1280,18 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() {
@Test
@EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun removeDesk_multipleDesks_active_marksInactiveInDisplay() {
+ repo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 2)
+ repo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 3)
+ repo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 3)
+
+ repo.removeDesk(deskId = 3)
+
+ assertThat(repo.getActiveDeskId(displayId = DEFAULT_DISPLAY)).isNull()
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
fun removeDesk_multipleDesks_inactive_removes() {
repo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 2)
repo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 3)
@@ -1292,6 +1304,18 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() {
}
@Test
+ @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun removeDesk_multipleDesks_inactive_keepsOtherDeskActiveInDisplay() {
+ repo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 2)
+ repo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 3)
+ repo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 3)
+
+ repo.removeDesk(deskId = 2)
+
+ assertThat(repo.getActiveDeskId(displayId = DEFAULT_DISPLAY)).isEqualTo(3)
+ }
+
+ @Test
@EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND, FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE)
fun removeDesk_removesFromPersistence() =
runTest(StandardTestDispatcher()) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index 71e46bcb0698..b577667d8279 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -1526,6 +1526,37 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ fun launchIntent_taskInDesktopMode_onSecondaryDisplay_transitionStarted() {
+ setUpLandscapeDisplay()
+ taskRepository.addDesk(SECOND_DISPLAY, deskId = 2)
+ val intent = Intent().setComponent(homeComponentName)
+ whenever(
+ desktopMixedTransitionHandler.startLaunchTransition(
+ eq(TRANSIT_OPEN),
+ any(),
+ anyOrNull(),
+ anyOrNull(),
+ anyOrNull(),
+ )
+ )
+ .thenReturn(Binder())
+
+ controller.startLaunchIntentTransition(intent, Bundle.EMPTY, SECOND_DISPLAY)
+
+ val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_OPEN)
+ // We expect two actions: open the app and start the desk
+ assertThat(wct.hierarchyOps).hasSize(2)
+ val hOps0 = wct.hierarchyOps[0]
+ val hOps1 = wct.hierarchyOps[1]
+ assertThat(hOps0.type).isEqualTo(HIERARCHY_OP_TYPE_PENDING_INTENT)
+ val activityOptions0 = ActivityOptions.fromBundle(hOps0.launchOptions)
+ assertThat(activityOptions0.launchDisplayId).isEqualTo(SECOND_DISPLAY)
+ assertThat(hOps1.type).isEqualTo(HIERARCHY_OP_TYPE_PENDING_INTENT)
+ val activityOptions1 = ActivityOptions.fromBundle(hOps1.launchOptions)
+ assertThat(activityOptions1.launchDisplayId).isEqualTo(SECOND_DISPLAY)
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
fun addMoveToDeskTaskChanges_landscapeDevice_userFullscreenOverride_defaultPortraitBounds() {
setUpLandscapeDisplay()
@@ -7615,6 +7646,106 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
assertNull(latestWct.hierarchyOps.find { op -> op.container == wallpaperToken.asBinder() })
}
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun onRecentsInDesktopAnimationFinishing_returningToApp_noDeskDeactivation() {
+ val deskId = 0
+ taskRepository.setActiveDesk(DEFAULT_DISPLAY, deskId)
+
+ val transition = Binder()
+ val finishWct = WindowContainerTransaction()
+ controller.onRecentsInDesktopAnimationFinishing(
+ transition = transition,
+ finishWct = finishWct,
+ returnToApp = true,
+ )
+
+ verify(desksOrganizer, never()).deactivateDesk(finishWct, deskId)
+ verify(desksTransitionsObserver, never())
+ .addPendingTransition(
+ argThat { t -> t.token == transition && t is DeskTransition.DeactivateDesk }
+ )
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun onRecentsInDesktopAnimationFinishing_noActiveDesk_noDeskDeactivation() {
+ val deskId = 0
+ taskRepository.setDeskInactive(deskId)
+
+ val transition = Binder()
+ val finishWct = WindowContainerTransaction()
+ controller.onRecentsInDesktopAnimationFinishing(
+ transition = transition,
+ finishWct = finishWct,
+ returnToApp = false,
+ )
+
+ verify(desksOrganizer, never()).deactivateDesk(finishWct, deskId)
+ verify(desksTransitionsObserver, never())
+ .addPendingTransition(
+ argThat { t -> t.token == transition && t is DeskTransition.DeactivateDesk }
+ )
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun onRecentsInDesktopAnimationFinishing_activeDesk_notReturningToDesk_deactivatesDesk() {
+ val deskId = 0
+ taskRepository.setActiveDesk(DEFAULT_DISPLAY, deskId)
+
+ val transition = Binder()
+ val finishWct = WindowContainerTransaction()
+ controller.onRecentsInDesktopAnimationFinishing(
+ transition = transition,
+ finishWct = finishWct,
+ returnToApp = false,
+ )
+
+ verify(desksOrganizer).deactivateDesk(finishWct, deskId)
+ verify(desksTransitionsObserver)
+ .addPendingTransition(DeskTransition.DeactivateDesk(transition, deskId))
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun onRecentsInDesktopAnimationFinishing_activeDesk_notReturningToDesk_notifiesDesktopExit() {
+ val deskId = 0
+ taskRepository.setActiveDesk(DEFAULT_DISPLAY, deskId)
+
+ val transition = Binder()
+ val finishWct = WindowContainerTransaction()
+ controller.onRecentsInDesktopAnimationFinishing(
+ transition = transition,
+ finishWct = finishWct,
+ returnToApp = false,
+ )
+
+ verify(desktopModeEnterExitTransitionListener).onExitDesktopModeTransitionStarted(any())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun onRecentsInDesktopAnimationFinishing_activeDesk_notReturningToDesk_doesNotBringUpWallpaperOrHome() {
+ val deskId = 0
+ taskRepository.setActiveDesk(DEFAULT_DISPLAY, deskId)
+
+ val transition = Binder()
+ val finishWct = WindowContainerTransaction()
+ controller.onRecentsInDesktopAnimationFinishing(
+ transition = transition,
+ finishWct = finishWct,
+ returnToApp = false,
+ )
+
+ finishWct.assertWithoutHop { hop ->
+ hop.type == HIERARCHY_OP_TYPE_REORDER &&
+ hop.container == wallpaperToken.asBinder() &&
+ !hop.toTop
+ }
+ finishWct.assertWithoutHop { hop -> hop.type == HIERARCHY_OP_TYPE_PENDING_INTENT }
+ }
+
private class RunOnStartTransitionCallback : ((IBinder) -> Unit) {
var invocations = 0
private set
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserverTest.kt
index 409ca57715fc..e55d7cbb73e1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserverTest.kt
@@ -264,6 +264,36 @@ class DesksTransitionObserverTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun onTransitionFinish_deactivateDesk_updatesRepository() {
+ val transition = Binder()
+ val deactivateTransition = DeskTransition.DeactivateDesk(transition, deskId = 5)
+ repository.addDesk(DEFAULT_DISPLAY, deskId = 5)
+ repository.setActiveDesk(DEFAULT_DISPLAY, deskId = 5)
+
+ observer.addPendingTransition(deactivateTransition)
+ observer.onTransitionFinished(transition)
+
+ assertThat(repository.getActiveDeskId(DEFAULT_DISPLAY)).isNull()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun onTransitionMergedAndFinished_deactivateDesk_updatesRepository() {
+ val transition = Binder()
+ val deactivateTransition = DeskTransition.DeactivateDesk(transition, deskId = 5)
+ repository.addDesk(DEFAULT_DISPLAY, deskId = 5)
+ repository.setActiveDesk(DEFAULT_DISPLAY, deskId = 5)
+
+ observer.addPendingTransition(deactivateTransition)
+ val bookEndTransition = Binder()
+ observer.onTransitionMerged(merged = transition, playing = bookEndTransition)
+ observer.onTransitionFinished(bookEndTransition)
+
+ assertThat(repository.getActiveDeskId(DEFAULT_DISPLAY)).isNull()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
fun onTransitionReady_twoPendingTransitions_handlesBoth() {
val transition = Binder()
// Active one desk and deactivate another in different displays, such as in some
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
index 714e5f486285..69a42164071b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
@@ -373,6 +373,25 @@ public class FreeformTaskTransitionObserverTest extends ShellTestCase {
verify(mDesksTransitionObserver).onTransitionReady(transition, info);
}
+ @Test
+ public void onTransitionMerged_forwardsToDesksTransitionObserver() {
+ final IBinder merged = mock(IBinder.class);
+ final IBinder playing = mock(IBinder.class);
+
+ mTransitionObserver.onTransitionMerged(merged, playing);
+
+ verify(mDesksTransitionObserver).onTransitionMerged(merged, playing);
+ }
+
+ @Test
+ public void onTransitionFinished_forwardsToDesksTransitionObserver() {
+ final IBinder transition = mock(IBinder.class);
+
+ mTransitionObserver.onTransitionFinished(transition, /* aborted = */ false);
+
+ verify(mDesksTransitionObserver).onTransitionFinished(transition);
+ }
+
private static TransitionInfo.Change createChange(int mode, int taskId, int windowingMode) {
final ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
taskInfo.taskId = taskId;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
index 4c9c2f14d805..2126d1d9b986 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
@@ -55,6 +55,7 @@ import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.common.MultiDisplayDragMoveIndicatorController
import com.android.wm.shell.common.MultiInstanceHelper
import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.compatui.api.CompatUIHandler
import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler
import com.android.wm.shell.desktopmode.DesktopImmersiveController
import com.android.wm.shell.desktopmode.DesktopModeEventLogger
@@ -144,6 +145,7 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
mock<DesktopActivityOrientationChangeHandler>()
protected val mockMultiDisplayDragMoveIndicatorController =
mock<MultiDisplayDragMoveIndicatorController>()
+ protected val mockCompatUIHandler = mock<CompatUIHandler>()
protected val mockInputManager = mock<InputManager>()
private val mockTaskPositionerFactory =
mock<DesktopModeWindowDecorViewModel.TaskPositionerFactory>()
@@ -243,6 +245,7 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
desktopModeCompatPolicy,
mockTilingWindowDecoration,
mockMultiDisplayDragMoveIndicatorController,
+ mockCompatUIHandler,
)
desktopModeWindowDecorViewModel.setSplitScreenController(mockSplitScreenController)
whenever(mockDisplayController.getDisplayLayout(any())).thenReturn(mockDisplayLayout)
@@ -334,7 +337,7 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
mockDesktopModeWindowDecorFactory.create(
any(), any(), any(), any(), any(), any(), any(), eq(task), any(), any(), any(),
any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(),
- any(), any(), any(), any())
+ any(), any(), any(), any(), any())
).thenReturn(decoration)
decoration.mTaskInfo = task
whenever(decoration.user).thenReturn(mockUserHandle)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
index dd777a5ed270..0908f56e1cfb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -106,6 +106,7 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.desktopmode.CaptionState;
import com.android.wm.shell.desktopmode.DesktopModeEventLogger;
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger;
import com.android.wm.shell.desktopmode.DesktopRepository;
import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository;
@@ -246,6 +247,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
@Mock
private DesktopModeEventLogger mDesktopModeEventLogger;
@Mock
+ private DesktopModeUiEventLogger mDesktopModeUiEventLogger;
+ @Mock
private DesktopRepository mDesktopRepository;
@Mock
private WindowDecorTaskResourceLoader mMockTaskResourceLoader;
@@ -303,15 +306,15 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
doReturn(mInsetsState).when(mMockDisplayController).getInsetsState(anyInt());
when(mMockHandleMenuFactory.create(any(), any(), any(), any(), any(), anyInt(), any(),
anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(),
- any(), anyInt(), anyInt(), anyInt(), anyInt()))
+ anyBoolean(), any(), any(), anyInt(), anyInt(), anyInt(), anyInt()))
.thenReturn(mMockHandleMenu);
when(mMockMultiInstanceHelper.supportsMultiInstanceSplit(any(), anyInt()))
.thenReturn(false);
when(mMockAppHeaderViewHolderFactory
- .create(any(), any(), any(), any(), any(), any(), any(), any(), any()))
+ .create(any(), any(), any(), any(), any(), any(), any(), any(), any(), any()))
.thenReturn(mMockAppHeaderViewHolder);
when(mMockAppHandleViewHolderFactory
- .create(any(), any(), any(), any(), any()))
+ .create(any(), any(), any(), any(), any(), any()))
.thenReturn(mMockAppHandleViewHolder);
when(mMockDesktopUserRepositories.getCurrent()).thenReturn(mDesktopRepository);
when(mMockDesktopUserRepositories.getProfile(anyInt())).thenReturn(mDesktopRepository);
@@ -1450,6 +1453,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
any(),
any(),
any(),
+ any(),
anyBoolean()
);
// Run runnable to set captured link to used
@@ -1487,6 +1491,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
any(),
any(),
any(),
+ any(),
anyBoolean()
);
openInBrowserCaptor.getValue().invoke(new Intent(Intent.ACTION_MAIN, TEST_URI1));
@@ -1519,6 +1524,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
any(),
any(),
any(),
+ any(),
anyBoolean()
);
@@ -1584,6 +1590,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
any(),
any(),
any(),
+ any(),
closeClickListener.capture(),
any(),
anyBoolean()
@@ -1617,6 +1624,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
any(),
any(),
any(),
+ any(),
/* forceShowSystemBars= */ eq(true)
);
}
@@ -1790,9 +1798,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
private void verifyHandleMenuCreated(@Nullable Uri uri) {
verify(mMockHandleMenuFactory).create(any(), any(), any(), any(), any(), anyInt(),
any(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(),
- anyBoolean(), argThat(intent ->
+ anyBoolean(), anyBoolean(), argThat(intent ->
(uri == null && intent == null) || intent.getData().equals(uri)),
- anyInt(), anyInt(), anyInt(), anyInt());
+ any(), anyInt(), anyInt(), anyInt(), anyInt());
}
private void createMaximizeMenu(DesktopModeWindowDecoration decoration) {
@@ -1889,7 +1897,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
new WindowManagerWrapper(mMockWindowManager), mMockSurfaceControlViewHostFactory,
mMockWindowDecorViewHostSupplier, maximizeMenuFactory, mMockHandleMenuFactory,
mMockMultiInstanceHelper, mMockCaptionHandleRepository, mDesktopModeEventLogger,
- mDesktopModeCompatPolicy);
+ mDesktopModeUiEventLogger, mDesktopModeCompatPolicy);
windowDecor.setCaptionListeners(mMockTouchEventListener, mMockTouchEventListener,
mMockTouchEventListener, mMockTouchEventListener);
windowDecor.setExclusionRegionListener(mMockExclusionRegionListener);
@@ -2015,7 +2023,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
@NonNull Context decorWindowContext,
@NonNull Function2<? super Integer,? super Integer,? extends Point>
positionSupplier,
- @NonNull Supplier<SurfaceControl.Transaction> transactionSupplier) {
+ @NonNull Supplier<SurfaceControl.Transaction> transactionSupplier,
+ @NonNull DesktopModeUiEventLogger desktopModeUiEventLogger) {
return mMaximizeMenu;
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
index 2e46f6312d03..b0b95c97ae18 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
@@ -43,6 +43,7 @@ import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.TestRunningTaskInfoBuilder
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger
import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT
import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED
@@ -95,6 +96,8 @@ class HandleMenuTest : ShellTestCase() {
private lateinit var mockTaskResourceLoader: WindowDecorTaskResourceLoader
@Mock
private lateinit var mockAppIcon: Bitmap
+ @Mock
+ private lateinit var mockDesktopModeUiEventLogger: DesktopModeUiEventLogger
private lateinit var handleMenu: HandleMenu
@@ -287,8 +290,10 @@ class HandleMenuTest : ShellTestCase() {
shouldShowManageWindowsButton = false,
shouldShowChangeAspectRatioButton = false,
shouldShowDesktopModeButton = true,
+ shouldShowRestartButton = true,
isBrowserApp = false,
null /* openInAppOrBrowserIntent */,
+ mockDesktopModeUiEventLogger,
captionWidth = HANDLE_WIDTH,
captionHeight = 50,
captionX = captionX,
@@ -304,6 +309,7 @@ class HandleMenuTest : ShellTestCase() {
onChangeAspectRatioClickListener = mock(),
openInAppOrBrowserClickListener = mock(),
onOpenByDefaultClickListener = mock(),
+ onRestartClickListener = mock(),
onCloseMenuClickListener = mock(),
onOutsideTouchListener = mock(),
forceShowSystemBars = forceShowSystemBars
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt
index e5f8d7d34a47..0adca5e864bf 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt
@@ -587,6 +587,31 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() {
}
@Test
+ fun tilingDivider_shouldBeShown_whenTiledTasksBecomeVisible() {
+ val task1 = createVisibleTask()
+ val task2 = createVisibleTask()
+ val additionalTaskHelper: DesktopTilingWindowDecoration.AppResizingHelper = mock()
+ whenever(tiledTaskHelper.taskInfo).thenReturn(task1)
+ whenever(tiledTaskHelper.desktopModeWindowDecoration).thenReturn(desktopWindowDecoration)
+ whenever(additionalTaskHelper.taskInfo).thenReturn(task2)
+ whenever(additionalTaskHelper.desktopModeWindowDecoration)
+ .thenReturn(desktopWindowDecoration)
+
+ tilingDecoration.leftTaskResizingHelper = tiledTaskHelper
+ tilingDecoration.rightTaskResizingHelper = additionalTaskHelper
+ tilingDecoration.desktopTilingDividerWindowManager = desktopTilingDividerWindowManager
+ val changeInfo = createTransitFrontTransition(task1, task2)
+ tilingDecoration.onTransitionReady(
+ transition = mock(),
+ info = changeInfo,
+ startTransaction = mock(),
+ finishTransaction = mock(),
+ )
+
+ verify(desktopTilingDividerWindowManager, times(1)).showDividerBar()
+ }
+
+ @Test
fun taskNotTiled_shouldNotBeRemoved_whenNotTiled() {
val task1 = createVisibleTask()
val task2 = createVisibleTask()
@@ -762,6 +787,30 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() {
)
}
+ private fun createTransitFrontTransition(
+ task1: RunningTaskInfo?,
+ task2: RunningTaskInfo?,
+ type: Int = TRANSIT_TO_FRONT,
+ ) =
+ TransitionInfo(type, /* flags= */ 0).apply {
+ addChange(
+ Change(mock(), mock()).apply {
+ mode = TRANSIT_TO_FRONT
+ parent = null
+ taskInfo = task1
+ flags = flags
+ }
+ )
+ addChange(
+ Change(mock(), mock()).apply {
+ mode = TRANSIT_TO_FRONT
+ parent = null
+ taskInfo = task2
+ flags = flags
+ }
+ )
+ }
+
companion object {
private val NON_STABLE_BOUNDS_MOCK = Rect(50, 55, 100, 100)
private val STABLE_BOUNDS_MOCK = Rect(0, 0, 100, 100)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolderTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolderTest.kt
index 2c3009cb8dc4..e23019a59307 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolderTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolderTest.kt
@@ -26,6 +26,7 @@ import androidx.test.filters.SmallTest
import com.android.internal.policy.SystemBarUtils
import com.android.wm.shell.R
import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger
import com.android.wm.shell.windowdecor.WindowManagerWrapper
import org.junit.Before
import org.junit.runner.RunWith
@@ -57,6 +58,7 @@ class AppHandleViewHolderTest : ShellTestCase() {
private val mockWindowManagerWrapper = mock<WindowManagerWrapper>()
private val mockHandler = mock<Handler>()
private val mockTaskInfo = mock<RunningTaskInfo>()
+ private val mockDesktopModeUiEventLogger = mock<DesktopModeUiEventLogger>()
@Before
fun setup() {
@@ -92,7 +94,8 @@ class AppHandleViewHolderTest : ShellTestCase() {
mockOnTouchListener,
mockOnClickListener,
mockWindowManagerWrapper,
- mockHandler
+ mockHandler,
+ mockDesktopModeUiEventLogger,
)
}
}
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 714f6e41ff05..e09ab5fd1643 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -879,10 +879,10 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry(
// if we don't have a result yet
if (!final_result ||
// or this config is better before the locale than the existing result
- result->config.isBetterThanBeforeLocale(final_result->config, desired_config) ||
+ result->config.isBetterThanBeforeLocale(final_result->config, *desired_config) ||
// or the existing config isn't better before locale and this one specifies a locale
// whereas the existing one doesn't
- (!final_result->config.isBetterThanBeforeLocale(result->config, desired_config)
+ (!final_result->config.isBetterThanBeforeLocale(result->config, *desired_config)
&& has_locale && !final_has_locale)) {
final_result = result.value();
final_overlaid = overlaid;
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 8ecd6ba9b253..6ec605c2ced5 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -2615,16 +2615,14 @@ bool ResTable_config::isLocaleBetterThan(const ResTable_config& o,
}
bool ResTable_config::isBetterThanBeforeLocale(const ResTable_config& o,
- const ResTable_config* requested) const {
- if (requested) {
- if (imsi || o.imsi) {
- if ((mcc != o.mcc) && requested->mcc) {
- return (mcc);
- }
+ const ResTable_config& requested) const {
+ if (imsi || o.imsi) {
+ if ((mcc != o.mcc) && requested.mcc) {
+ return mcc;
+ }
- if ((mnc != o.mnc) && requested->mnc) {
- return (mnc);
- }
+ if ((mnc != o.mnc) && requested.mnc) {
+ return mnc;
}
}
return false;
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 63b28da075cd..bd72d3741460 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -1416,7 +1416,10 @@ struct ResTable_config
// match the requested configuration at all.
bool isLocaleBetterThan(const ResTable_config& o, const ResTable_config* requested) const;
- bool isBetterThanBeforeLocale(const ResTable_config& o, const ResTable_config* requested) const;
+ // The first part of isBetterThan() that only compares the fields that are higher priority than
+ // the locale. Use it when you need to do custom locale matching to filter out the configs prior
+ // to that.
+ bool isBetterThanBeforeLocale(const ResTable_config& o, const ResTable_config& requested) const;
String8 toString() const;
diff --git a/libs/hwui/jni/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp
index 104ece6582f5..b6a2ad7064a9 100644
--- a/libs/hwui/jni/Bitmap.cpp
+++ b/libs/hwui/jni/Bitmap.cpp
@@ -28,7 +28,9 @@
#include "SkRefCnt.h"
#include "SkStream.h"
#include "SkTypes.h"
+#ifdef __linux__ // Only Linux support parcel
#include "android/binder_parcel.h"
+#endif
#include "android_nio_utils.h"
#ifdef __ANDROID__
@@ -851,6 +853,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {
#endif
}
+#ifdef __linux__ // Only Linux support parcel
// Returns whether this bitmap should be written to the parcel as mutable.
static bool shouldParcelAsMutable(SkBitmap& bitmap, AParcel* parcel) {
// If the bitmap is immutable, then parcel as immutable.
@@ -867,6 +870,7 @@ static bool shouldParcelAsMutable(SkBitmap& bitmap, AParcel* parcel) {
// writing it to the parcel.
return !shouldUseAshmem(parcel, bitmap.computeByteSize());
}
+#endif
static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, jlong bitmapHandle, jint density,
jobject parcel) {
diff --git a/libs/hwui/jni/GIFMovie.cpp b/libs/hwui/jni/GIFMovie.cpp
index 476b6fda5007..6c82aa1ca27d 100644
--- a/libs/hwui/jni/GIFMovie.cpp
+++ b/libs/hwui/jni/GIFMovie.cpp
@@ -63,7 +63,7 @@ GIFMovie::GIFMovie(SkStream* stream)
}
fCurrIndex = -1;
fLastDrawIndex = -1;
- fPaintingColor = SK_AlphaTRANSPARENT;
+ fPaintingColor = SkPackARGB32(0, 0, 0, 0);
}
GIFMovie::~GIFMovie()
@@ -127,7 +127,7 @@ static void copyLine(uint32_t* dst, const unsigned char* src, const ColorMapObje
for (; width > 0; width--, src++, dst++) {
if (*src != transparent && *src < cmap->ColorCount) {
const GifColorType& col = cmap->Colors[*src];
- *dst = SkColorSetRGB(col.Red, col.Green, col.Blue);
+ *dst = SkPackARGB32(0xFF, col.Red, col.Green, col.Blue);
}
}
}
@@ -395,10 +395,10 @@ bool GIFMovie::onGetBitmap(SkBitmap* bm)
lastIndex = fGIF->ImageCount - 1;
}
- SkColor bgColor = SK_ColorTRANSPARENT;
+ SkColor bgColor = SkPackARGB32(0, 0, 0, 0);
if (gif->SColorMap != nullptr && gif->SBackGroundColor < gif->SColorMap->ColorCount) {
const GifColorType& col = gif->SColorMap->Colors[gif->SBackGroundColor];
- bgColor = SkColorSetRGB(col.Red, col.Green, col.Blue);
+ bgColor = SkColorSetARGB(0xFF, col.Red, col.Green, col.Blue);
}
// draw each frames - not intelligent way
@@ -411,7 +411,7 @@ bool GIFMovie::onGetBitmap(SkBitmap* bm)
if (!trans && gif->SColorMap != nullptr) {
fPaintingColor = bgColor;
} else {
- fPaintingColor = SK_ColorTRANSPARENT;
+ fPaintingColor = SkColorSetARGB(0, 0, 0, 0);
}
bm->eraseColor(fPaintingColor);
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 0cd9c53c830f..e5a6260cfd98 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -44,7 +44,7 @@ namespace uirenderer {
namespace renderthread {
// Not all of these are strictly required, but are all enabled if present.
-static std::array<std::string_view, 23> sEnableExtensions{
+static std::array<std::string_view, 25> sEnableExtensions{
VK_KHR_BIND_MEMORY_2_EXTENSION_NAME,
VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME,
VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME,
@@ -68,6 +68,8 @@ static std::array<std::string_view, 23> sEnableExtensions{
VK_EXT_GLOBAL_PRIORITY_QUERY_EXTENSION_NAME,
VK_KHR_GLOBAL_PRIORITY_EXTENSION_NAME,
VK_EXT_DEVICE_FAULT_EXTENSION_NAME,
+ VK_EXT_FRAME_BOUNDARY_EXTENSION_NAME,
+ VK_ANDROID_FRAME_BOUNDARY_EXTENSION_NAME,
};
static bool shouldEnableExtension(const std::string_view& extension) {
@@ -746,7 +748,14 @@ VulkanManager::VkDrawResult VulkanManager::finishFrame(SkSurface* surface) {
ALOGE_IF(!context, "Surface is not backed by gpu");
GrSemaphoresSubmitted submitted = context->flush(
surface, SkSurfaces::BackendSurfaceAccess::kPresent, flushInfo);
- context->submit();
+
+ static uint64_t currentFrameID = 0;
+ GrSubmitInfo submitInfo;
+ if (!mFrameBoundaryANDROID) {
+ submitInfo.fMarkBoundary = GrMarkFrameBoundary::kYes;
+ submitInfo.fFrameID = currentFrameID++;
+ }
+ context->submit(submitInfo);
VkDrawResult drawResult{
.submissionTime = systemTime(),
};
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index a593ec6f8351..744211e39f79 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -30,14 +30,10 @@
// VK_ANDROID_frame_boundary is a bespoke extension defined by AGI
// (https://github.com/google/agi) to enable profiling of apps rendering via
// HWUI. This extension is not defined in Khronos, hence the need to declare it
-// manually here. There's a superseding extension (VK_EXT_frame_boundary) being
-// discussed in Khronos, but in the meantime we use the bespoke
-// VK_ANDROID_frame_boundary. This is a device extension that is implemented by
+// manually here. There's an extension (VK_EXT_frame_boundary) which we will use
+// instead if available. This is a device extension that is implemented by
// AGI's Vulkan capture layer, such that it is only supported by devices when
// AGI is doing a capture of the app.
-//
-// TODO(b/182165045): use the Khronos blessed VK_EXT_frame_boudary once it has
-// landed in the spec.
typedef void(VKAPI_PTR* PFN_vkFrameBoundaryANDROID)(VkDevice device, VkSemaphore semaphore,
VkImage image);
#define VK_ANDROID_FRAME_BOUNDARY_EXTENSION_NAME "VK_ANDROID_frame_boundary"
diff --git a/location/api/system-current.txt b/location/api/system-current.txt
index eb19ba84ee62..47984745fafc 100644
--- a/location/api/system-current.txt
+++ b/location/api/system-current.txt
@@ -96,8 +96,8 @@ package android.location {
public static final class BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime implements android.os.Parcelable {
method public int describeContents();
+ method @IntRange(from=0, to=31) public int getAode();
method @IntRange(from=0) public int getBeidouWeekNumber();
- method @IntRange(from=0, to=31) public int getIode();
method @IntRange(from=0, to=604792) public int getToeSeconds();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.location.BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime> CREATOR;
@@ -106,8 +106,8 @@ package android.location {
public static final class BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime.Builder {
ctor public BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime.Builder();
method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime build();
+ method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime.Builder setAode(int);
method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime.Builder setBeidouWeekNumber(@IntRange(from=0) int);
- method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime.Builder setIode(int);
method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime.Builder setToeSeconds(@IntRange(from=0, to=604792) int);
}
@@ -177,7 +177,7 @@ package android.location {
method public int describeContents();
method @Nullable public android.location.GnssAlmanac getAlmanac();
method @Nullable public android.location.AuxiliaryInformation getAuxiliaryInformation();
- method @Nullable public android.location.KlobucharIonosphericModel getIonosphericModel();
+ method @Nullable public android.location.GalileoIonosphericModel getIonosphericModel();
method @Nullable public android.location.LeapSecondsModel getLeapSecondsModel();
method @NonNull public java.util.List<android.location.RealTimeIntegrityModel> getRealTimeIntegrityModels();
method @NonNull public java.util.List<android.location.GnssAssistance.GnssSatelliteCorrections> getSatelliteCorrections();
@@ -193,7 +193,7 @@ package android.location {
method @NonNull public android.location.GalileoAssistance build();
method @NonNull public android.location.GalileoAssistance.Builder setAlmanac(@Nullable android.location.GnssAlmanac);
method @NonNull public android.location.GalileoAssistance.Builder setAuxiliaryInformation(@Nullable android.location.AuxiliaryInformation);
- method @NonNull public android.location.GalileoAssistance.Builder setIonosphericModel(@Nullable android.location.KlobucharIonosphericModel);
+ method @NonNull public android.location.GalileoAssistance.Builder setIonosphericModel(@Nullable android.location.GalileoIonosphericModel);
method @NonNull public android.location.GalileoAssistance.Builder setLeapSecondsModel(@Nullable android.location.LeapSecondsModel);
method @NonNull public android.location.GalileoAssistance.Builder setRealTimeIntegrityModels(@NonNull java.util.List<android.location.RealTimeIntegrityModel>);
method @NonNull public android.location.GalileoAssistance.Builder setSatelliteCorrections(@NonNull java.util.List<android.location.GnssAssistance.GnssSatelliteCorrections>);
diff --git a/location/java/android/location/BeidouSatelliteEphemeris.java b/location/java/android/location/BeidouSatelliteEphemeris.java
index 3382c20964d9..dc5e8b89d7d7 100644
--- a/location/java/android/location/BeidouSatelliteEphemeris.java
+++ b/location/java/android/location/BeidouSatelliteEphemeris.java
@@ -527,7 +527,7 @@ public final class BeidouSatelliteEphemeris implements Parcelable {
*
* <p>This is defined in BDS-SIS-ICD-B1I-3.0 section 5.2.4.11 Table 5-8.
*/
- private final int mIode;
+ private final int mAode;
/** Beidou week number without rollover */
private final int mBeidouWeekNumber;
@@ -540,18 +540,18 @@ public final class BeidouSatelliteEphemeris implements Parcelable {
private final int mToeSeconds;
private BeidouSatelliteEphemerisTime(Builder builder) {
- Preconditions.checkArgumentInRange(builder.mIode, 0, 31, "Iode");
+ Preconditions.checkArgumentInRange(builder.mAode, 0, 31, "Aode");
Preconditions.checkArgument(builder.mBeidouWeekNumber >= 0);
Preconditions.checkArgumentInRange(builder.mToeSeconds, 0, 604792, "ToeSeconds");
- mIode = builder.mIode;
+ mAode = builder.mAode;
mBeidouWeekNumber = builder.mBeidouWeekNumber;
mToeSeconds = builder.mToeSeconds;
}
/** Returns the AODE Age of Data, Ephemeris. */
@IntRange(from = 0, to = 31)
- public int getIode() {
- return mIode;
+ public int getAode() {
+ return mAode;
}
/** Returns the Beidou week number without rollover . */
@@ -573,7 +573,7 @@ public final class BeidouSatelliteEphemeris implements Parcelable {
public BeidouSatelliteEphemerisTime createFromParcel(Parcel in) {
final BeidouSatelliteEphemerisTime.Builder beidouSatelliteEphemerisTime =
new Builder()
- .setIode(in.readInt())
+ .setAode(in.readInt())
.setBeidouWeekNumber(in.readInt())
.setToeSeconds(in.readInt());
return beidouSatelliteEphemerisTime.build();
@@ -592,7 +592,7 @@ public final class BeidouSatelliteEphemeris implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel parcel, int flags) {
- parcel.writeInt(mIode);
+ parcel.writeInt(mAode);
parcel.writeInt(mBeidouWeekNumber);
parcel.writeInt(mToeSeconds);
}
@@ -600,7 +600,7 @@ public final class BeidouSatelliteEphemeris implements Parcelable {
@Override
public String toString() {
StringBuilder builder = new StringBuilder("BeidouSatelliteEphemerisTime[");
- builder.append("iode = ").append(mIode);
+ builder.append("aode = ").append(mAode);
builder.append(", beidouWeekNumber = ").append(mBeidouWeekNumber);
builder.append(", toeSeconds = ").append(mToeSeconds);
builder.append("]");
@@ -609,14 +609,14 @@ public final class BeidouSatelliteEphemeris implements Parcelable {
/** Builder for {@link BeidouSatelliteEphemerisTime} */
public static final class Builder {
- private int mIode;
+ private int mAode;
private int mBeidouWeekNumber;
private int mToeSeconds;
/** Sets the AODE Age of Data, Ephemeris. */
@NonNull
- public Builder setIode(int iode) {
- mIode = iode;
+ public Builder setAode(int iode) {
+ mAode = iode;
return this;
}
diff --git a/location/java/android/location/GalileoAssistance.java b/location/java/android/location/GalileoAssistance.java
index 8a09e6634d09..7f81ccdf346f 100644
--- a/location/java/android/location/GalileoAssistance.java
+++ b/location/java/android/location/GalileoAssistance.java
@@ -41,8 +41,8 @@ public final class GalileoAssistance implements Parcelable {
/** The Galileo almanac. */
@Nullable private final GnssAlmanac mAlmanac;
- /** The Klobuchar ionospheric model. */
- @Nullable private final KlobucharIonosphericModel mIonosphericModel;
+ /** The Galileo ionospheric model. */
+ @Nullable private final GalileoIonosphericModel mIonosphericModel;
/** The UTC model. */
@Nullable private final UtcModel mUtcModel;
@@ -102,9 +102,9 @@ public final class GalileoAssistance implements Parcelable {
return mAlmanac;
}
- /** Returns the Klobuchar ionospheric model. */
+ /** Returns the Galileo ionospheric model. */
@Nullable
- public KlobucharIonosphericModel getIonosphericModel() {
+ public GalileoIonosphericModel getIonosphericModel() {
return mIonosphericModel;
}
@@ -192,7 +192,7 @@ public final class GalileoAssistance implements Parcelable {
return new GalileoAssistance.Builder()
.setAlmanac(in.readTypedObject(GnssAlmanac.CREATOR))
.setIonosphericModel(
- in.readTypedObject(KlobucharIonosphericModel.CREATOR))
+ in.readTypedObject(GalileoIonosphericModel.CREATOR))
.setUtcModel(in.readTypedObject(UtcModel.CREATOR))
.setLeapSecondsModel(in.readTypedObject(LeapSecondsModel.CREATOR))
.setAuxiliaryInformation(
@@ -216,7 +216,7 @@ public final class GalileoAssistance implements Parcelable {
/** Builder for {@link GalileoAssistance}. */
public static final class Builder {
private GnssAlmanac mAlmanac;
- private KlobucharIonosphericModel mIonosphericModel;
+ private GalileoIonosphericModel mIonosphericModel;
private UtcModel mUtcModel;
private LeapSecondsModel mLeapSecondsModel;
private AuxiliaryInformation mAuxiliaryInformation;
@@ -232,9 +232,9 @@ public final class GalileoAssistance implements Parcelable {
return this;
}
- /** Sets the Klobuchar ionospheric model. */
+ /** Sets the Galileo ionospheric model. */
@NonNull
- public Builder setIonosphericModel(@Nullable KlobucharIonosphericModel ionosphericModel) {
+ public Builder setIonosphericModel(@Nullable GalileoIonosphericModel ionosphericModel) {
mIonosphericModel = ionosphericModel;
return this;
}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 4aba491c291e..0a1bfd55e77f 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -19,14 +19,15 @@ package android.media;
import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT;
import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO;
import static android.content.Context.DEVICE_ID_DEFAULT;
-import static android.media.audio.Flags.FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT;
-import static android.media.audio.Flags.autoPublicVolumeApiHardening;
-import static android.media.audio.Flags.cacheGetStreamMinMaxVolume;
-import static android.media.audio.Flags.cacheGetStreamVolume;
import static android.media.audio.Flags.FLAG_DEPRECATE_STREAM_BT_SCO;
import static android.media.audio.Flags.FLAG_FOCUS_EXCLUSIVE_WITH_RECORDING;
import static android.media.audio.Flags.FLAG_FOCUS_FREEZE_TEST_API;
+import static android.media.audio.Flags.FLAG_REGISTER_VOLUME_CALLBACK_API_HARDENING;
import static android.media.audio.Flags.FLAG_SUPPORTED_DEVICE_TYPES_API;
+import static android.media.audio.Flags.FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT;
+import static android.media.audio.Flags.autoPublicVolumeApiHardening;
+import static android.media.audio.Flags.cacheGetStreamMinMaxVolume;
+import static android.media.audio.Flags.cacheGetStreamVolume;
import static android.media.audiopolicy.Flags.FLAG_ENABLE_FADE_MANAGER_CONFIGURATION;
import android.Manifest;
@@ -65,7 +66,7 @@ import android.media.audiopolicy.AudioPolicy;
import android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener;
import android.media.audiopolicy.AudioProductStrategy;
import android.media.audiopolicy.AudioVolumeGroup;
-import android.media.audiopolicy.AudioVolumeGroupChangeHandler;
+import android.media.audiopolicy.IAudioVolumeChangeDispatcher;
import android.media.projection.MediaProjection;
import android.media.session.MediaController;
import android.media.session.MediaSession;
@@ -128,8 +129,6 @@ public class AudioManager {
private static final String TAG = "AudioManager";
private static final boolean DEBUG = false;
private static final AudioPortEventHandler sAudioPortEventHandler = new AudioPortEventHandler();
- private static final AudioVolumeGroupChangeHandler sAudioAudioVolumeGroupChangedHandler =
- new AudioVolumeGroupChangeHandler();
private static WeakReference<Context> sContext;
@@ -8761,9 +8760,13 @@ public class AudioManager {
}
}
+ //====================================================================
+ // Notification of volume group changes
/**
+ * Callback to receive updates on volume group changes, register using
+ * {@link AudioManager#registerVolumeGroupCallback(Executor, AudioVolumeCallback)}.
+ *
* @hide
- * Callback registered by client to be notified upon volume group change.
*/
@SystemApi
public abstract static class VolumeGroupCallback {
@@ -8774,34 +8777,69 @@ public class AudioManager {
public void onAudioVolumeGroupChanged(int group, int flags) {}
}
- /**
- * @hide
- * Register an audio volume group change listener.
- * @param callback the {@link VolumeGroupCallback} to register
- */
+ /**
+ * Register an audio volume group change listener.
+ *
+ * @param executor {@link Executor} to handle the callbacks
+ * @param callback the callback to receive the audio volume group changes
+ * @throws SecurityException if the caller doesn't have the required permission.
+ *
+ * @hide
+ */
@SystemApi
- public void registerVolumeGroupCallback(
- @NonNull Executor executor,
+ @FlaggedApi(FLAG_REGISTER_VOLUME_CALLBACK_API_HARDENING)
+ @RequiresPermission("Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED")
+ public void registerVolumeGroupCallback(@NonNull Executor executor,
@NonNull VolumeGroupCallback callback) {
- Preconditions.checkNotNull(executor, "executor must not be null");
- Preconditions.checkNotNull(callback, "volume group change cb must not be null");
- sAudioAudioVolumeGroupChangedHandler.init();
- // TODO: make use of executor
- sAudioAudioVolumeGroupChangedHandler.registerListener(callback);
+ mVolumeChangedListenerMgr.addListener(executor, callback, "registerVolumeGroupCallback",
+ () -> new AudioVolumeChangeDispatcherStub());
}
- /**
- * @hide
- * Unregister an audio volume group change listener.
- * @param callback the {@link VolumeGroupCallback} to unregister
- */
+ /**
+ * Unregister an audio volume group change listener.
+ *
+ * @param callback the {@link VolumeGroupCallback} to unregister
+ *
+ * @hide
+ */
@SystemApi
- public void unregisterVolumeGroupCallback(
- @NonNull VolumeGroupCallback callback) {
- Preconditions.checkNotNull(callback, "volume group change cb must not be null");
- sAudioAudioVolumeGroupChangedHandler.unregisterListener(callback);
+ @FlaggedApi(FLAG_REGISTER_VOLUME_CALLBACK_API_HARDENING)
+ @RequiresPermission("Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED")
+ public void unregisterVolumeGroupCallback(@NonNull VolumeGroupCallback callback) {
+ mVolumeChangedListenerMgr.removeListener(callback, "unregisterVolumeGroupCallback");
+ }
+
+ /**
+ * Manages the VolumeGroupCallback listeners and the AudioVolumeChangeDispatcherStub
+ */
+ private final CallbackUtil.LazyListenerManager<VolumeGroupCallback> mVolumeChangedListenerMgr =
+ new CallbackUtil.LazyListenerManager();
+
+ final class AudioVolumeChangeDispatcherStub extends IAudioVolumeChangeDispatcher.Stub
+ implements CallbackUtil.DispatcherStub {
+
+ @Override
+ public void register(boolean register) {
+ try {
+ if (register) {
+ getService().registerAudioVolumeCallback(this);
+ } else {
+ getService().unregisterAudioVolumeCallback(this);
+ }
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
+ public void onAudioVolumeGroupChanged(int group, int flags) {
+ mVolumeChangedListenerMgr.callListeners((listener) ->
+ listener.onAudioVolumeGroupChanged(group, flags));
+ }
}
+ //====================================================================
+
/**
* Return if an asset contains haptic channels or not.
*
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index ad6f2e52fd97..4906cd3fb1e5 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -2732,4 +2732,25 @@ public class AudioSystem
* @hide
*/
public static native void triggerSystemPropertyUpdate(long handle);
+
+ /**
+ * Registers the given {@link INativeAudioVolumeGroupCallback} to native audioserver.
+ * @param callback to register
+ * @return {@link #SUCCESS} if successfully registered.
+ *
+ * @hide
+ */
+ public static native int registerAudioVolumeGroupCallback(
+ INativeAudioVolumeGroupCallback callback);
+
+ /**
+ * Unegisters the given {@link INativeAudioVolumeGroupCallback} from native audioserver
+ * previously registered via {@link #registerAudioVolumeGroupCallback}.
+ * @param callback to register
+ * @return {@link #SUCCESS} if successfully registered.
+ *
+ * @hide
+ */
+ public static native int unregisterAudioVolumeGroupCallback(
+ INativeAudioVolumeGroupCallback callback);
}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 8aadb418cf5a..c505bcee0332 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -65,6 +65,7 @@ import android.media.audiopolicy.AudioPolicyConfig;
import android.media.audiopolicy.AudioProductStrategy;
import android.media.audiopolicy.AudioVolumeGroup;
import android.media.audiopolicy.IAudioPolicyCallback;
+import android.media.audiopolicy.IAudioVolumeChangeDispatcher;
import android.media.projection.IMediaProjection;
import android.net.Uri;
import android.os.PersistableBundle;
@@ -446,6 +447,12 @@ interface IAudioService {
boolean isAudioServerRunning();
+ @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED")
+ void registerAudioVolumeCallback(IAudioVolumeChangeDispatcher avc);
+
+ @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED")
+ oneway void unregisterAudioVolumeCallback(IAudioVolumeChangeDispatcher avc);
+
int setUidDeviceAffinity(in IAudioPolicyCallback pcb, in int uid, in int[] deviceTypes,
in String[] deviceAddresses);
diff --git a/media/java/android/media/audiopolicy/AudioVolumeGroupChangeHandler.java b/media/java/android/media/audiopolicy/AudioVolumeGroupChangeHandler.java
deleted file mode 100644
index 022cfeeb4e43..000000000000
--- a/media/java/android/media/audiopolicy/AudioVolumeGroupChangeHandler.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.audiopolicy;
-
-import android.annotation.NonNull;
-import android.media.AudioManager;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Message;
-
-import com.android.internal.util.Preconditions;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-
-/**
- * The AudioVolumeGroupChangeHandler handles AudioManager.OnAudioVolumeGroupChangedListener
- * callbacks posted from JNI
- *
- * TODO: Make use of Executor of callbacks.
- * @hide
- */
-public class AudioVolumeGroupChangeHandler {
- private Handler mHandler;
- private HandlerThread mHandlerThread;
- private final ArrayList<AudioManager.VolumeGroupCallback> mListeners =
- new ArrayList<AudioManager.VolumeGroupCallback>();
-
- private static final String TAG = "AudioVolumeGroupChangeHandler";
-
- private static final int AUDIOVOLUMEGROUP_EVENT_VOLUME_CHANGED = 1000;
- private static final int AUDIOVOLUMEGROUP_EVENT_NEW_LISTENER = 4;
-
- /**
- * Accessed by native methods: JNI Callback context.
- */
- @SuppressWarnings("unused")
- private long mJniCallback;
-
- /**
- * Initialization
- */
- public void init() {
- synchronized (this) {
- if (mHandler != null) {
- return;
- }
- // create a new thread for our new event handler
- mHandlerThread = new HandlerThread(TAG);
- mHandlerThread.start();
-
- if (mHandlerThread.getLooper() == null) {
- mHandler = null;
- return;
- }
- mHandler = new Handler(mHandlerThread.getLooper()) {
- @Override
- public void handleMessage(Message msg) {
- ArrayList<AudioManager.VolumeGroupCallback> listeners;
- synchronized (this) {
- if (msg.what == AUDIOVOLUMEGROUP_EVENT_NEW_LISTENER) {
- listeners =
- new ArrayList<AudioManager.VolumeGroupCallback>();
- if (mListeners.contains(msg.obj)) {
- listeners.add(
- (AudioManager.VolumeGroupCallback) msg.obj);
- }
- } else {
- listeners = (ArrayList<AudioManager.VolumeGroupCallback>)
- mListeners.clone();
- }
- }
- if (listeners.isEmpty()) {
- return;
- }
-
- switch (msg.what) {
- case AUDIOVOLUMEGROUP_EVENT_VOLUME_CHANGED:
- for (int i = 0; i < listeners.size(); i++) {
- listeners.get(i).onAudioVolumeGroupChanged((int) msg.arg1,
- (int) msg.arg2);
- }
- break;
-
- default:
- break;
- }
- }
- };
- native_setup(new WeakReference<AudioVolumeGroupChangeHandler>(this));
- }
- }
-
- private native void native_setup(Object moduleThis);
-
- @Override
- protected void finalize() {
- native_finalize();
- if (mHandlerThread.isAlive()) {
- mHandlerThread.quit();
- }
- }
- private native void native_finalize();
-
- /**
- * @param cb the {@link AudioManager.VolumeGroupCallback} to register
- */
- public void registerListener(@NonNull AudioManager.VolumeGroupCallback cb) {
- Preconditions.checkNotNull(cb, "volume group callback shall not be null");
- synchronized (this) {
- mListeners.add(cb);
- }
- if (mHandler != null) {
- Message m = mHandler.obtainMessage(
- AUDIOVOLUMEGROUP_EVENT_NEW_LISTENER, 0, 0, cb);
- mHandler.sendMessage(m);
- }
- }
-
- /**
- * @param cb the {@link AudioManager.VolumeGroupCallback} to unregister
- */
- public void unregisterListener(@NonNull AudioManager.VolumeGroupCallback cb) {
- Preconditions.checkNotNull(cb, "volume group callback shall not be null");
- synchronized (this) {
- mListeners.remove(cb);
- }
- }
-
- Handler handler() {
- return mHandler;
- }
-
- @SuppressWarnings("unused")
- private static void postEventFromNative(Object moduleRef,
- int what, int arg1, int arg2, Object obj) {
- AudioVolumeGroupChangeHandler eventHandler =
- (AudioVolumeGroupChangeHandler) ((WeakReference) moduleRef).get();
- if (eventHandler == null) {
- return;
- }
-
- if (eventHandler != null) {
- Handler handler = eventHandler.handler();
- if (handler != null) {
- Message m = handler.obtainMessage(what, arg1, arg2, obj);
- // Do not remove previous messages, as we would lose notification of group changes
- handler.sendMessage(m);
- }
- }
- }
-}
diff --git a/media/java/android/media/audiopolicy/IAudioVolumeChangeDispatcher.aidl b/media/java/android/media/audiopolicy/IAudioVolumeChangeDispatcher.aidl
new file mode 100644
index 000000000000..e6f9024cfd1e
--- /dev/null
+++ b/media/java/android/media/audiopolicy/IAudioVolumeChangeDispatcher.aidl
@@ -0,0 +1,31 @@
+/* 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.media.audiopolicy;
+
+/**
+ * AIDL for the AudioService to signal audio volume groups changes
+ *
+ * {@hide}
+ */
+oneway interface IAudioVolumeChangeDispatcher {
+
+ /**
+ * Called when a volume group has been changed
+ * @param group id of the volume group that has changed.
+ * @param flags one or more flags to describe the volume change.
+ */
+ void onAudioVolumeGroupChanged(int group, int flags);
+}
diff --git a/media/java/android/media/projection/TEST_MAPPING b/media/java/android/media/projection/TEST_MAPPING
index b33097c50002..ea62287b7411 100644
--- a/media/java/android/media/projection/TEST_MAPPING
+++ b/media/java/android/media/projection/TEST_MAPPING
@@ -1,7 +1,7 @@
{
- "presubmit": [
+ "imports": [
{
- "name": "MediaProjectionTests"
+ "path": "frameworks/base/services/core/java/com/android/server/media/projection"
}
]
-}
+} \ No newline at end of file
diff --git a/media/java/android/media/quality/MediaQualityContract.java b/media/java/android/media/quality/MediaQualityContract.java
index fccdba8e727f..ece87a66556f 100644
--- a/media/java/android/media/quality/MediaQualityContract.java
+++ b/media/java/android/media/quality/MediaQualityContract.java
@@ -72,6 +72,43 @@ public class MediaQualityContract {
*/
public static final String LEVEL_OFF = "level_off";
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef(prefix = "COLOR_TEMP", value = {
+ COLOR_TEMP_USER,
+ COLOR_TEMP_COOL,
+ COLOR_TEMP_STANDARD,
+ COLOR_TEMP_WARM,
+ COLOR_TEMP_USER_HDR10PLUS,
+ COLOR_TEMP_COOL_HDR10PLUS,
+ COLOR_TEMP_STANDARD_HDR10PLUS,
+ COLOR_TEMP_WARM_HDR10PLUS,
+ COLOR_TEMP_FMMSDR,
+ COLOR_TEMP_FMMHDR,
+ })
+ public @interface ColorTempValue {}
+
+ /** @hide */
+ public static final String COLOR_TEMP_USER = "color_temp_user";
+ /** @hide */
+ public static final String COLOR_TEMP_COOL = "color_temp_cool";
+ /** @hide */
+ public static final String COLOR_TEMP_STANDARD = "color_temp_standard";
+ /** @hide */
+ public static final String COLOR_TEMP_WARM = "color_temp_warm";
+ /** @hide */
+ public static final String COLOR_TEMP_USER_HDR10PLUS = "color_temp_user_hdr10plus";
+ /** @hide */
+ public static final String COLOR_TEMP_COOL_HDR10PLUS = "color_temp_cool_hdr10plus";
+ /** @hide */
+ public static final String COLOR_TEMP_STANDARD_HDR10PLUS = "color_temp_standard_hdr10plus";
+ /** @hide */
+ public static final String COLOR_TEMP_WARM_HDR10PLUS = "color_temp_warm_hdr10plus";
+ /** @hide */
+ public static final String COLOR_TEMP_FMMSDR = "color_temp_fmmsdr";
+ /** @hide */
+ public static final String COLOR_TEMP_FMMHDR = "color_temp_fmmhdr";
+
/**
* @hide
@@ -82,7 +119,6 @@ public class MediaQualityContract {
String PARAMETER_NAME = "_name";
String PARAMETER_PACKAGE = "_package";
String PARAMETER_INPUT_ID = "_input_id";
- String VENDOR_PARAMETERS = "_vendor_parameters";
}
/**
diff --git a/media/tests/AudioPolicyTest/AndroidManifest.xml b/media/tests/AudioPolicyTest/AndroidManifest.xml
index 5c911b135a5d..466da7e66fbf 100644
--- a/media/tests/AudioPolicyTest/AndroidManifest.xml
+++ b/media/tests/AudioPolicyTest/AndroidManifest.xml
@@ -19,6 +19,7 @@
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
+ <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" />
<uses-permission android:name="android.permission.CHANGE_ACCESSIBILITY_VOLUME" />
diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupChangeHandlerTest.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupChangeHandlerTest.java
deleted file mode 100644
index 82394a2eb420..000000000000
--- a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupChangeHandlerTest.java
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.audiopolicytest;
-
-import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
-
-import static com.android.audiopolicytest.AudioVolumeTestUtil.DEFAULT_ATTRIBUTES;
-import static com.android.audiopolicytest.AudioVolumeTestUtil.incrementVolumeIndex;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThrows;
-import static org.junit.Assert.assertTrue;
-
-import android.media.AudioAttributes;
-import android.media.AudioManager;
-import android.media.audiopolicy.AudioVolumeGroup;
-import android.media.audiopolicy.AudioVolumeGroupChangeHandler;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-public class AudioVolumeGroupChangeHandlerTest {
- private static final String TAG = "AudioVolumeGroupChangeHandlerTest";
-
- @Rule
- public final AudioVolumesTestRule rule = new AudioVolumesTestRule();
-
- private AudioManager mAudioManager;
-
- @Before
- public void setUp() {
- mAudioManager = getApplicationContext().getSystemService(AudioManager.class);
- }
-
- @Test
- public void testRegisterInvalidCallback() {
- final AudioVolumeGroupChangeHandler audioAudioVolumeGroupChangedHandler =
- new AudioVolumeGroupChangeHandler();
-
- audioAudioVolumeGroupChangedHandler.init();
-
- assertThrows(NullPointerException.class, () -> {
- AudioManager.VolumeGroupCallback nullCb = null;
- audioAudioVolumeGroupChangedHandler.registerListener(nullCb);
- });
- }
-
- @Test
- public void testUnregisterInvalidCallback() {
- final AudioVolumeGroupChangeHandler audioAudioVolumeGroupChangedHandler =
- new AudioVolumeGroupChangeHandler();
-
- audioAudioVolumeGroupChangedHandler.init();
-
- final AudioVolumeGroupCallbackHelper cb = new AudioVolumeGroupCallbackHelper();
- audioAudioVolumeGroupChangedHandler.registerListener(cb);
-
- assertThrows(NullPointerException.class, () -> {
- AudioManager.VolumeGroupCallback nullCb = null;
- audioAudioVolumeGroupChangedHandler.unregisterListener(nullCb);
- });
- audioAudioVolumeGroupChangedHandler.unregisterListener(cb);
- }
-
- @Test
- public void testRegisterUnregisterCallback() {
- final AudioVolumeGroupChangeHandler audioAudioVolumeGroupChangedHandler =
- new AudioVolumeGroupChangeHandler();
-
- audioAudioVolumeGroupChangedHandler.init();
- final AudioVolumeGroupCallbackHelper validCb = new AudioVolumeGroupCallbackHelper();
-
- // Should not assert, otherwise test will fail
- audioAudioVolumeGroupChangedHandler.registerListener(validCb);
-
- // Should not assert, otherwise test will fail
- audioAudioVolumeGroupChangedHandler.unregisterListener(validCb);
- }
-
- @Test
- public void testCallbackReceived() {
- final AudioVolumeGroupChangeHandler audioAudioVolumeGroupChangedHandler =
- new AudioVolumeGroupChangeHandler();
-
- audioAudioVolumeGroupChangedHandler.init();
-
- final AudioVolumeGroupCallbackHelper validCb = new AudioVolumeGroupCallbackHelper();
- audioAudioVolumeGroupChangedHandler.registerListener(validCb);
-
- List<AudioVolumeGroup> audioVolumeGroups = mAudioManager.getAudioVolumeGroups();
- assertTrue(audioVolumeGroups.size() > 0);
-
- try {
- for (final AudioVolumeGroup audioVolumeGroup : audioVolumeGroups) {
- int volumeGroupId = audioVolumeGroup.getId();
-
- List<AudioAttributes> avgAttributes = audioVolumeGroup.getAudioAttributes();
- // Set the volume per attributes (if valid) and wait the callback
- if (avgAttributes.size() == 0 || avgAttributes.get(0).equals(DEFAULT_ATTRIBUTES)) {
- // Some volume groups may not have valid attributes, used for internal
- // volume management like patch/rerouting
- // so bailing out strategy retrieval from attributes
- continue;
- }
- final AudioAttributes aa = avgAttributes.get(0);
-
- int index = mAudioManager.getVolumeIndexForAttributes(aa);
- int indexMax = mAudioManager.getMaxVolumeIndexForAttributes(aa);
- int indexMin = mAudioManager.getMinVolumeIndexForAttributes(aa);
-
- final int indexForAa = incrementVolumeIndex(index, indexMin, indexMax);
-
- // Set the receiver to filter only the current group callback
- validCb.setExpectedVolumeGroup(volumeGroupId);
- mAudioManager.setVolumeIndexForAttributes(aa, indexForAa, 0/*flags*/);
- assertTrue(validCb.waitForExpectedVolumeGroupChanged(
- AudioVolumeGroupCallbackHelper.ASYNC_TIMEOUT_MS));
-
- final int readIndex = mAudioManager.getVolumeIndexForAttributes(aa);
- assertEquals(readIndex, indexForAa);
- }
- } finally {
- audioAudioVolumeGroupChangedHandler.unregisterListener(validCb);
- }
- }
-
- @Test
- public void testMultipleCallbackReceived() {
-
- final AudioVolumeGroupChangeHandler audioAudioVolumeGroupChangedHandler =
- new AudioVolumeGroupChangeHandler();
-
- audioAudioVolumeGroupChangedHandler.init();
-
- final int callbackCount = 10;
- final List<AudioVolumeGroupCallbackHelper> validCbs =
- new ArrayList<AudioVolumeGroupCallbackHelper>();
- for (int i = 0; i < callbackCount; i++) {
- validCbs.add(new AudioVolumeGroupCallbackHelper());
- }
- for (final AudioVolumeGroupCallbackHelper cb : validCbs) {
- audioAudioVolumeGroupChangedHandler.registerListener(cb);
- }
-
- List<AudioVolumeGroup> audioVolumeGroups = mAudioManager.getAudioVolumeGroups();
- assertTrue(audioVolumeGroups.size() > 0);
-
- try {
- for (final AudioVolumeGroup audioVolumeGroup : audioVolumeGroups) {
- int volumeGroupId = audioVolumeGroup.getId();
-
- List<AudioAttributes> avgAttributes = audioVolumeGroup.getAudioAttributes();
- // Set the volume per attributes (if valid) and wait the callback
- if (avgAttributes.size() == 0 || avgAttributes.get(0).equals(DEFAULT_ATTRIBUTES)) {
- // Some volume groups may not have valid attributes, used for internal
- // volume management like patch/rerouting
- // so bailing out strategy retrieval from attributes
- continue;
- }
- AudioAttributes aa = avgAttributes.get(0);
-
- int index = mAudioManager.getVolumeIndexForAttributes(aa);
- int indexMax = mAudioManager.getMaxVolumeIndexForAttributes(aa);
- int indexMin = mAudioManager.getMinVolumeIndexForAttributes(aa);
-
- final int indexForAa = incrementVolumeIndex(index, indexMin, indexMax);
-
- // Set the receiver to filter only the current group callback
- for (final AudioVolumeGroupCallbackHelper cb : validCbs) {
- cb.setExpectedVolumeGroup(volumeGroupId);
- }
- mAudioManager.setVolumeIndexForAttributes(aa, indexForAa, 0/*flags*/);
-
- for (final AudioVolumeGroupCallbackHelper cb : validCbs) {
- assertTrue(cb.waitForExpectedVolumeGroupChanged(
- AudioVolumeGroupCallbackHelper.ASYNC_TIMEOUT_MS));
- }
- int readIndex = mAudioManager.getVolumeIndexForAttributes(aa);
- assertEquals(readIndex, indexForAa);
- }
- } finally {
- for (final AudioVolumeGroupCallbackHelper cb : validCbs) {
- audioAudioVolumeGroupChangedHandler.unregisterListener(cb);
- }
- }
- }
-}
diff --git a/media/tests/projection/TEST_MAPPING b/media/tests/projection/TEST_MAPPING
new file mode 100644
index 000000000000..b33097c50002
--- /dev/null
+++ b/media/tests/projection/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "MediaProjectionTests"
+ }
+ ]
+}
diff --git a/packages/SettingsLib/AdaptiveIcon/Android.bp b/packages/SettingsLib/AdaptiveIcon/Android.bp
index 67b6fb5f2ed9..c9409e44c8ee 100644
--- a/packages/SettingsLib/AdaptiveIcon/Android.bp
+++ b/packages/SettingsLib/AdaptiveIcon/Android.bp
@@ -18,8 +18,9 @@ android_library {
resource_dirs: ["res"],
static_libs: [
- "androidx.annotation_annotation",
"SettingsLibTile",
+ "androidx.annotation_annotation",
+ "androidx.core_core",
],
min_sdk_version: "21",
diff --git a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIconShapeDrawable.java b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIconShapeDrawable.java
index 4d7610cf97b6..aa1d35afab4a 100644
--- a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIconShapeDrawable.java
+++ b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIconShapeDrawable.java
@@ -23,7 +23,8 @@ import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.PathShape;
import android.util.AttributeSet;
-import android.util.PathParser;
+
+import androidx.core.graphics.PathParser;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
diff --git a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java
index 12c234ecd739..c7ca18a9c339 100644
--- a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java
+++ b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java
@@ -27,12 +27,12 @@ import android.graphics.Rect;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.DrawableWrapper;
import android.os.RemoteException;
-import android.util.PathParser;
import android.view.IWindowManager;
import android.view.WindowManagerGlobal;
import androidx.annotation.IntDef;
import androidx.annotation.VisibleForTesting;
+import androidx.core.graphics.PathParser;
import com.android.settingslib.widget.adaptiveicon.R;
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/AndroidSecureSettings.java b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/AndroidSecureSettings.java
index 8aee576c3d04..a06d8829e331 100644
--- a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/AndroidSecureSettings.java
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/AndroidSecureSettings.java
@@ -24,11 +24,11 @@ import android.provider.Settings;
* Implementation of {@link SecureSettings} that uses Android's {@link Settings.Secure}
* implementation.
*/
-class AndroidSecureSettings implements SecureSettings {
+public class AndroidSecureSettings implements SecureSettings {
private final ContentResolver mContentResolver;
- AndroidSecureSettings(ContentResolver contentResolver) {
+ public AndroidSecureSettings(ContentResolver contentResolver) {
mContentResolver = contentResolver;
}
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManager.kt b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManager.kt
index fdde3d3f5669..1da17756fae6 100644
--- a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManager.kt
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManager.kt
@@ -17,6 +17,7 @@
package com.android.settingslib.devicestate
import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK
+import android.util.Dumpable
/**
* Interface for managing [DEVICE_STATE_ROTATION_LOCK] setting.
@@ -25,7 +26,7 @@ import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK
* specific device states, retrieve the setting value, and check if rotation is locked for specific
* or all device states.
*/
-interface DeviceStateAutoRotateSettingManager {
+interface DeviceStateAutoRotateSettingManager : Dumpable {
// TODO: b/397928958 - Rename all terms from rotationLock to autoRotate in all apis.
/** Listener for changes in device-state based auto rotate setting. */
@@ -65,5 +66,3 @@ data class SettableDeviceState(
/** Returns whether there is an auto-rotation setting for this device state. */
val isSettable: Boolean
)
-
-
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerImpl.kt b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerImpl.kt
index 0b6c6e238956..a9f9eda07118 100644
--- a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerImpl.kt
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerImpl.kt
@@ -22,11 +22,13 @@ import android.os.UserHandle
import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK
import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED
import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED
+import android.util.IndentingPrintWriter
import android.util.Log
import android.util.SparseIntArray
import com.android.internal.R
import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager.DeviceStateAutoRotateSettingListener
import com.android.window.flags.Flags
+import java.io.PrintWriter
import java.util.concurrent.Executor
/**
@@ -104,6 +106,15 @@ class DeviceStateAutoRotateSettingManagerImpl(
throw UnsupportedOperationException("API updateSetting is not implemented yet")
}
+ override fun dump(writer: PrintWriter, args: Array<out String>?) {
+ val indentingWriter = IndentingPrintWriter(writer)
+ indentingWriter.println("DeviceStateAutoRotateSettingManagerImpl")
+ indentingWriter.increaseIndent()
+ indentingWriter.println("fallbackPostureMap: $fallbackPostureMap")
+ indentingWriter.println("settableDeviceState: $settableDeviceState")
+ indentingWriter.decreaseIndent()
+ }
+
private fun notifyListeners() =
settingListeners.forEach { listener -> listener.onSettingsChanged() }
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerProvider.kt b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerProvider.kt
new file mode 100644
index 000000000000..2db8e6f97498
--- /dev/null
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerProvider.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.settingslib.devicestate
+
+import android.content.Context
+import android.os.Handler
+import com.android.window.flags.Flags
+import java.util.concurrent.Executor
+
+/**
+ * Provides appropriate instance of [DeviceStateAutoRotateSettingManager], based on the value of
+ * [Flags.FLAG_ENABLE_DEVICE_STATE_AUTO_ROTATE_SETTING_REFACTOR].
+ */
+object DeviceStateAutoRotateSettingManagerProvider {
+ /**
+ * Provides an instance of [DeviceStateAutoRotateSettingManager], based on the value of
+ * [Flags.FLAG_ENABLE_DEVICE_STATE_AUTO_ROTATE_SETTING_REFACTOR]. It is supposed to be used
+ * by apps that supports dagger.
+ */
+ @JvmStatic
+ fun createInstance(
+ context: Context,
+ backgroundExecutor: Executor,
+ secureSettings: SecureSettings,
+ mainHandler: Handler,
+ posturesHelper: PosturesHelper,
+ ): DeviceStateAutoRotateSettingManager =
+ if (Flags.enableDeviceStateAutoRotateSettingRefactor()) {
+ DeviceStateAutoRotateSettingManagerImpl(
+ context,
+ backgroundExecutor,
+ secureSettings,
+ mainHandler,
+ posturesHelper,
+ )
+ } else {
+ DeviceStateRotationLockSettingsManager(context, secureSettings)
+ }
+}
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java
index deeba574f2ad..6d180b63cd08 100644
--- a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java
@@ -20,10 +20,8 @@ import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORE
import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED;
import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED;
-import static com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager.DeviceStateAutoRotateSettingListener;
-
+import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -41,6 +39,7 @@ import android.util.SparseIntArray;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
@@ -51,7 +50,8 @@ import java.util.Set;
* Manages device-state based rotation lock settings. Handles reading, writing, and listening for
* changes.
*/
-public final class DeviceStateRotationLockSettingsManager {
+public final class DeviceStateRotationLockSettingsManager implements
+ DeviceStateAutoRotateSettingManager {
private static final String TAG = "DSRotLockSettingsMngr";
private static final String SEPARATOR_REGEX = ":";
@@ -68,8 +68,7 @@ public final class DeviceStateRotationLockSettingsManager {
private SparseIntArray mPostureRotationLockFallbackSettings;
private List<SettableDeviceState> mSettableDeviceStates;
- @VisibleForTesting
- DeviceStateRotationLockSettingsManager(Context context, SecureSettings secureSettings) {
+ public DeviceStateRotationLockSettingsManager(Context context, SecureSettings secureSettings) {
mSecureSettings = secureSettings;
mPosturesHelper = new PosturesHelper(context, getDeviceStateManager(context));
@@ -89,30 +88,6 @@ public final class DeviceStateRotationLockSettingsManager {
return null;
}
- /** Returns a singleton instance of this class */
- public static synchronized DeviceStateRotationLockSettingsManager getInstance(Context context) {
- if (sSingleton == null) {
- Context applicationContext = context.getApplicationContext();
- ContentResolver contentResolver = applicationContext.getContentResolver();
- SecureSettings secureSettings = new AndroidSecureSettings(contentResolver);
- sSingleton =
- new DeviceStateRotationLockSettingsManager(applicationContext, secureSettings);
- }
- return sSingleton;
- }
-
- /** Resets the singleton instance of this class. Only used for testing. */
- @VisibleForTesting
- public static synchronized void resetInstance() {
- sSingleton = null;
- }
-
- /** Returns true if device-state based rotation lock settings are enabled. */
- public static boolean isDeviceStateRotationLockEnabled(Context context) {
- return context.getResources()
- .getStringArray(R.array.config_perDeviceStateRotationLockDefaults).length > 0;
- }
-
private void listenForSettingsChange() {
mSecureSettings
.registerContentObserver(
@@ -131,7 +106,8 @@ public final class DeviceStateRotationLockSettingsManager {
* Registers a {@link DeviceStateAutoRotateSettingListener} to be notified when the settings
* change. Can be called multiple times with different listeners.
*/
- public void registerListener(DeviceStateAutoRotateSettingListener runnable) {
+ @Override
+ public void registerListener(@NonNull DeviceStateAutoRotateSettingListener runnable) {
mListeners.add(runnable);
}
@@ -139,14 +115,16 @@ public final class DeviceStateRotationLockSettingsManager {
* Unregisters a {@link DeviceStateAutoRotateSettingListener}. No-op if the given instance
* was never registered.
*/
+ @Override
public void unregisterListener(
- DeviceStateAutoRotateSettingListener deviceStateAutoRotateSettingListener) {
+ @NonNull DeviceStateAutoRotateSettingListener deviceStateAutoRotateSettingListener) {
if (!mListeners.remove(deviceStateAutoRotateSettingListener)) {
Log.w(TAG, "Attempting to unregister a listener hadn't been registered");
}
}
/** Updates the rotation lock setting for a specified device state. */
+ @Override
public void updateSetting(int deviceState, boolean rotationLocked) {
int posture = mPosturesHelper.deviceStateToPosture(deviceState);
if (mPostureRotationLockFallbackSettings.indexOfKey(posture) >= 0) {
@@ -173,6 +151,7 @@ public final class DeviceStateRotationLockSettingsManager {
* DEVICE_STATE_ROTATION_LOCK_IGNORED}.
*/
@Settings.Secure.DeviceStateRotationLockSetting
+ @Override
public int getRotationLockSetting(int deviceState) {
int devicePosture = mPosturesHelper.deviceStateToPosture(deviceState);
int rotationLockSetting = mPostureRotationLockSettings.get(
@@ -196,6 +175,7 @@ public final class DeviceStateRotationLockSettingsManager {
/** Returns true if the rotation is locked for the current device state */
+ @Override
public boolean isRotationLocked(int deviceState) {
return getRotationLockSetting(deviceState) == DEVICE_STATE_ROTATION_LOCK_LOCKED;
}
@@ -204,6 +184,7 @@ public final class DeviceStateRotationLockSettingsManager {
* Returns true if there is no device state for which the current setting is {@link
* DEVICE_STATE_ROTATION_LOCK_UNLOCKED}.
*/
+ @Override
public boolean isRotationLockedForAllStates() {
for (int i = 0; i < mPostureRotationLockSettings.size(); i++) {
if (mPostureRotationLockSettings.valueAt(i)
@@ -215,6 +196,8 @@ public final class DeviceStateRotationLockSettingsManager {
}
/** Returns a list of device states and their respective auto-rotation setting availability. */
+ @Override
+ @NonNull
public List<SettableDeviceState> getSettableDeviceStates() {
// Returning a copy to make sure that nothing outside can mutate our internal list.
return new ArrayList<>(mSettableDeviceStates);
@@ -356,17 +339,21 @@ public final class DeviceStateRotationLockSettingsManager {
}
}
- /** Dumps internal state. */
- public void dump(IndentingPrintWriter pw) {
- pw.println("DeviceStateRotationLockSettingsManager");
- pw.increaseIndent();
- pw.println("mPostureRotationLockDefaults: "
+ @Override
+ public void dump(@NonNull PrintWriter writer, String[] args) {
+ IndentingPrintWriter indentingWriter = new IndentingPrintWriter(writer);
+ indentingWriter.println("DeviceStateRotationLockSettingsManager");
+ indentingWriter.increaseIndent();
+ indentingWriter.println("mPostureRotationLockDefaults: "
+ Arrays.toString(mPostureRotationLockDefaults));
- pw.println("mPostureDefaultRotationLockSettings: " + mPostureDefaultRotationLockSettings);
- pw.println("mDeviceStateRotationLockSettings: " + mPostureRotationLockSettings);
- pw.println("mPostureRotationLockFallbackSettings: " + mPostureRotationLockFallbackSettings);
- pw.println("mSettableDeviceStates: " + mSettableDeviceStates);
- pw.decreaseIndent();
+ indentingWriter.println(
+ "mPostureDefaultRotationLockSettings: " + mPostureDefaultRotationLockSettings);
+ indentingWriter.println(
+ "mDeviceStateRotationLockSettings: " + mPostureRotationLockSettings);
+ indentingWriter.println(
+ "mPostureRotationLockFallbackSettings: " + mPostureRotationLockFallbackSettings);
+ indentingWriter.println("mSettableDeviceStates: " + mSettableDeviceStates);
+ indentingWriter.decreaseIndent();
}
/**
diff --git a/packages/SettingsLib/SettingsTheme/res/layout/settingslib_expressive_collapsable_textview.xml b/packages/SettingsLib/SettingsTheme/res/layout/settingslib_expressive_collapsable_textview.xml
index 7d7bec14ed78..cc55cacb9e5c 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout/settingslib_expressive_collapsable_textview.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout/settingslib_expressive_collapsable_textview.xml
@@ -44,10 +44,11 @@
<com.android.settingslib.widget.LinkableTextView
android:id="@+id/settingslib_expressive_learn_more"
- android:layout_width="wrap_content"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@android:id/title"
app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
android:paddingTop="@dimen/settingslib_expressive_space_extrasmall6"
android:textAlignment="viewStart"
android:clickable="true"
diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/CollapsableTextView.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/CollapsableTextView.kt
index 976711bdc5f3..007dc5143262 100644
--- a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/CollapsableTextView.kt
+++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/CollapsableTextView.kt
@@ -85,6 +85,7 @@ class CollapsableTextView @JvmOverloads constructor(
Gravity.CENTER_VERTICAL, Gravity.CENTER, Gravity.CENTER_HORIZONTAL -> {
centerHorizontally(titleTextView)
centerHorizontally(collapseButton)
+ centerHorizontally(learnMoreTextView)
}
}
isCollapsable = getBoolean(isCollapsableAttr, DEFAULT_COLLAPSABLE)
diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts
index 50408190b3ef..25406d794af9 100644
--- a/packages/SettingsLib/Spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/build.gradle.kts
@@ -28,7 +28,7 @@ val androidTop: String = File(rootDir, "../../../../..").canonicalPath
allprojects {
extra["androidTop"] = androidTop
- extra["jetpackComposeVersion"] = "1.8.0-beta02"
+ extra["jetpackComposeVersion"] = "1.8.0-rc01"
}
subprojects {
@@ -36,11 +36,11 @@ subprojects {
plugins.withType<AndroidBasePlugin> {
configure<BaseExtension> {
- compileSdkVersion(35)
+ compileSdkVersion(36)
defaultConfig {
minSdk = 21
- targetSdk = 35
+ targetSdk = 36
}
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
index 8636524ed23c..b9a3a7f4b48f 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
@@ -124,4 +124,6 @@ class GallerySpaEnvironment(context: Context) : SpaEnvironment(context) {
// For debugging
override val searchProviderAuthorities = "com.android.spa.gallery.search.provider"
+
+ override val isSpaExpressiveEnabled = true
}
diff --git a/packages/SettingsLib/Spa/gradle/libs.versions.toml b/packages/SettingsLib/Spa/gradle/libs.versions.toml
index b074f4b91ed0..d041eb011986 100644
--- a/packages/SettingsLib/Spa/gradle/libs.versions.toml
+++ b/packages/SettingsLib/Spa/gradle/libs.versions.toml
@@ -15,10 +15,10 @@
#
[versions]
-agp = "8.8.1"
+agp = "8.9.0"
dexmaker-mockito = "2.28.3"
jvm = "21"
-kotlin = "2.0.21"
+kotlin = "2.1.10"
truth = "1.4.4"
[libraries]
diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts
index 7ce5b71f678e..57f5520fd659 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/spa/build.gradle.kts
@@ -52,18 +52,18 @@ android {
dependencies {
api(project(":SettingsLibColor"))
api("androidx.appcompat:appcompat:1.7.0")
- api("androidx.compose.material3:material3:1.4.0-alpha08")
- api("androidx.compose.material:material-icons-extended")
+ api("androidx.compose.material3:material3:1.4.0-alpha10")
+ api("androidx.compose.material:material-icons-extended:1.7.8")
api("androidx.compose.runtime:runtime-livedata:$jetpackComposeVersion")
api("androidx.compose.ui:ui-tooling-preview:$jetpackComposeVersion")
api("androidx.graphics:graphics-shapes-android:1.0.1")
api("androidx.lifecycle:lifecycle-livedata-ktx")
api("androidx.lifecycle:lifecycle-runtime-compose")
- api("androidx.navigation:navigation-compose:2.9.0-alpha06")
+ api("androidx.navigation:navigation-compose:2.9.0-alpha08")
api("com.github.PhilJay:MPAndroidChart:v3.1.0-alpha")
api("com.google.android.material:material:1.13.0-alpha08")
debugApi("androidx.compose.ui:ui-tooling:$jetpackComposeVersion")
- implementation("com.airbnb.android:lottie-compose:6.4.0")
+ implementation("com.airbnb.android:lottie-compose:6.5.2")
androidTestImplementation(project(":testutils"))
androidTestImplementation(libs.dexmaker.mockito)
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/AmbientVolumeUiController.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/AmbientVolumeUiController.java
index e7ddc46093e3..f7da8a57410f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/AmbientVolumeUiController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/AmbientVolumeUiController.java
@@ -80,6 +80,7 @@ public class AmbientVolumeUiController implements
mLocalDataManager = new HearingDeviceLocalDataManager(context);
mLocalDataManager.setOnDeviceLocalDataChangeListener(this,
ThreadUtils.getBackgroundExecutor());
+ mLocalDataManager.start();
}
@VisibleForTesting
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
index bf86911ee683..f2c013598cdc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
@@ -431,7 +431,10 @@ public class CsipDeviceManager {
for (BluetoothDevice device : sinksToSync) {
log("addMemberDevicesIntoMainDevice: sync audio sharing source to "
+ device.getAnonymizedAddress());
- assistant.addSource(device, metadata, /* isGroupOp= */ false);
+ if (assistant.getConnectionStatus(device)
+ == BluetoothProfile.STATE_CONNECTED) {
+ assistant.addSource(device, metadata, /* isGroupOp= */ false);
+ }
}
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt b/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt
index c4e724554c04..21d518a644a9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt
@@ -27,7 +27,6 @@ import android.content.IntentFilter
import android.database.ContentObserver
import android.os.Handler
import android.provider.Settings
-import com.android.settingslib.flags.Flags
import com.android.settingslib.notification.modes.ZenMode
import com.android.settingslib.notification.modes.ZenModesBackend
import java.time.Duration
@@ -35,6 +34,7 @@ import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.callbackFlow
@@ -72,7 +72,7 @@ class ZenModeRepositoryImpl(
private val notificationManager: NotificationManager,
private val backend: ZenModesBackend,
private val contentResolver: ContentResolver,
- val scope: CoroutineScope,
+ val applicationScope: CoroutineScope,
val backgroundCoroutineContext: CoroutineContext,
// This is nullable just to simplify testing, since SettingsLib doesn't have a good way
// to create a fake handler.
@@ -104,7 +104,7 @@ class ZenModeRepositoryImpl(
awaitClose { context.unregisterReceiver(receiver) }
}
.flowOn(backgroundCoroutineContext)
- .shareIn(started = SharingStarted.WhileSubscribed(), scope = scope)
+ .shareIn(started = SharingStarted.WhileSubscribed(), scope = applicationScope)
}
override val consolidatedNotificationPolicy: StateFlow<NotificationManager.Policy?> by lazy {
@@ -129,14 +129,11 @@ class ZenModeRepositoryImpl(
.map { mapper(it) }
.onStart { emit(mapper(null)) }
.flowOn(backgroundCoroutineContext)
- .stateIn(scope, SharingStarted.WhileSubscribed(), null)
+ .stateIn(applicationScope, SharingStarted.WhileSubscribed(), null)
private val zenConfigChanged by lazy {
if (android.app.Flags.modesUi()) {
callbackFlow {
- // emit an initial value
- trySend(Unit)
-
val observer =
object : ContentObserver(backgroundHandler) {
override fun onChange(selfChange: Boolean) {
@@ -163,16 +160,18 @@ class ZenModeRepositoryImpl(
}
}
- override val modes: Flow<List<ZenMode>> by lazy {
- if (android.app.Flags.modesUi()) {
+ override val modes: StateFlow<List<ZenMode>> =
+ if (android.app.Flags.modesUi())
zenConfigChanged
.map { backend.modes }
.distinctUntilChanged()
.flowOn(backgroundCoroutineContext)
- } else {
- flowOf(emptyList())
- }
- }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.Eagerly,
+ initialValue = backend.modes,
+ )
+ else MutableStateFlow<List<ZenMode>>(emptyList())
/**
* Gets the current list of [ZenMode] instances according to the backend.
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt
index 78dba57028ba..a9329ba3c76c 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt
@@ -181,8 +181,10 @@ class DeviceStateAutoRotateSettingManagerImplTest {
@Test
fun getAutoRotateSetting_forInvalidPosture_returnsSettingForFallbackPosture() {
- persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_UNLOCKED)
- persistSettings(DEVICE_STATE_ROTATION_KEY_FOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED)
+ persistSettings(
+ "$DEVICE_STATE_ROTATION_KEY_FOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED:" +
+ "$DEVICE_STATE_ROTATION_KEY_UNFOLDED:$DEVICE_STATE_ROTATION_LOCK_UNLOCKED"
+ )
val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_HALF_FOLDED)
@@ -276,7 +278,6 @@ class DeviceStateAutoRotateSettingManagerImplTest {
SettableDeviceState(DEVICE_STATE_ROTATION_KEY_FOLDED, isSettable = true),
SettableDeviceState(DEVICE_STATE_ROTATION_KEY_HALF_FOLDED, isSettable = false),
SettableDeviceState(DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY, isSettable = false),
- SettableDeviceState(DEVICE_STATE_ROTATION_LOCK_IGNORED, isSettable = false),
)
}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerProviderTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerProviderTest.kt
new file mode 100644
index 000000000000..c3ec4edfdee5
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerProviderTest.kt
@@ -0,0 +1,89 @@
+/*
+ * 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.settingslib.devicestate
+
+import android.content.Context
+import android.os.Handler
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.window.flags.Flags
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
+import java.util.concurrent.Executor
+import org.mockito.Mockito.`when` as whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceStateAutoRotateSettingManagerProviderTest {
+
+ @get:Rule
+ val setFlagsRule: SetFlagsRule = SetFlagsRule()
+
+ @get:Rule val rule = MockitoJUnit.rule()
+
+ private val context: Context = ApplicationProvider.getApplicationContext()
+
+ @Mock
+ private lateinit var mockExecutor: Executor
+
+ @Mock
+ private lateinit var mockSecureSettings: SecureSettings
+
+ @Mock
+ private lateinit var mockMainHandler: Handler
+
+ @Mock
+ private lateinit var mockPosturesHelper: PosturesHelper
+
+ @Before
+ fun setup() {
+ whenever(mockSecureSettings.getStringForUser(any(), anyInt())).thenReturn("")
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DEVICE_STATE_AUTO_ROTATE_SETTING_REFACTOR)
+ fun createInstance_refactorFlagEnabled_returnsRefactoredManager() {
+ val manager =
+ DeviceStateAutoRotateSettingManagerProvider.createInstance(
+ context, mockExecutor, mockSecureSettings, mockMainHandler, mockPosturesHelper
+ )
+
+ assertThat(manager).isInstanceOf(DeviceStateAutoRotateSettingManagerImpl::class.java)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DEVICE_STATE_AUTO_ROTATE_SETTING_REFACTOR)
+ fun createInstance_refactorFlagDisabled_returnsLegacyManager() {
+ val manager =
+ DeviceStateAutoRotateSettingManagerProvider.createInstance(
+ context, mockExecutor, mockSecureSettings, mockMainHandler, mockPosturesHelper
+ )
+
+ assertThat(manager).isInstanceOf(DeviceStateRotationLockSettingsManager::class.java)
+ }
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
index baebaf7dfef0..b23ea5f1786f 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
@@ -151,8 +151,8 @@ public class DeviceStateRotationLockSettingsManagerTest {
new String[]{"2:1", "1:0:1", "0:2"});
List<SettableDeviceState> settableDeviceStates =
- DeviceStateRotationLockSettingsManager.getInstance(
- mMockContext).getSettableDeviceStates();
+ new DeviceStateRotationLockSettingsManager(mMockContext,
+ mFakeSecureSettings).getSettableDeviceStates();
assertThat(settableDeviceStates).containsExactly(
new SettableDeviceState(/* deviceState= */ 2, /* isSettable= */ true),
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CsipDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CsipDeviceManagerTest.java
index fd14d1ff6786..4314982752c3 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CsipDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CsipDeviceManagerTest.java
@@ -377,6 +377,7 @@ public class CsipDeviceManagerTest {
BluetoothLeBroadcastReceiveState.class);
when(state.getBisSyncState()).thenReturn(ImmutableList.of(1L));
when(mAssistant.getAllSources(mDevice2)).thenReturn(ImmutableList.of(state));
+ when(mAssistant.getConnectionStatus(mDevice1)).thenReturn(BluetoothAdapter.STATE_CONNECTED);
when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
when(mUserManager.isManagedProfile()).thenReturn(true);
@@ -407,6 +408,7 @@ public class CsipDeviceManagerTest {
BluetoothLeBroadcastReceiveState.class);
when(state.getBisSyncState()).thenReturn(ImmutableList.of(1L));
when(mAssistant.getAllSources(mDevice2)).thenReturn(ImmutableList.of(state));
+ when(mAssistant.getConnectionStatus(mDevice1)).thenReturn(BluetoothAdapter.STATE_CONNECTED);
assertThat(mCsipDeviceManager.addMemberDevicesIntoMainDevice(GROUP1, preferredDevice))
.isTrue();
@@ -474,6 +476,8 @@ public class CsipDeviceManagerTest {
BluetoothLeBroadcastReceiveState.class);
when(state.getBisSyncState()).thenReturn(ImmutableList.of(1L));
when(mAssistant.getAllSources(mDevice1)).thenReturn(ImmutableList.of(state));
+ when(mAssistant.getConnectionStatus(mDevice2)).thenReturn(BluetoothAdapter.STATE_CONNECTED);
+ when(mAssistant.getConnectionStatus(mDevice3)).thenReturn(BluetoothAdapter.STATE_CONNECTED);
assertThat(mCsipDeviceManager.addMemberDevicesIntoMainDevice(GROUP1, preferredDevice))
.isTrue();
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/data/repository/ZenModeRepositoryTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/data/repository/ZenModeRepositoryTest.kt
index b364368df473..ec7baf6bf081 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/data/repository/ZenModeRepositoryTest.kt
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/data/repository/ZenModeRepositoryTest.kt
@@ -75,10 +75,14 @@ class ZenModeRepositoryTest {
private val testScope: TestScope = TestScope()
+ private val initialModes = listOf(TestModeBuilder().setId("Built-in").build())
+
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
+ `when`(zenModesBackend.modes).thenReturn(initialModes)
+
underTest =
ZenModeRepositoryImpl(
context,
@@ -151,8 +155,8 @@ class ZenModeRepositoryTest {
fun modesListEmitsOnSettingsChange() {
testScope.runTest {
val values = mutableListOf<List<ZenMode>>()
- val modes1 = listOf(TestModeBuilder().setId("One").build())
- `when`(zenModesBackend.modes).thenReturn(modes1)
+
+ // an initial list of modes is read when the stateflow is created
underTest.modes.onEach { values.add(it) }.launchIn(backgroundScope)
runCurrent()
@@ -172,7 +176,7 @@ class ZenModeRepositoryTest {
triggerZenModeSettingUpdate()
runCurrent()
- assertThat(values).containsExactly(modes1, modes2, modes3).inOrder()
+ assertThat(values).containsExactly(initialModes, modes2, modes3).inOrder()
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index 2273b4f81eea..527a1f16a84f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -16,6 +16,8 @@
package com.android.providers.settings;
+import static com.android.settingslib.devicestate.DeviceStateAutoRotateSettingUtils.isDeviceStateRotationLockEnabled;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -49,7 +51,6 @@ import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.LocalePicker;
import com.android.server.backup.Flags;
-import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
import java.io.FileNotFoundException;
import java.util.ArrayList;
@@ -348,7 +349,7 @@ public class SettingsHelper {
private boolean shouldSkipAutoRotateRestore() {
// When device state based auto rotation settings are available, let's skip the restoring
// of the standard auto rotation settings to avoid conflicting setting values.
- return DeviceStateRotationLockSettingsManager.isDeviceStateRotationLockEnabled(mContext);
+ return isDeviceStateRotationLockEnabled(mContext);
}
public String onBackupValue(String name, String value) {
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 7f4544379cd3..2b17ae48132c 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -220,6 +220,7 @@ filegroup {
"tests/src/**/systemui/statusbar/phone/StatusBarBoundsProviderTest.kt",
"tests/src/**/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt",
"tests/src/**/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt",
+ "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryKairosAdapterTest.kt",
"tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt",
"tests/src/**/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt",
"tests/src/**/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt",
@@ -356,7 +357,9 @@ filegroup {
"tests/src/**/systemui/qs/tiles/AlarmTileTest.kt",
"tests/src/**/systemui/qs/tiles/BluetoothTileTest.kt",
"tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt",
+ "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryKairosAdapterTest.kt",
"tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt",
+ "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionKairosAdapterTelephonySmokeTests.kt",
"tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt",
"tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt",
"tests/src/**/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfigTest.kt",
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/Android.bp b/packages/SystemUI/accessibility/accessibilitymenu/Android.bp
index fb1f715bc68f..172f687ef6fd 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/Android.bp
+++ b/packages/SystemUI/accessibility/accessibilitymenu/Android.bp
@@ -40,6 +40,7 @@ android_app {
"com_android_systemui_flags_lib",
"SettingsLibDisplayUtils",
"SettingsLibSettingsTheme",
+ "SystemUI-shared-utils",
"com_android_a11y_menu_flags_lib",
"//frameworks/libs/systemui:view_capture",
],
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
index a60778658c59..db2fbd96408c 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
@@ -22,8 +22,6 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.View.ACCESSIBILITY_LIVE_REGION_POLITE;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
-import static com.android.app.viewcapture.ViewCaptureFactory.getViewCaptureAwareWindowManagerInstance;
-
import static java.lang.Math.max;
import android.animation.Animator;
@@ -55,11 +53,11 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.UiContext;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService;
import com.android.systemui.accessibility.accessibilitymenu.R;
import com.android.systemui.accessibility.accessibilitymenu.activity.A11yMenuSettingsActivity.A11yMenuPreferenceFragment;
import com.android.systemui.accessibility.accessibilitymenu.model.A11yMenuShortcut;
+import com.android.systemui.utils.windowmanager.WindowManagerUtils;
import java.util.ArrayList;
import java.util.List;
@@ -145,9 +143,7 @@ public class A11yMenuOverlayLayout {
final Display display = mDisplayManager.getDisplay(DEFAULT_DISPLAY);
final Context uiContext = mService.createWindowContext(
display, TYPE_ACCESSIBILITY_OVERLAY, /* options= */null);
- final ViewCaptureAwareWindowManager windowManager =
- getViewCaptureAwareWindowManagerInstance(uiContext,
- com.android.systemui.Flags.enableViewCaptureTracing());
+ final WindowManager windowManager = WindowManagerUtils.getWindowManager(uiContext);
mLayout = new A11yMenuFrameLayout(uiContext);
updateLayoutPosition(uiContext);
inflateLayoutAndSetOnTouchListener(mLayout, uiContext);
@@ -162,8 +158,7 @@ public class A11yMenuOverlayLayout {
public void clearLayout() {
if (mLayout != null) {
- ViewCaptureAwareWindowManager windowManager = getViewCaptureAwareWindowManagerInstance(
- mLayout.getContext(), com.android.systemui.Flags.enableViewCaptureTracing());
+ WindowManager windowManager = WindowManagerUtils.getWindowManager(mLayout.getContext());
if (windowManager != null) {
windowManager.removeView(mLayout);
}
@@ -178,7 +173,7 @@ public class A11yMenuOverlayLayout {
return;
}
updateLayoutPosition(mLayout.getContext());
- WindowManager windowManager = mLayout.getContext().getSystemService(WindowManager.class);
+ WindowManager windowManager = WindowManagerUtils.getWindowManager(mLayout.getContext());
if (windowManager != null) {
windowManager.updateViewLayout(mLayout, mLayoutParameter);
}
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java
index b899c45b0f7e..842834b5fc27 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java
@@ -36,6 +36,7 @@ import com.android.systemui.accessibility.accessibilitymenu.R;
import com.android.systemui.accessibility.accessibilitymenu.activity.A11yMenuSettingsActivity.A11yMenuPreferenceFragment;
import com.android.systemui.accessibility.accessibilitymenu.model.A11yMenuShortcut;
import com.android.systemui.accessibility.accessibilitymenu.view.A11yMenuFooter.A11yMenuFooterCallBack;
+import com.android.systemui.utils.windowmanager.WindowManagerUtils;
import java.util.ArrayList;
import java.util.List;
@@ -292,8 +293,8 @@ public class A11yMenuViewPager {
// Keeps footer window height unchanged no matter the density is changed.
mA11yMenuFooter.adjustFooterToDensityScale(densityScale);
// Adjust the view pager height for system bar and display cutout insets.
- WindowManager windowManager = mA11yMenuLayout.getContext()
- .getSystemService(WindowManager.class);
+ WindowManager windowManager = WindowManagerUtils
+ .getWindowManager(mA11yMenuLayout.getContext());
WindowMetrics windowMetric = windowManager.getCurrentWindowMetrics();
Insets windowInsets = windowMetric.getWindowInsets().getInsetsIgnoringVisibility(
WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout());
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index cea91a18ae32..9da07170430f 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -289,6 +289,15 @@ flag {
}
}
+flag {
+ name: "notification_skip_silent_updates"
+ namespace: "systemui"
+ description: "Do not notify HeadsUpManager for silent updates."
+ bug: "401068530"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
flag {
name: "scene_container"
@@ -533,6 +542,14 @@ flag {
}
flag {
+ name: "icon_refresh_2025"
+ namespace: "systemui"
+ description: "Build time flag for 2025 icon refresh"
+ bug: "391605373"
+ is_fixed_read_only: true
+}
+
+flag {
name: "promote_notifications_automatically"
namespace: "systemui"
description: "Flag to automatically turn certain notifications into promoted notifications so "
@@ -2155,3 +2172,17 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "extended_apps_shortcut_category"
+ namespace: "systemui"
+ description: "Allow users to add shortcuts to open apps that are not present in the apps category in shortcut helper by default"
+ bug: "394290928"
+}
+
+flag {
+ name: "use_aad_prox_sensor"
+ namespace: "systemui"
+ description: "Use AAD proximity sensor if flag is enabled and sensor is present"
+ bug: "402534470"
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
index 1fb7901dcb59..440a81fc2152 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
@@ -1088,9 +1088,8 @@ constructor(
if (!success) finishedCallback?.onAnimationFinished()
}
} else {
- // This should never happen, as either the controller or factory should always be
- // defined. This final call is for safety in case something goes wrong.
- Log.wtf(TAG, "initAndRun with neither a controller nor factory")
+ // This happens when onDisposed() has already been called due to the animation being
+ // cancelled. Only issue the callback.
finishedCallback?.onAnimationFinished()
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimFlingBehavior.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimFlingBehavior.kt
new file mode 100644
index 000000000000..bc38ef0abb25
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimFlingBehavior.kt
@@ -0,0 +1,73 @@
+/*
+ * 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.notifications.ui.composable
+
+import androidx.compose.animation.core.AnimationState
+import androidx.compose.animation.core.DecayAnimationSpec
+import androidx.compose.animation.core.animateDecay
+import androidx.compose.foundation.gestures.FlingBehavior
+import androidx.compose.foundation.gestures.ScrollScope
+import androidx.compose.ui.MotionDurationScale
+import com.android.systemui.scene.session.ui.composable.rememberSession
+import kotlinx.coroutines.CancellationException
+import kotlinx.coroutines.withContext
+import kotlin.math.abs
+
+/**
+ * Fork of [androidx.compose.foundation.gestures.DefaultFlingBehavior] to allow us to use it with
+ * [rememberSession].
+ */
+internal class NotificationScrimFlingBehavior(
+ private var flingDecay: DecayAnimationSpec<Float>,
+ private val motionDurationScale: MotionDurationScale = NotificationScrimMotionDurationScale
+) : FlingBehavior {
+ override suspend fun ScrollScope.performFling(initialVelocity: Float): Float {
+ // come up with the better threshold, but we need it since spline curve gives us NaNs
+ return withContext(motionDurationScale) {
+ if (abs(initialVelocity) > 1f) {
+ var velocityLeft = initialVelocity
+ var lastValue = 0f
+ val animationState =
+ AnimationState(
+ initialValue = 0f,
+ initialVelocity = initialVelocity,
+ )
+ try {
+ animationState.animateDecay(flingDecay) {
+ val delta = value - lastValue
+ val consumed = scrollBy(delta)
+ lastValue = value
+ velocityLeft = this.velocity
+ // avoid rounding errors and stop if anything is unconsumed
+ if (abs(delta - consumed) > 0.5f) this.cancelAnimation()
+ }
+ } catch (exception: CancellationException) {
+ velocityLeft = animationState.velocity
+ }
+ velocityLeft
+ } else {
+ initialVelocity
+ }
+ }
+ }
+}
+
+internal val NotificationScrimMotionDurationScale =
+ object : MotionDurationScale {
+ override val scaleFactor: Float
+ get() = 1f
+ } \ No newline at end of file
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
index 3e907e87c13b..79b346439d5d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
@@ -20,12 +20,13 @@ package com.android.systemui.notifications.ui.composable
import android.util.Log
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.AnimationVector1D
+import androidx.compose.animation.core.DecayAnimationSpec
import androidx.compose.animation.core.tween
+import androidx.compose.animation.splineBasedDecay
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.Orientation
-import androidx.compose.foundation.gestures.ScrollableDefaults
import androidx.compose.foundation.gestures.animateScrollBy
import androidx.compose.foundation.gestures.rememberScrollableState
import androidx.compose.foundation.gestures.scrollBy
@@ -59,7 +60,6 @@ import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
@@ -97,6 +97,7 @@ import com.android.systemui.common.ui.compose.windowinsets.LocalScreenCornerRadi
import com.android.systemui.res.R
import com.android.systemui.scene.session.ui.composable.SaveableSession
import com.android.systemui.scene.session.ui.composable.rememberSession
+import com.android.systemui.scene.session.ui.composable.sessionCoroutineScope
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.ui.composable.ShadeHeader
import com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent
@@ -298,7 +299,7 @@ fun ContentScope.NotificationScrollingStack(
onEmptySpaceClick: (() -> Unit)? = null,
modifier: Modifier = Modifier,
) {
- val coroutineScope = rememberCoroutineScope()
+ val coroutineScope = shadeSession.sessionCoroutineScope()
val density = LocalDensity.current
val screenCornerRadius = LocalScreenCornerRadius.current
val scrimCornerRadius = dimensionResource(R.dimen.notification_scrim_corner_radius)
@@ -308,8 +309,6 @@ fun ContentScope.NotificationScrollingStack(
ScrollState(initial = 0)
}
val syntheticScroll = viewModel.syntheticScroll.collectAsStateWithLifecycle(0f)
- val isCurrentGestureOverscroll =
- viewModel.isCurrentGestureOverscroll.collectAsStateWithLifecycle(false)
val expansionFraction by viewModel.expandFraction.collectAsStateWithLifecycle(0f)
val shadeToQsFraction by viewModel.shadeToQsFraction.collectAsStateWithLifecycle(0f)
@@ -454,15 +453,15 @@ fun ContentScope.NotificationScrollingStack(
}
}
- val flingBehavior = ScrollableDefaults.flingBehavior()
val scrimNestedScrollConnection =
shadeSession.rememberSession(
scrimOffset,
- maxScrimTop,
minScrimTop,
- isCurrentGestureOverscroll,
- flingBehavior,
+ viewModel.isCurrentGestureOverscroll,
+ density,
) {
+ val flingSpec: DecayAnimationSpec<Float> = splineBasedDecay(density)
+ val flingBehavior = NotificationScrimFlingBehavior(flingSpec)
NotificationScrimNestedScrollConnection(
scrimOffset = { scrimOffset.value },
snapScrimOffset = { value -> coroutineScope.launch { scrimOffset.snapTo(value) } },
@@ -473,7 +472,7 @@ fun ContentScope.NotificationScrollingStack(
maxScrimOffset = 0f,
contentHeight = { stackHeight.intValue.toFloat() },
minVisibleScrimHeight = minVisibleScrimHeight,
- isCurrentGestureOverscroll = { isCurrentGestureOverscroll.value },
+ isCurrentGestureOverscroll = { viewModel.isCurrentGestureOverscroll },
flingBehavior = flingBehavior,
)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/session/ui/composable/Session.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/session/ui/composable/Session.kt
index 4eaacf31e23d..1750b11a998b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/session/ui/composable/Session.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/session/ui/composable/Session.kt
@@ -17,10 +17,14 @@
package com.android.systemui.scene.session.ui.composable
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffectResult
+import androidx.compose.runtime.DisposableEffectScope
+import androidx.compose.runtime.RememberObserver
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.currentCompositeKeyHash
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.rememberCompositionContext
import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.SaverScope
import androidx.compose.runtime.saveable.mapSaver
@@ -28,6 +32,10 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import com.android.systemui.scene.session.shared.SessionStorage
import com.android.systemui.util.kotlin.mapValuesNotNullTo
+import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.EmptyCoroutineContext
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
/**
* An explicit storage for remembering composable state outside of the lifetime of a composition.
@@ -89,6 +97,55 @@ fun <T> Session.rememberSession(vararg inputs: Any?, key: String? = null, init:
rememberSession(key, *inputs, init = init)
/**
+ * A side effect of composition that must be reversed or cleaned up if the [Session] ends.
+ *
+ * @see androidx.compose.runtime.DisposableEffect
+ */
+@Composable
+fun Session.SessionDisposableEffect(
+ vararg inputs: Any?,
+ key: String? = null,
+ effect: DisposableEffectScope.() -> DisposableEffectResult,
+) {
+ rememberSession(inputs, key) {
+ object : RememberObserver {
+
+ var onDispose: DisposableEffectResult? = null
+
+ override fun onAbandoned() {
+ // no-op
+ }
+
+ override fun onForgotten() {
+ onDispose?.dispose()
+ onDispose = null
+ }
+
+ override fun onRemembered() {
+ onDispose = DisposableEffectScope().effect()
+ }
+ }
+ }
+}
+
+/**
+ * Return a [CoroutineScope] bound to this [Session] using the optional [CoroutineContext] provided
+ * by [getContext]. [getContext] will only be called once and the same [CoroutineScope] instance
+ * will be returned for the duration of the [Session].
+ *
+ * @see androidx.compose.runtime.rememberCoroutineScope
+ */
+@Composable
+fun Session.sessionCoroutineScope(
+ getContext: () -> CoroutineContext = { EmptyCoroutineContext }
+): CoroutineScope {
+ val effectContext: CoroutineContext = rememberCompositionContext().effectCoroutineContext
+ val job = rememberSession { Job() }
+ SessionDisposableEffect { onDispose { job.cancel() } }
+ return rememberSession { CoroutineScope(effectContext + job + getContext()) }
+}
+
+/**
* An explicit storage for remembering composable state outside of the lifetime of a composition.
*
* Specifically, this allows easy conversion of standard [rememberSession] invocations to ones that
@@ -147,15 +204,10 @@ interface SaveableSession : Session {
* location in the composition tree.
*/
@Composable
-fun rememberSaveableSession(
- vararg inputs: Any?,
- key: String? = null,
-): SaveableSession =
+fun rememberSaveableSession(vararg inputs: Any?, key: String? = null): SaveableSession =
rememberSaveable(*inputs, SaveableSessionImpl.SessionSaver, key) { SaveableSessionImpl() }
-private class SessionImpl(
- private val storage: SessionStorage = SessionStorage(),
-) : Session {
+private class SessionImpl(private val storage: SessionStorage = SessionStorage()) : Session {
@Composable
override fun <T> rememberSession(key: String?, vararg inputs: Any?, init: () -> T): T {
val storage = storage.storage
@@ -169,16 +221,31 @@ private class SessionImpl(
}
if (finalKey !in storage) {
val value = init()
- SideEffect { storage[finalKey] = SessionStorage.StorageEntry(inputs, value) }
+ SideEffect {
+ storage[finalKey] = SessionStorage.StorageEntry(inputs, value)
+ if (value is RememberObserver) {
+ value.onRemembered()
+ }
+ }
return value
}
val entry = storage[finalKey]!!
if (!inputs.contentEquals(entry.keys)) {
val value = init()
- SideEffect { entry.stored = value }
+ SideEffect {
+ val oldValue = entry.stored
+ if (oldValue is RememberObserver) {
+ oldValue.onForgotten()
+ }
+ entry.stored = value
+ if (value is RememberObserver) {
+ value.onRemembered()
+ }
+ }
return value
}
- @Suppress("UNCHECKED_CAST") return entry.stored as T
+ @Suppress("UNCHECKED_CAST")
+ return entry.stored as T
}
}
@@ -228,7 +295,8 @@ private class SaveableSessionImpl(
}
return value
}
- @Suppress("UNCHECKED_CAST") return entry.stored as T
+ @Suppress("UNCHECKED_CAST")
+ return entry.stored as T
}
}
}
@@ -263,7 +331,7 @@ private class SaveableSessionImpl(
v?.let { StorageEntry.Unrestored(v) }
}
)
- }
+ },
)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
index 068218a0053a..b30e12f073ad 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
@@ -55,6 +55,7 @@ import com.android.compose.animation.scene.LowestZIndexContentPicker
import com.android.compose.windowsizeclass.LocalWindowSizeClass
import com.android.mechanics.behavior.VerticalExpandContainerSpec
import com.android.mechanics.behavior.verticalExpandContainerBackground
+import com.android.systemui.Flags
import com.android.systemui.res.R
import com.android.systemui.shade.ui.composable.OverlayShade.rememberShadeExpansionMotion
@@ -189,9 +190,17 @@ object OverlayShade {
}
object Colors {
- val ScrimBackground = Color(0f, 0f, 0f, alpha = 0.2f)
+ val ScrimBackground = Color(0f, 0f, 0f, alpha = 0.3f)
val PanelBackground: Color
- @Composable @ReadOnlyComposable get() = MaterialTheme.colorScheme.surfaceContainer
+ @Composable
+ @ReadOnlyComposable
+ get() {
+ return if (Flags.notificationShadeBlur()) {
+ MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.4f)
+ } else {
+ MaterialTheme.colorScheme.surfaceContainer
+ }
+ }
}
object Dimensions {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
index addb08fd2319..03debb6fa7ca 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
@@ -293,6 +293,25 @@ fun ContentScope.OverlayShadeHeader(
viewModel: ShadeHeaderViewModel,
modifier: Modifier = Modifier,
) {
+ OverlayShadeHeaderPartialStateless(
+ viewModel,
+ viewModel.showClock,
+ modifier,
+ )
+}
+
+/**
+ * Ideally, we should have a stateless function for overlay shade header, which facilitates testing.
+ * However, it is cumbersome to implement such a stateless function, especially when some of the
+ * overlay shade header's children accept a view model as the param. Therefore, this function only
+ * break up the clock visibility. It is where "PartialStateless" comes from.
+ */
+@Composable
+fun ContentScope.OverlayShadeHeaderPartialStateless(
+ viewModel: ShadeHeaderViewModel,
+ showClock: Boolean,
+ modifier: Modifier = Modifier,
+) {
val horizontalPadding =
max(LocalScreenCornerRadius.current / 2f, Shade.Dimensions.HorizontalPadding)
@@ -308,7 +327,7 @@ fun ContentScope.OverlayShadeHeader(
modifier = Modifier.padding(horizontal = horizontalPadding),
) {
val chipHighlight = viewModel.notificationsChipHighlight
- if (viewModel.showClock) {
+ if (showClock) {
Clock(
onClick = viewModel::onClockClicked,
modifier = Modifier.padding(horizontal = 4.dp),
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
index f9492dad85df..a65afad9cb7a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
@@ -127,6 +127,8 @@ fun VolumeSlider(
SliderDefaults.colors(
activeTickColor = MaterialTheme.colorScheme.surfaceContainerHigh,
inactiveTrackColor = MaterialTheme.colorScheme.surfaceContainerHigh,
+ disabledActiveTickColor = MaterialTheme.colorScheme.surfaceContainerHigh,
+ disabledInactiveTrackColor = MaterialTheme.colorScheme.surfaceContainerHigh,
)
Slider(
value = state.value,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
index aa95abb3528f..0b0088926aae 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
@@ -73,14 +73,10 @@ import android.widget.ImageView;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.app.viewcapture.ViewCapture;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.res.R;
-import kotlin.Lazy;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -108,8 +104,6 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
private SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
@Mock
private MagnificationModeSwitch.ClickListener mClickListener;
- @Mock
- private Lazy<ViewCapture> mLazyViewCapture;
private TestableWindowManager mWindowManager;
private ViewPropertyAnimator mViewPropertyAnimator;
private MagnificationModeSwitch mMagnificationModeSwitch;
@@ -123,7 +117,6 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
mContext = Mockito.spy(getContext());
final WindowManager wm = mContext.getSystemService(WindowManager.class);
mWindowManager = spy(new TestableWindowManager(wm));
- mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
mContext.addMockSystemService(Context.ACCESSIBILITY_SERVICE, mAccessibilityManager);
mSpyImageView = Mockito.spy(new ImageView(mContext));
mViewPropertyAnimator = Mockito.spy(mSpyImageView.animate());
@@ -139,10 +132,8 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
return null;
}).when(mSfVsyncFrameProvider).postFrameCallback(
any(Choreographer.FrameCallback.class));
- ViewCaptureAwareWindowManager vwm = new ViewCaptureAwareWindowManager(mWindowManager,
- mLazyViewCapture, false);
- mMagnificationModeSwitch = new MagnificationModeSwitch(mContext, mSpyImageView,
- mSfVsyncFrameProvider, mClickListener, vwm);
+ mMagnificationModeSwitch = new MagnificationModeSwitch(mContext, mWindowManager,
+ mSpyImageView, mSfVsyncFrameProvider, mClickListener);
assertNotNull(mTouchListener);
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationSettingsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationSettingsControllerTest.java
index 3cd3fefb8ef0..02ec5aac120c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationSettingsControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationSettingsControllerTest.java
@@ -23,11 +23,11 @@ import static org.mockito.Mockito.verify;
import android.content.pm.ActivityInfo;
import android.testing.TestableLooper;
+import android.view.WindowManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.accessibility.WindowMagnificationSettings.MagnificationSize;
@@ -58,15 +58,15 @@ public class MagnificationSettingsControllerTest extends SysuiTestCase {
@Mock
private SecureSettings mSecureSettings;
@Mock
- private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
+ private WindowManager mWindowManager;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mMagnificationSettingsController = new MagnificationSettingsController(
mContext, mSfVsyncFrameProvider,
- mMagnificationSettingControllerCallback, mSecureSettings,
- mWindowMagnificationSettings, mViewCaptureAwareWindowManager);
+ mMagnificationSettingControllerCallback, mSecureSettings, mWindowManager,
+ mWindowMagnificationSettings);
}
@After
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java
index 12c866f0adb2..463bfe5ae73f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java
@@ -27,14 +27,15 @@ import static org.mockito.Mockito.verify;
import android.content.Context;
import android.graphics.Point;
+import android.os.Looper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.WindowManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
@@ -48,7 +49,7 @@ import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class MirrorWindowControlTest extends SysuiTestCase {
- @Mock ViewCaptureAwareWindowManager mWindowManager;
+ @Mock WindowManager mWindowManager;
View mView;
int mViewWidth;
int mViewHeight;
@@ -69,8 +70,12 @@ public class MirrorWindowControlTest extends SysuiTestCase {
return null;
}).when(mWindowManager).addView(any(View.class), any(LayoutParams.class));
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+
mStubMirrorWindowControl = new StubMirrorWindowControl(getContext(), mView, mViewWidth,
- mViewHeight);
+ mViewHeight, mWindowManager);
}
@Test
@@ -122,8 +127,9 @@ public class MirrorWindowControlTest extends SysuiTestCase {
boolean mInvokeOnCreateView = false;
- StubMirrorWindowControl(Context context, View view, int width, int height) {
- super(context);
+ StubMirrorWindowControl(Context context, View view, int width, int height,
+ WindowManager windowManager) {
+ super(context, windowManager);
mView = view;
mWidth = width;
mHeight = height;
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
index e1e515eb31f5..b1c837a99f3d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
@@ -24,11 +24,11 @@ import android.provider.Settings;
import android.testing.TestableLooper;
import android.view.Display;
import android.view.View;
+import android.view.WindowManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.systemui.SysuiTestCase;
import org.junit.After;
@@ -51,8 +51,6 @@ public class ModeSwitchesControllerTest extends SysuiTestCase {
private View mSpyView;
@Mock
private MagnificationModeSwitch.ClickListener mListener;
- @Mock
- private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
@Before
@@ -61,8 +59,9 @@ public class ModeSwitchesControllerTest extends SysuiTestCase {
mSupplier = new FakeSwitchSupplier(mContext.getSystemService(DisplayManager.class));
mModeSwitchesController = new ModeSwitchesController(mSupplier);
mModeSwitchesController.setClickListenerDelegate(mListener);
- mModeSwitch = Mockito.spy(new MagnificationModeSwitch(mContext, mModeSwitchesController,
- mViewCaptureAwareWindowManager));
+ WindowManager wm = mContext.getSystemService(WindowManager.class);
+ mModeSwitch = Mockito.spy(new MagnificationModeSwitch(mContext, wm,
+ mModeSwitchesController));
mSpyView = Mockito.spy(new View(mContext));
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
index 6edf94939010..95ebd8190e9c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
@@ -72,7 +72,7 @@ public class DragToInteractAnimationControllerTest extends SysuiTestCase {
stubWindowManager);
final MenuView stubMenuView = spy(new MenuView(mContext, stubMenuViewModel,
stubMenuViewAppearance, mockSecureSettings));
- mInteractView = spy(new DragToInteractView(mContext));
+ mInteractView = spy(new DragToInteractView(mContext, stubWindowManager));
mDismissView = spy(new DismissView(mContext));
if (Flags.floatingMenuDragToEdit()) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
index fff6def52803..572d140b850d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
@@ -101,7 +101,7 @@ public class MenuListViewTouchHandlerTest extends SysuiTestCase {
mStubMenuView.setTranslationY(0);
mMenuAnimationController = spy(new MenuAnimationController(
mStubMenuView, stubMenuViewAppearance));
- mInteractView = spy(new DragToInteractView(mContext));
+ mInteractView = spy(new DragToInteractView(mContext, windowManager));
mDismissView = spy(new DismissView(mContext));
if (Flags.floatingMenuDragToEdit()) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java
index 4f043109a534..b5fa52570d6a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java
@@ -39,15 +39,11 @@ import android.view.accessibility.AccessibilityManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.app.viewcapture.ViewCapture;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.settingslib.bluetooth.HearingAidDeviceManager;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.util.settings.SecureSettings;
-import kotlin.Lazy;
-
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -78,16 +74,11 @@ public class MenuViewLayerControllerTest extends SysuiTestCase {
@Mock
private WindowMetrics mWindowMetrics;
- @Mock
- private Lazy<ViewCapture> mLazyViewCapture;
-
private MenuViewLayerController mMenuViewLayerController;
@Before
public void setUp() throws Exception {
final WindowManager wm = mContext.getSystemService(WindowManager.class);
- final ViewCaptureAwareWindowManager viewCaptureAwareWm = new ViewCaptureAwareWindowManager(
- mWindowManager, mLazyViewCapture, /* isViewCaptureEnabled= */ false);
doAnswer(invocation -> wm.getMaximumWindowMetrics()).when(
mWindowManager).getMaximumWindowMetrics();
mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
@@ -95,8 +86,8 @@ public class MenuViewLayerControllerTest extends SysuiTestCase {
when(mWindowMetrics.getBounds()).thenReturn(new Rect(0, 0, 1080, 2340));
when(mWindowMetrics.getWindowInsets()).thenReturn(stubDisplayInsets());
mMenuViewLayerController = new MenuViewLayerController(mContext, mWindowManager,
- viewCaptureAwareWm, mAccessibilityManager, mSecureSettings,
- mock(NavigationModeController.class), mHearingAidDeviceManager);
+ mAccessibilityManager, mSecureSettings, mock(NavigationModeController.class),
+ mHearingAidDeviceManager);
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
index d118ace08b85..530aba4993a8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
@@ -176,6 +176,8 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase {
.thenReturn(SETTINGS_PACKAGE_NAME);
when(mDevice.getBondState()).thenReturn(BOND_BONDED);
when(mDevice.isConnected()).thenReturn(true);
+ when(mDevice.getAddress()).thenReturn(DEVICE_ADDRESS);
+ when(mDevice.getAnonymizedAddress()).thenReturn(DEVICE_ADDRESS);
when(mCachedDevice.getDevice()).thenReturn(mDevice);
when(mCachedDevice.getAddress()).thenReturn(DEVICE_ADDRESS);
when(mCachedDevice.getName()).thenReturn(DEVICE_NAME);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index 446891a7873e..c4fafc192260 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -41,7 +41,6 @@ import android.widget.ScrollView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.app.viewcapture.ViewCapture
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.widget.LockPatternUtils
import com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN
@@ -116,7 +115,6 @@ open class AuthContainerViewTest : SysuiTestCase() {
@Mock private lateinit var packageManager: PackageManager
@Mock private lateinit var activityTaskManager: ActivityTaskManager
@Mock private lateinit var accessibilityManager: AccessibilityManager
- @Mock private lateinit var lazyViewCapture: Lazy<ViewCapture>
private lateinit var displayRepository: FakeDisplayRepository
private lateinit var displayStateInteractor: DisplayStateInteractor
@@ -689,7 +687,6 @@ open class AuthContainerViewTest : SysuiTestCase() {
{ credentialViewModel },
fakeExecutor,
vibrator,
- lazyViewCapture,
msdlPlayer,
) {
override fun postOnAnimation(runnable: Runnable) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
index a1a2aa70d869..f4185ee48510 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -83,7 +83,6 @@ import android.view.WindowManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.app.viewcapture.ViewCapture;
import com.android.internal.R;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.widget.LockPatternUtils;
@@ -100,11 +99,10 @@ import com.android.systemui.util.concurrency.Execution;
import com.android.systemui.util.concurrency.FakeExecution;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import com.google.android.msdl.domain.MSDLPlayer;
-import dagger.Lazy;
-
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -175,8 +173,6 @@ public class AuthControllerTest extends SysuiTestCase {
private PromptViewModel mPromptViewModel;
@Mock
private UdfpsUtils mUdfpsUtils;
- @Mock
- private Lazy<ViewCapture> mLazyViewCapture;
@Captor
private ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> mFpAuthenticatorsRegisteredCaptor;
@@ -198,6 +194,8 @@ public class AuthControllerTest extends SysuiTestCase {
private KeyguardManager mKeyguardManager;
@Mock
private MSDLPlayer mMSDLPlayer;
+ @Mock
+ private WindowManagerProvider mWindowManagerProvider;
private TestableContext mContextSpy;
private Execution mExecution;
@@ -1198,7 +1196,8 @@ public class AuthControllerTest extends SysuiTestCase {
when(mDisplayManager.getDisplay(displayId)).thenReturn(mockDisplay);
}
doReturn(mockDisplayContext).when(mContextSpy).createDisplayContext(mockDisplay);
- when(mockDisplayContext.getSystemService(WindowManager.class)).thenReturn(mockDisplayWM);
+ when(mWindowManagerProvider.getWindowManager(mockDisplayContext))
+ .thenReturn(mockDisplayWM);
return mockDisplayWM;
}
@@ -1214,7 +1213,7 @@ public class AuthControllerTest extends SysuiTestCase {
() -> mLogContextInteractor, () -> mPromptSelectionInteractor,
() -> mCredentialViewModel, () -> mPromptViewModel, mInteractionJankMonitor,
mHandler, mBackgroundExecutor, mUdfpsUtils, mVibratorHelper, mKeyguardManager,
- mLazyViewCapture, mMSDLPlayer);
+ mMSDLPlayer, mWindowManagerProvider);
}
@Override
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index a2f5a30a20ff..675c9deaff23 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -45,12 +45,12 @@ import android.view.LayoutInflater;
import android.view.Surface;
import android.view.View;
import android.view.ViewRootImpl;
+import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.logging.InstanceIdSequence;
import com.android.internal.util.LatencyTracker;
import com.android.keyguard.KeyguardUpdateMonitor;
@@ -129,7 +129,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Mock
private FingerprintManager mFingerprintManager;
@Mock
- private ViewCaptureAwareWindowManager mWindowManager;
+ private WindowManager mWindowManager;
@Mock
private StatusBarStateController mStatusBarStateController;
@Mock
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractorTest.kt
index 95334b5aaf09..856a62e3f5a7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractorTest.kt
@@ -25,9 +25,10 @@ import com.android.systemui.common.data.repository.fake
import com.android.systemui.communal.data.model.FEATURE_AUTO_OPEN
import com.android.systemui.communal.data.model.FEATURE_MANUAL_OPEN
import com.android.systemui.communal.data.model.SuppressionReason
+import com.android.systemui.communal.posturing.data.model.PositionState
import com.android.systemui.communal.posturing.data.repository.fake
import com.android.systemui.communal.posturing.data.repository.posturingRepository
-import com.android.systemui.communal.posturing.shared.model.PosturedState
+import com.android.systemui.communal.posturing.domain.interactor.advanceTimeBySlidingWindowAndRun
import com.android.systemui.dock.DockManager
import com.android.systemui.dock.fakeDockManager
import com.android.systemui.kosmos.Kosmos
@@ -127,7 +128,12 @@ class CommunalAutoOpenInteractorTest : SysuiTestCase() {
)
batteryRepository.fake.setDevicePluggedIn(true)
- posturingRepository.fake.setPosturedState(PosturedState.NotPostured)
+ posturingRepository.fake.emitPositionState(
+ PositionState(
+ stationary = PositionState.StationaryState.Stationary(confidence = 1f),
+ orientation = PositionState.OrientationState.NotPostured(confidence = 1f),
+ )
+ )
assertThat(shouldAutoOpen).isFalse()
assertThat(suppressionReason)
@@ -135,7 +141,13 @@ class CommunalAutoOpenInteractorTest : SysuiTestCase() {
SuppressionReason.ReasonWhenToAutoShow(FEATURE_AUTO_OPEN or FEATURE_MANUAL_OPEN)
)
- posturingRepository.fake.setPosturedState(PosturedState.Postured(1f))
+ posturingRepository.fake.emitPositionState(
+ PositionState(
+ stationary = PositionState.StationaryState.Stationary(confidence = 1f),
+ orientation = PositionState.OrientationState.Postured(confidence = 1f),
+ )
+ )
+ advanceTimeBySlidingWindowAndRun()
assertThat(shouldAutoOpen).isTrue()
assertThat(suppressionReason).isNull()
}
@@ -153,7 +165,7 @@ class CommunalAutoOpenInteractorTest : SysuiTestCase() {
)
batteryRepository.fake.setDevicePluggedIn(true)
- posturingRepository.fake.setPosturedState(PosturedState.Postured(1f))
+ posturingRepository.fake.emitPositionState(PositionState())
fakeDockManager.setIsDocked(true)
assertThat(shouldAutoOpen).isFalse()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorTest.kt
index 0df8834618d5..b4708d97c4c3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorTest.kt
@@ -16,22 +16,39 @@
package com.android.systemui.communal.posturing.domain.interactor
+import android.hardware.Sensor
+import android.hardware.TriggerEventListener
+import android.platform.test.annotations.EnableFlags
+import android.service.dreams.Flags.FLAG_ALLOW_DREAM_WHEN_POSTURED
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.posturing.data.model.PositionState
import com.android.systemui.communal.posturing.data.repository.fake
import com.android.systemui.communal.posturing.data.repository.posturingRepository
import com.android.systemui.communal.posturing.shared.model.PosturedState
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.advanceTimeBy
import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runCurrent
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.testKosmos
+import com.android.systemui.util.sensors.asyncSensorManager
import com.google.common.truth.Truth.assertThat
+import kotlin.time.Duration.Companion.hours
+import kotlin.time.Duration.Companion.milliseconds
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.stub
@SmallTest
@RunWith(AndroidJUnit4::class)
+@EnableFlags(FLAG_ALLOW_DREAM_WHEN_POSTURED)
class PosturingInteractorTest : SysuiTestCase() {
private val kosmos = testKosmos().useUnconfinedTestDispatcher()
@@ -44,8 +61,166 @@ class PosturingInteractorTest : SysuiTestCase() {
val postured by collectLastValue(underTest.postured)
assertThat(postured).isFalse()
- posturingRepository.fake.setPosturedState(PosturedState.Postured(1f))
+ posturingRepository.fake.emitPositionState(
+ PositionState(
+ stationary = PositionState.StationaryState.Stationary(confidence = 1f),
+ orientation = PositionState.OrientationState.Postured(confidence = 1f),
+ )
+ )
+
+ advanceTimeBySlidingWindowAndRun()
+ assertThat(postured).isTrue()
+ }
+
+ @Test
+ fun testLowConfidenceOrientation() =
+ kosmos.runTest {
+ val postured by collectLastValue(underTest.postured)
+ assertThat(postured).isFalse()
+
+ posturingRepository.fake.emitPositionState(
+ PositionState(
+ stationary = PositionState.StationaryState.Stationary(confidence = 1f),
+ orientation = PositionState.OrientationState.Postured(confidence = 0.2f),
+ )
+ )
+
+ advanceTimeBySlidingWindowAndRun()
+ assertThat(postured).isFalse()
+ }
+
+ @Test
+ fun testLowConfidenceStationary() =
+ kosmos.runTest {
+ val postured by collectLastValue(underTest.postured)
+ assertThat(postured).isFalse()
+
+ posturingRepository.fake.emitPositionState(
+ PositionState(
+ stationary = PositionState.StationaryState.Stationary(confidence = 1f),
+ orientation = PositionState.OrientationState.Postured(confidence = 0.2f),
+ )
+ )
+
+ advanceTimeBySlidingWindowAndRun()
+ assertThat(postured).isFalse()
+ }
+
+ @Test
+ fun testSlidingWindow() =
+ kosmos.runTest {
+ val postured by collectLastValue(underTest.postured)
+ assertThat(postured).isFalse()
+
+ posturingRepository.fake.emitPositionState(
+ PositionState(
+ stationary = PositionState.StationaryState.Stationary(confidence = 1f),
+ orientation = PositionState.OrientationState.Postured(confidence = 0.2f),
+ )
+ )
+
+ advanceTimeBy(PosturingInteractor.SLIDING_WINDOW_DURATION / 2)
+ runCurrent()
+ assertThat(postured).isFalse()
+
+ posturingRepository.fake.emitPositionState(
+ PositionState(
+ stationary = PositionState.StationaryState.Stationary(confidence = 1f),
+ orientation = PositionState.OrientationState.Postured(confidence = 1f),
+ )
+ )
+ assertThat(postured).isFalse()
+ advanceTimeBy(PosturingInteractor.SLIDING_WINDOW_DURATION / 2)
+ runCurrent()
+
+ // The 0.2 confidence will have fallen out of the sliding window, and we should now flip
+ // to true.
+ assertThat(postured).isTrue()
+
+ advanceTimeBy(9999.hours)
+ // We should remain postured if no other updates are received.
+ assertThat(postured).isTrue()
+ }
+
+ @Test
+ fun testLiftGesture_afterSlidingWindow() =
+ kosmos.runTest {
+ val triggerSensor = stubSensorManager()
+ val sensor = asyncSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE)!!
+
+ val postured by collectLastValue(underTest.postured)
+ assertThat(postured).isFalse()
+
+ posturingRepository.fake.emitPositionState(
+ PositionState(
+ stationary = PositionState.StationaryState.Stationary(confidence = 1f),
+ orientation = PositionState.OrientationState.Postured(confidence = 1f),
+ )
+ )
+
+ advanceTimeBySlidingWindowAndRun()
+ assertThat(postured).isTrue()
+
+ // If we detect a lift gesture, we should transition back to not postured.
+ triggerSensor(sensor)
+ assertThat(postured).isFalse()
+
+ advanceTimeBy(9999.hours)
+ assertThat(postured).isFalse()
+ }
+
+ @Test
+ fun testLiftGesture_overridesSlidingWindow() =
+ kosmos.runTest {
+ val triggerSensor = stubSensorManager()
+ val sensor = asyncSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE)!!
+
+ val postured by collectLastValue(underTest.postured)
+ assertThat(postured).isFalse()
+
+ // Add multiple stationary + postured events to the sliding window.
+ repeat(100) {
+ advanceTimeBy(1.milliseconds)
+ posturingRepository.fake.emitPositionState(
+ PositionState(
+ stationary = PositionState.StationaryState.Stationary(confidence = 1f),
+ orientation = PositionState.OrientationState.Postured(confidence = 1f),
+ )
+ )
+ }
+
+ assertThat(postured).isTrue()
+
+ // If we detect a lift gesture, we should transition back to not postured immediately.
+ triggerSensor(sensor)
+ assertThat(postured).isFalse()
+ }
+
+ @Test
+ fun testSignificantMotion_afterSlidingWindow() =
+ kosmos.runTest {
+ val triggerSensor = stubSensorManager()
+ val sensor = asyncSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION)!!
+
+ val postured by collectLastValue(underTest.postured)
+ assertThat(postured).isFalse()
+
+ posturingRepository.fake.emitPositionState(
+ PositionState(
+ stationary = PositionState.StationaryState.Stationary(confidence = 1f),
+ orientation = PositionState.OrientationState.Postured(confidence = 1f),
+ )
+ )
+
+ advanceTimeBySlidingWindowAndRun()
assertThat(postured).isTrue()
+
+ // If we detect motion, we should transition back to not postured.
+ triggerSensor(sensor)
+ assertThat(postured).isFalse()
+
+ advanceTimeBy(9999.hours)
+ assertThat(postured).isFalse()
}
@Test
@@ -55,12 +230,51 @@ class PosturingInteractorTest : SysuiTestCase() {
assertThat(postured).isFalse()
underTest.setValueForDebug(PosturedState.NotPostured)
- posturingRepository.fake.setPosturedState(PosturedState.Postured(1f))
+ posturingRepository.fake.emitPositionState(
+ PositionState(
+ stationary = PositionState.StationaryState.Stationary(confidence = 1f),
+ orientation = PositionState.OrientationState.Postured(confidence = 1f),
+ )
+ )
// Repository value is overridden by debug value
assertThat(postured).isFalse()
underTest.setValueForDebug(PosturedState.Unknown)
+
+ advanceTimeBySlidingWindowAndRun()
assertThat(postured).isTrue()
}
+
+ private fun Kosmos.stubSensorManager(): (sensor: Sensor) -> Unit {
+ val callbacks = mutableMapOf<Sensor, List<TriggerEventListener>>()
+ val pickupSensor = mock<Sensor>()
+ val motionSensor = mock<Sensor>()
+
+ asyncSensorManager.stub {
+ on { getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE) } doReturn pickupSensor
+ on { getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION) } doReturn motionSensor
+ on { requestTriggerSensor(any(), any()) } doAnswer
+ {
+ val callback = it.arguments[0] as TriggerEventListener
+ val sensor = it.arguments[1] as Sensor
+ callbacks[sensor] = callbacks.getOrElse(sensor) { emptyList() } + callback
+ true
+ }
+ on { cancelTriggerSensor(any(), any()) } doAnswer
+ {
+ val callback = it.arguments[0] as TriggerEventListener
+ val sensor = it.arguments[1] as Sensor
+ callbacks[sensor] = callbacks.getOrElse(sensor) { emptyList() } - callback
+ true
+ }
+ }
+
+ return { sensor: Sensor ->
+ val list = callbacks.getOrElse(sensor) { emptyList() }
+ // Simulate a trigger sensor which unregisters callbacks after triggering.
+ callbacks[sensor] = emptyList()
+ list.forEach { it.onTrigger(mock()) }
+ }
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
index b42eddd12e4e..fd99313a17b7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
@@ -36,7 +36,6 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
import androidx.test.filters.SmallTest
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.app.viewcapture.ViewCaptureFactory
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.internal.logging.UiEventLogger
@@ -143,7 +142,6 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() {
mock<ScrimManager> { on { currentController }.thenReturn(mScrimController) }
private val mSystemDialogsCloser = mock<SystemDialogsCloser>()
private val mDreamOverlayCallbackController = mock<DreamOverlayCallbackController>()
- private val mLazyViewCapture = lazy { viewCaptureSpy }
private val mViewCaptor = argumentCaptor<View>()
private val mTouchHandlersCaptor = argumentCaptor<Set<TouchHandler>>()
@@ -156,7 +154,6 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() {
private val gestureInteractor = spy(kosmos.gestureInteractor)
private lateinit var mCommunalInteractor: CommunalInteractor
- private lateinit var mViewCaptureAwareWindowManager: ViewCaptureAwareWindowManager
private lateinit var environmentComponents: EnvironmentComponents
private lateinit var mService: DreamOverlayService
@@ -244,18 +241,12 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() {
mComplicationComponentFactory,
mAmbientTouchComponentFactory,
)
- mViewCaptureAwareWindowManager =
- ViewCaptureAwareWindowManager(
- mWindowManager,
- mLazyViewCapture,
- isViewCaptureEnabled = false,
- )
mService =
DreamOverlayService(
mContext,
mLifecycleOwner,
mMainExecutor,
- mViewCaptureAwareWindowManager,
+ mWindowManager,
mComplicationComponentFactory,
mDreamComplicationComponentFactory,
mDreamOverlayComponentFactory,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt
index 63229dbb47a4..8fdbf9b45d2e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt
@@ -229,7 +229,6 @@ class FromAlternateBouncerTransitionInteractorTest(flags: FlagsParameterization)
}
@Test
- @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
fun transitionToDreaming() =
kosmos.runTest {
fakePowerRepository.updateWakefulness(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt
index 8a11be0ab8fa..b1427f21345c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt
@@ -219,17 +219,13 @@ class MediaFilterRepositoryTest : SysuiTestCase() {
underTest.addSelectedUserMediaEntry(playingData1)
underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(playingInstanceId1))
underTest.addSelectedUserMediaEntry(playingData2)
- underTest.addMediaDataLoadingState(
- MediaDataLoadingModel.Loaded(playingInstanceId2, false)
- )
+ underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(playingInstanceId2))
assertThat(currentMedia?.size).isEqualTo(2)
assertThat(currentMedia)
.containsExactly(
MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(playingInstanceId1)),
- MediaCommonModel.MediaControl(
- MediaDataLoadingModel.Loaded(playingInstanceId2, false)
- ),
+ MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(playingInstanceId2)),
)
.inOrder()
@@ -238,9 +234,7 @@ class MediaFilterRepositoryTest : SysuiTestCase() {
assertThat(currentMedia?.size).isEqualTo(2)
assertThat(currentMedia)
.containsExactly(
- MediaCommonModel.MediaControl(
- MediaDataLoadingModel.Loaded(playingInstanceId2, false)
- ),
+ MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(playingInstanceId2)),
MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(playingInstanceId1)),
)
.inOrder()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/util/MediaDiffUtilTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/util/MediaDiffUtilTest.kt
index faa62c2febc1..45128e23c900 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/util/MediaDiffUtilTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/util/MediaDiffUtilTest.kt
@@ -21,7 +21,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
import com.android.systemui.SysuiTestCase
-import com.android.systemui.media.controls.ui.viewmodel.MediaCommonViewModel
+import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel
import com.android.systemui.media.controls.ui.viewmodel.mediaControlViewModel
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -37,8 +37,8 @@ class MediaDiffUtilTest : SysuiTestCase() {
@Test
fun newMediaControlAdded() {
- val mediaControl = createMediaControl(InstanceId.fakeInstanceId(123), true)
- val oldList = listOf<MediaCommonViewModel>()
+ val mediaControl = createMediaControl(InstanceId.fakeInstanceId(123))
+ val oldList = listOf<MediaControlViewModel>()
val newList = listOf(mediaControl)
val mediaLoadedCallback = MediaViewModelCallback(oldList, newList)
val mediaLoadedListUpdateCallback =
@@ -56,18 +56,18 @@ class MediaDiffUtilTest : SysuiTestCase() {
@Test
fun updateMediaControl_contentChanged() {
- val mediaControl = createMediaControl(InstanceId.fakeInstanceId(123), true)
+ val mediaControl = createMediaControl(InstanceId.fakeInstanceId(123))
val oldList = listOf(mediaControl)
- val newList = listOf(mediaControl.copy(immediatelyUpdateUi = false))
+ val newList = listOf(mediaControl.copy())
val mediaLoadedCallback = MediaViewModelCallback(oldList, newList)
val mediaLoadedListUpdateCallback =
MediaViewModelListUpdateCallback(
oldList,
newList,
- { commonViewModel, _ -> fail("Unexpected to add $commonViewModel") },
- { commonViewModel, _ -> assertThat(commonViewModel).isNotEqualTo(mediaControl) },
+ { controlViewModel, _ -> fail("Unexpected to add $controlViewModel") },
+ { controlViewModel, _ -> assertThat(controlViewModel).isNotEqualTo(mediaControl) },
{ fail("Unexpected to remove $it") },
- { commonViewModel, _, _ -> fail("Unexpected to move $commonViewModel ") },
+ { controlViewModel, _, _ -> fail("Unexpected to move $controlViewModel ") },
)
DiffUtil.calculateDiff(mediaLoadedCallback).dispatchUpdatesTo(mediaLoadedListUpdateCallback)
@@ -75,8 +75,8 @@ class MediaDiffUtilTest : SysuiTestCase() {
@Test
fun mediaControlMoved() {
- val mediaControl1 = createMediaControl(InstanceId.fakeInstanceId(123), true)
- val mediaControl2 = createMediaControl(InstanceId.fakeInstanceId(456), false)
+ val mediaControl1 = createMediaControl(InstanceId.fakeInstanceId(123))
+ val mediaControl2 = createMediaControl(InstanceId.fakeInstanceId(456))
val oldList = listOf(mediaControl1, mediaControl2)
val newList = listOf(mediaControl2, mediaControl1)
val mediaLoadedCallback = MediaViewModelCallback(oldList, newList)
@@ -84,10 +84,10 @@ class MediaDiffUtilTest : SysuiTestCase() {
MediaViewModelListUpdateCallback(
oldList,
newList,
- { commonViewModel, _ -> fail("Unexpected to add $commonViewModel") },
- { commonViewModel, _ -> fail("Unexpected to update $commonViewModel") },
+ { controlViewModel, _ -> fail("Unexpected to add $controlViewModel") },
+ { controlViewModel, _ -> fail("Unexpected to update $controlViewModel") },
{ fail("Unexpected to remove $it") },
- { commonViewModel, _, _ -> assertThat(commonViewModel).isEqualTo(mediaControl1) },
+ { controlViewModel, _, _ -> assertThat(controlViewModel).isEqualTo(mediaControl1) },
)
DiffUtil.calculateDiff(mediaLoadedCallback).dispatchUpdatesTo(mediaLoadedListUpdateCallback)
@@ -95,34 +95,24 @@ class MediaDiffUtilTest : SysuiTestCase() {
@Test
fun mediaControlRemoved() {
- val mediaControl = createMediaControl(InstanceId.fakeInstanceId(123), true)
+ val mediaControl = createMediaControl(InstanceId.fakeInstanceId(123))
val oldList = listOf(mediaControl)
- val newList = listOf<MediaCommonViewModel>()
+ val newList = listOf<MediaControlViewModel>()
val mediaLoadedCallback = MediaViewModelCallback(oldList, newList)
val mediaLoadedListUpdateCallback =
MediaViewModelListUpdateCallback(
oldList,
newList,
- { commonViewModel, _ -> fail("Unexpected to add $commonViewModel") },
- { commonViewModel, _ -> fail("Unexpected to update $commonViewModel") },
- { commonViewModel -> assertThat(commonViewModel).isEqualTo(mediaControl) },
- { commonViewModel, _, _ -> fail("Unexpected to move $commonViewModel ") },
+ { controlViewModel, _ -> fail("Unexpected to add $controlViewModel") },
+ { controlViewModel, _ -> fail("Unexpected to update $controlViewModel") },
+ { controlViewModel -> assertThat(controlViewModel).isEqualTo(mediaControl) },
+ { controlViewModel, _, _ -> fail("Unexpected to move $controlViewModel ") },
)
DiffUtil.calculateDiff(mediaLoadedCallback).dispatchUpdatesTo(mediaLoadedListUpdateCallback)
}
- private fun createMediaControl(
- instanceId: InstanceId,
- immediatelyUpdateUi: Boolean,
- ): MediaCommonViewModel.MediaControl {
- return MediaCommonViewModel.MediaControl(
- instanceId = instanceId,
- immediatelyUpdateUi = immediatelyUpdateUi,
- controlViewModel = kosmos.mediaControlViewModel,
- onAdded = {},
- onRemoved = {},
- onUpdated = {},
- )
+ private fun createMediaControl(instanceId: InstanceId): MediaControlViewModel {
+ return kosmos.mediaControlViewModel.copy(instanceId = instanceId)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt
index e56b114dc847..fab5a3cdf8fd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt
@@ -86,23 +86,23 @@ class MediaCarouselViewModelTest : SysuiTestCase() {
loadMediaControl(KEY_2, instanceId2, isPlaying = true)
loadMediaControl(KEY, instanceId1, isPlaying = false)
- var mediaControl2 = sortedMedia?.get(0) as MediaCommonViewModel.MediaControl
- var mediaControl1 = sortedMedia?.get(1) as MediaCommonViewModel.MediaControl
+ var mediaControl2 = sortedMedia?.get(0) as MediaControlViewModel
+ var mediaControl1 = sortedMedia?.get(1) as MediaControlViewModel
assertThat(mediaControl2.instanceId).isEqualTo(instanceId2)
assertThat(mediaControl1.instanceId).isEqualTo(instanceId1)
loadMediaControl(KEY, instanceId1, isPlaying = true)
loadMediaControl(KEY_2, instanceId2, isPlaying = false)
- mediaControl2 = sortedMedia?.get(0) as MediaCommonViewModel.MediaControl
- mediaControl1 = sortedMedia?.get(1) as MediaCommonViewModel.MediaControl
+ mediaControl2 = sortedMedia?.get(0) as MediaControlViewModel
+ mediaControl1 = sortedMedia?.get(1) as MediaControlViewModel
assertThat(mediaControl2.instanceId).isEqualTo(instanceId2)
assertThat(mediaControl1.instanceId).isEqualTo(instanceId1)
underTest.onReorderingAllowed()
- mediaControl1 = sortedMedia?.get(0) as MediaCommonViewModel.MediaControl
- mediaControl2 = sortedMedia?.get(1) as MediaCommonViewModel.MediaControl
+ mediaControl1 = sortedMedia?.get(0) as MediaControlViewModel
+ mediaControl2 = sortedMedia?.get(1) as MediaControlViewModel
assertThat(mediaControl1.instanceId).isEqualTo(instanceId1)
assertThat(mediaControl2.instanceId).isEqualTo(instanceId2)
}
@@ -115,7 +115,7 @@ class MediaCarouselViewModelTest : SysuiTestCase() {
loadMediaControl(KEY, instanceId)
- val mediaControl = sortedMedia?.get(0) as MediaCommonViewModel.MediaControl
+ val mediaControl = sortedMedia?.get(0) as MediaControlViewModel
assertThat(mediaControl.instanceId).isEqualTo(instanceId)
// when media control is added to carousel
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt
index 55e52b780488..18b14a4b3752 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt
@@ -55,8 +55,6 @@ class TaskPreviewSizeProviderTest : SysuiTestCase() {
@Before
fun setup() {
- whenever(mockContext.getSystemService(eq(WindowManager::class.java)))
- .thenReturn(windowManager)
whenever(mockContext.resources).thenReturn(resources)
}
@@ -154,7 +152,8 @@ class TaskPreviewSizeProviderTest : SysuiTestCase() {
return TaskPreviewSizeProvider(
mockContext,
windowMetricsProvider,
- testConfigurationController
+ testConfigurationController,
+ windowManager
)
.also { it.addCallback(listener) }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
index 09e49eb217b0..0e8c5079579e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
@@ -82,7 +82,6 @@ import android.view.inputmethod.InputMethodManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.SysuiTestCase;
@@ -218,8 +217,6 @@ public class NavigationBarTest extends SysuiTestCase {
@Mock
private WindowManager mWindowManager;
@Mock
- private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
- @Mock
private TelecomManager mTelecomManager;
@Mock
private InputMethodManager mInputMethodManager;
@@ -685,7 +682,6 @@ public class NavigationBarTest extends SysuiTestCase {
null,
context,
mWindowManager,
- mViewCaptureAwareWindowManager,
() -> mAssistManager,
mock(AccessibilityManager.class),
deviceProvisionedController,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt
index 52a0a5445002..7fb66ce21919 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt
@@ -22,8 +22,7 @@ import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.Swipe
import com.android.compose.animation.scene.UserActionResult.HideOverlay
-import com.android.compose.animation.scene.UserActionResult.ShowOverlay
-import com.android.compose.animation.scene.UserActionResult.ShowOverlay.HideCurrentOverlays
+import com.android.compose.animation.scene.UserActionResult.ReplaceByOverlay
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.EnableSceneContainer
@@ -77,10 +76,8 @@ class NotificationsShadeOverlayActionsViewModelTest : SysuiTestCase() {
underTest.activateIn(this)
val action =
- (actions?.get(Swipe.Down(fromSource = SceneContainerArea.EndHalf)) as? ShowOverlay)
+ (actions?.get(Swipe.Down(fromSource = SceneContainerArea.TopEdgeEndHalf))
+ as? ReplaceByOverlay)
assertThat(action?.overlay).isEqualTo(Overlays.QuickSettingsShade)
- val overlaysToHide = action?.hideCurrentOverlays as? HideCurrentOverlays.Some
- assertThat(overlaysToHide).isNotNull()
- assertThat(overlaysToHide?.overlays).containsExactly(Overlays.NotificationsShade)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt
index 80ce43d61003..ffcd95bc7a4a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt
@@ -157,7 +157,7 @@ class NotificationsShadeOverlayContentViewModelTest : SysuiTestCase() {
}
@Test
- fun showMedia_noActiveMedia_false() =
+ fun showMedia_InactiveMedia_false() =
testScope.runTest {
kosmos.mediaFilterRepository.addSelectedUserMediaEntry(MediaData(active = false))
runCurrent()
@@ -166,6 +166,16 @@ class NotificationsShadeOverlayContentViewModelTest : SysuiTestCase() {
}
@Test
+ fun showMedia_noMedia_false() =
+ testScope.runTest {
+ kosmos.mediaFilterRepository.addSelectedUserMediaEntry(MediaData(active = true))
+ kosmos.mediaFilterRepository.clearSelectedUserMedia()
+ runCurrent()
+
+ assertThat(underTest.showMedia).isFalse()
+ }
+
+ @Test
fun showMedia_qsDisabled_false() =
testScope.runTest {
kosmos.mediaFilterRepository.addSelectedUserMediaEntry(MediaData(active = true))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/power/PowerUITest.java
index 338ed7596bd6..70ded2a7f21c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/power/PowerUITest.java
@@ -40,11 +40,11 @@ import android.service.vr.IVrStateCallbacks;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.testing.TestableResources;
+import android.view.WindowManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.app.viewcapture.ViewCapture;
import com.android.settingslib.fuelgauge.Estimate;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -54,8 +54,6 @@ import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
-import dagger.Lazy;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -96,7 +94,7 @@ public class PowerUITest extends SysuiTestCase {
@Mock private BroadcastDispatcher mBroadcastDispatcher;
@Mock private CommandQueue mCommandQueue;
@Mock private IVrManager mVrManager;
- @Mock private Lazy<ViewCapture> mLazyViewCapture;
+ @Mock private WindowManager mWindowManager;
@Before
public void setup() {
@@ -709,7 +707,7 @@ public class PowerUITest extends SysuiTestCase {
mWakefulnessLifecycle,
mPowerManager,
mUserTracker,
- mLazyViewCapture);
+ mWindowManager);
mPowerUI.mThermalService = mThermalServiceMock;
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelTest.kt
index 701e55d0759d..d11701570e32 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelTest.kt
@@ -95,11 +95,21 @@ class QuickSettingsContainerViewModelTest : SysuiTestCase() {
}
@Test
- fun showMedia_noActiveMedia_false() =
+ fun showMedia_InactiveMedia_true() =
testScope.runTest {
kosmos.mediaFilterRepository.addSelectedUserMediaEntry(MediaData(active = false))
runCurrent()
+ assertThat(underTest.showMedia).isTrue()
+ }
+
+ @Test
+ fun showMedia_noMedia_false() =
+ testScope.runTest {
+ kosmos.mediaFilterRepository.addSelectedUserMediaEntry(MediaData(active = true))
+ kosmos.mediaFilterRepository.clearSelectedUserMedia()
+ runCurrent()
+
assertThat(underTest.showMedia).isFalse()
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt
index b98059a1fe90..882d296d94b4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt
@@ -22,8 +22,7 @@ import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.Swipe
import com.android.compose.animation.scene.UserActionResult.HideOverlay
-import com.android.compose.animation.scene.UserActionResult.ShowOverlay
-import com.android.compose.animation.scene.UserActionResult.ShowOverlay.HideCurrentOverlays
+import com.android.compose.animation.scene.UserActionResult.ReplaceByOverlay
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.EnableSceneContainer
@@ -90,11 +89,8 @@ class QuickSettingsShadeOverlayActionsViewModelTest : SysuiTestCase() {
underTest.activateIn(this)
val action =
- (actions?.get(Swipe.Down(fromSource = SceneContainerArea.StartHalf))
- as? ShowOverlay)
+ (actions?.get(Swipe.Down(fromSource = SceneContainerArea.TopEdgeStartHalf))
+ as? ReplaceByOverlay)
assertThat(action?.overlay).isEqualTo(Overlays.NotificationsShade)
- val overlaysToHide = action?.hideCurrentOverlays as? HideCurrentOverlays.Some
- assertThat(overlaysToHide).isNotNull()
- assertThat(overlaysToHide?.overlays).containsExactly(Overlays.QuickSettingsShade)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerSwipeDetectorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerSwipeDetectorTest.kt
index a09e5cd9de9b..7536bb3cc02a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerSwipeDetectorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerSwipeDetectorTest.kt
@@ -32,6 +32,8 @@ import com.android.systemui.scene.ui.viewmodel.SceneContainerArea.Resolved.LeftE
import com.android.systemui.scene.ui.viewmodel.SceneContainerArea.Resolved.LeftHalf
import com.android.systemui.scene.ui.viewmodel.SceneContainerArea.Resolved.RightEdge
import com.android.systemui.scene.ui.viewmodel.SceneContainerArea.Resolved.RightHalf
+import com.android.systemui.scene.ui.viewmodel.SceneContainerArea.Resolved.TopEdgeLeftHalf
+import com.android.systemui.scene.ui.viewmodel.SceneContainerArea.Resolved.TopEdgeRightHalf
import com.android.systemui.scene.ui.viewmodel.SceneContainerArea.StartEdge
import com.android.systemui.scene.ui.viewmodel.SceneContainerArea.StartHalf
import com.google.common.truth.Truth.assertThat
@@ -55,9 +57,9 @@ class SceneContainerSwipeDetectorTest : SysuiTestCase() {
}
@Test
- fun source_swipeVerticallyOnTopLeft_detectsLeftHalf() {
+ fun source_swipeVerticallyOnTopLeft_detectsTopEdgeLeftHalf() {
val detectedEdge = swipeVerticallyFrom(x = 1, y = edgeSize - 1)
- assertThat(detectedEdge).isEqualTo(LeftHalf)
+ assertThat(detectedEdge).isEqualTo(TopEdgeLeftHalf)
}
@Test
@@ -69,7 +71,7 @@ class SceneContainerSwipeDetectorTest : SysuiTestCase() {
@Test
fun source_swipeVerticallyOnTopRight_detectsRightHalf() {
val detectedEdge = swipeVerticallyFrom(x = screenWidth - 1, y = edgeSize - 1)
- assertThat(detectedEdge).isEqualTo(RightHalf)
+ assertThat(detectedEdge).isEqualTo(TopEdgeRightHalf)
}
@Test
@@ -79,14 +81,26 @@ class SceneContainerSwipeDetectorTest : SysuiTestCase() {
}
@Test
- fun source_swipeVerticallyToLeftOfSplit_detectsLeftHalf() {
+ fun source_swipeVerticallyOnTopEdge_ToLeftOfSplit_detectsTopEdgeLeftHalf() {
val detectedEdge = swipeVerticallyFrom(x = (screenWidth / 2) - 1, y = edgeSize - 1)
+ assertThat(detectedEdge).isEqualTo(TopEdgeLeftHalf)
+ }
+
+ @Test
+ fun source_swipeVerticallyBelowTopEdge_ToLeftOfSplit_detectsLeftHalf() {
+ val detectedEdge = swipeVerticallyFrom(x = (screenWidth / 2) - 1, y = edgeSize + 1)
assertThat(detectedEdge).isEqualTo(LeftHalf)
}
@Test
- fun source_swipeVerticallyToRightOfSplit_detectsRightHalf() {
+ fun source_swipeVerticallyOnTopEdge_toRightOfSplit_detectsTopEdgeRightHalf() {
val detectedEdge = swipeVerticallyFrom(x = (screenWidth / 2) + 1, y = edgeSize - 1)
+ assertThat(detectedEdge).isEqualTo(TopEdgeRightHalf)
+ }
+
+ @Test
+ fun source_swipeVerticallyBelowTopEdge_toRightOfSplit_detectsRightHalf() {
+ val detectedEdge = swipeVerticallyFrom(x = (screenWidth / 2) + 1, y = edgeSize + 1)
assertThat(detectedEdge).isEqualTo(RightHalf)
}
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 94db429c2225..676e1ea5321a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -172,6 +172,7 @@ import com.android.systemui.user.domain.interactor.UserSwitcherInteractor;
import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.util.time.SystemClock;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import com.android.systemui.wallpapers.ui.viewmodel.WallpaperFocalAreaViewModel;
import com.android.wm.shell.animation.FlingAnimationUtils;
@@ -296,6 +297,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
@Mock private LargeScreenHeaderHelper mLargeScreenHeaderHelper;
@Mock private StatusBarLongPressGestureDetector mStatusBarLongPressGestureDetector;
@Mock protected SysUIStateDisplaysInteractor mSysUIStateDisplaysInteractor;
+ @Mock private WindowManagerProvider mWindowManagerProvider;
protected final int mMaxUdfpsBurnInOffsetY = 5;
protected FakeFeatureFlagsClassic mFeatureFlags = new FakeFeatureFlagsClassic();
protected KeyguardClockInteractor mKeyguardClockInteractor;
@@ -672,7 +674,8 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
mCastController,
new ResourcesSplitShadeStateController(),
() -> mKosmos.getCommunalTransitionViewModel(),
- () -> mLargeScreenHeaderHelper
+ () -> mLargeScreenHeaderHelper,
+ mWindowManagerProvider
);
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index 3788049256a2..fd17b7a32fd3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -50,7 +50,6 @@ import android.view.WindowManager;
import androidx.test.filters.SmallTest;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
@@ -100,7 +99,7 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
@Rule public final CheckFlagsRule checkFlagsRule =
DeviceFlagsValueProvider.createCheckFlagsRule();
- @Mock private ViewCaptureAwareWindowManager mWindowManager;
+ @Mock private WindowManager mWindowManager;
@Mock private DozeParameters mDozeParameters;
@Spy private final NotificationShadeWindowView mNotificationShadeWindowView = spy(
new NotificationShadeWindowView(mContext, null));
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
index 7433267ab3b0..db0c07c50dc6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
@@ -28,6 +28,7 @@ import android.os.Handler;
import android.os.Looper;
import android.view.ViewGroup;
import android.view.ViewParent;
+import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
import com.android.internal.logging.MetricsLogger;
@@ -79,6 +80,7 @@ import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupReposi
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.user.domain.interactor.UserSwitcherInteractor;
import com.android.systemui.util.kotlin.JavaAdapter;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import dagger.Lazy;
@@ -147,6 +149,7 @@ public class QuickSettingsControllerImplBaseTest extends SysuiTestCase {
@Mock protected UserSwitcherInteractor mUserSwitcherInteractor;
@Mock protected SelectedUserInteractor mSelectedUserInteractor;
@Mock protected LargeScreenHeaderHelper mLargeScreenHeaderHelper;
+ @Mock protected WindowManagerProvider mWindowManagerProvider;
protected FakeDisableFlagsRepository mDisableFlagsRepository =
mKosmos.getFakeDisableFlagsRepository();
@@ -232,6 +235,9 @@ public class QuickSettingsControllerImplBaseTest extends SysuiTestCase {
mMainHandler = new Handler(Looper.getMainLooper());
+ WindowManager wm = mContext.getSystemService(WindowManager.class);
+ when(mWindowManagerProvider.getWindowManager(mContext)).thenReturn(wm);
+
mQsController = new QuickSettingsControllerImpl(
mPanelViewControllerLazy,
mPanelView,
@@ -268,7 +274,8 @@ public class QuickSettingsControllerImplBaseTest extends SysuiTestCase {
mCastController,
splitShadeStateController,
() -> mKosmos.getCommunalTransitionViewModel(),
- () -> mLargeScreenHeaderHelper
+ () -> mLargeScreenHeaderHelper,
+ mWindowManagerProvider
);
mQsController.init();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt
index 32eec56af8ea..b376558371f3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt
@@ -21,6 +21,7 @@ import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.Flags
@@ -30,8 +31,10 @@ import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticati
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.domain.interactor.enableDualShade
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.testKosmos
@@ -177,6 +180,40 @@ class ShadeControllerSceneImplTest : SysuiTestCase() {
verify(testRunnable, times(1)).run()
}
+ @Test
+ fun instantCollapseShade_notificationShadeHidden() =
+ testScope.runTest {
+ val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+ // GIVEN the dual shade configuration with the notification shade overlay visible
+ kosmos.enableDualShade()
+ runCurrent()
+ sceneInteractor.showOverlay(Overlays.NotificationsShade, "test")
+ assertThat(currentOverlays).isEqualTo(setOf(Overlays.NotificationsShade))
+
+ // WHEN shade instantly collapses
+ underTest.instantCollapseShade()
+
+ // THEN overlay was hidden
+ assertThat(currentOverlays).isEmpty()
+ }
+
+ @Test
+ fun instantCollapseShade_qsShadeHidden() =
+ testScope.runTest {
+ val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+ // GIVEN the dual shade configuration with the QS shade overlay visible
+ kosmos.enableDualShade()
+ runCurrent()
+ sceneInteractor.showOverlay(Overlays.QuickSettingsShade, "test")
+ assertThat(currentOverlays).isEqualTo(setOf(Overlays.QuickSettingsShade))
+
+ // WHEN shade instantly collapses
+ underTest.instantCollapseShade()
+
+ // THEN overlay was hidden
+ assertThat(currentOverlays).isEmpty()
+ }
+
private fun setScene(key: SceneKey) {
sceneInteractor.changeScene(key, "test")
sceneInteractor.setTransitionState(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
index 6c498c8fd2f1..5ec9b601bcca 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
@@ -44,6 +44,7 @@ import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.core.StatusBarRootModernization
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentBuilder
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import com.android.systemui.statusbar.phone.ongoingcall.DisableChipsModernization
import com.android.systemui.statusbar.phone.ongoingcall.EnableChipsModernization
@@ -799,8 +800,8 @@ class CallChipViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
}
private val PROMOTED_CONTENT_WITH_COLOR =
- PromotedNotificationContentModel.Builder("notif")
- .apply {
+ PromotedNotificationContentBuilder("notif")
+ .applyToShared {
this.colors =
PromotedNotificationContentModel.Colors(
backgroundColor = PROMOTED_BACKGROUND_COLOR,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt
index 7f8f5f43e775..9ce43a0792c2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt
@@ -30,7 +30,7 @@ import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
-import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentBuilder
import com.android.systemui.testKosmos
import com.android.systemui.util.time.fakeSystemClock
import com.google.common.truth.Truth.assertThat
@@ -420,7 +420,7 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() {
// WHEN the notif gets a new UID that starts as visible
activityManagerRepository.fake.startingIsAppVisibleValue = true
val newPromotedContentBuilder =
- PromotedNotificationContentModel.Builder("notif").apply {
+ PromotedNotificationContentBuilder("notif").applyToShared {
this.shortCriticalText = "Arrived"
}
val newPromotedContent = newPromotedContentBuilder.build()
@@ -452,6 +452,6 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() {
companion object {
private const val UID = 885
- private val PROMOTED_CONTENT = PromotedNotificationContentModel.Builder("notif1").build()
+ private val PROMOTED_CONTENT = PromotedNotificationContentBuilder("notif1").build()
}
}
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 0b9b297130a2..233efbb97d4e 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
@@ -36,7 +36,7 @@ import com.android.systemui.statusbar.notification.data.repository.ActiveNotific
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.promoted.shared.model.PromotedNotificationContentBuilder
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
import com.android.systemui.statusbar.notification.shared.CallType
import com.android.systemui.testKosmos
@@ -56,16 +56,16 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
@Test
@DisableFlags(StatusBarNotifChips.FLAG_NAME)
- fun shownNotificationChips_flagOff_noNotifs() =
+ fun allNotificationChips_flagOff_noNotifs() =
kosmos.runTest {
- val latest by collectLastValue(underTest.shownNotificationChips)
+ val latest by collectLastValue(underTest.allNotificationChips)
setNotifs(
listOf(
activeNotificationModel(
key = "notif",
statusBarChipIcon = mock<StatusBarIconView>(),
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
)
@@ -75,9 +75,9 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
- fun shownNotificationChips_noNotifs_empty() =
+ fun allNotificationChips_noNotifs_empty() =
kosmos.runTest {
- val latest by collectLastValue(underTest.shownNotificationChips)
+ val latest by collectLastValue(underTest.allNotificationChips)
setNotifs(emptyList())
@@ -87,16 +87,16 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
@DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
- fun shownNotificationChips_notifMissingStatusBarChipIconView_cdFlagOff_empty() =
+ fun allNotificationChips_notifMissingStatusBarChipIconView_cdFlagOff_empty() =
kosmos.runTest {
- val latest by collectLastValue(underTest.shownNotificationChips)
+ val latest by collectLastValue(underTest.allNotificationChips)
setNotifs(
listOf(
activeNotificationModel(
key = "notif",
statusBarChipIcon = null,
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
)
@@ -106,16 +106,16 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME, StatusBarConnectedDisplays.FLAG_NAME)
- fun shownNotificationChips_notifMissingStatusBarChipIconView_cdFlagOn_notEmpty() =
+ fun allNotificationChips_notifMissingStatusBarChipIconView_cdFlagOn_notEmpty() =
kosmos.runTest {
- val latest by collectLastValue(underTest.shownNotificationChips)
+ val latest by collectLastValue(underTest.allNotificationChips)
setNotifs(
listOf(
activeNotificationModel(
key = "notif",
statusBarChipIcon = null,
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
)
@@ -125,9 +125,9 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
- fun shownNotificationChips_onePromotedNotif_statusBarIconViewMatches() =
+ fun allNotificationChips_onePromotedNotif_statusBarIconViewMatches() =
kosmos.runTest {
- val latest by collectLastValue(underTest.shownNotificationChips)
+ val latest by collectLastValue(underTest.allNotificationChips)
val icon = mock<StatusBarIconView>()
setNotifs(
@@ -135,7 +135,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
activeNotificationModel(
key = "notif",
statusBarChipIcon = icon,
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
)
@@ -147,9 +147,9 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
- fun shownNotificationChips_onlyForPromotedNotifs() =
+ fun allNotificationChips_onlyForPromotedNotifs() =
kosmos.runTest {
- val latest by collectLastValue(underTest.shownNotificationChips)
+ val latest by collectLastValue(underTest.allNotificationChips)
val firstIcon = mock<StatusBarIconView>()
val secondIcon = mock<StatusBarIconView>()
@@ -158,12 +158,12 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
activeNotificationModel(
key = "notif1",
statusBarChipIcon = firstIcon,
- promotedContent = PromotedNotificationContentModel.Builder("notif1").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif1").build(),
),
activeNotificationModel(
key = "notif2",
statusBarChipIcon = secondIcon,
- promotedContent = PromotedNotificationContentModel.Builder("notif2").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif2").build(),
),
activeNotificationModel(
key = "notif3",
@@ -182,11 +182,11 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
- fun shownNotificationChips_onlyForNotVisibleApps() =
+ fun allNotificationChips_appVisibilityInfoCorrect() =
kosmos.runTest {
activityManagerRepository.fake.startingIsAppVisibleValue = false
- val latest by collectLastValue(underTest.shownNotificationChips)
+ val latest by collectLastValue(underTest.allNotificationChips)
val uid = 433
setNotifs(
@@ -195,27 +195,30 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
key = "notif",
uid = uid,
statusBarChipIcon = mock<StatusBarIconView>(),
- promotedContent = PromotedNotificationContentModel.Builder("notif1").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
)
activityManagerRepository.fake.setIsAppVisible(uid, isAppVisible = false)
assertThat(latest).hasSize(1)
+ assertThat(latest!![0].isAppVisible).isFalse()
activityManagerRepository.fake.setIsAppVisible(uid, isAppVisible = true)
- assertThat(latest).isEmpty()
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0].isAppVisible).isTrue()
activityManagerRepository.fake.setIsAppVisible(uid, isAppVisible = false)
assertThat(latest).hasSize(1)
+ assertThat(latest!![0].isAppVisible).isFalse()
}
/** Regression test for b/388521980. */
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
- fun shownNotificationChips_callNotifIsAlsoPromoted_callNotifExcluded() =
+ fun allNotificationChips_callNotifIsAlsoPromoted_callNotifExcluded() =
kosmos.runTest {
- val latest by collectLastValue(underTest.shownNotificationChips)
+ val latest by collectLastValue(underTest.allNotificationChips)
setNotifs(
listOf(
@@ -223,14 +226,14 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
key = "promotedNormal",
statusBarChipIcon = mock(),
promotedContent =
- PromotedNotificationContentModel.Builder("promotedNormal").build(),
+ PromotedNotificationContentBuilder("promotedNormal").build(),
callType = CallType.None,
),
activeNotificationModel(
key = "promotedCall",
statusBarChipIcon = mock(),
promotedContent =
- PromotedNotificationContentModel.Builder("promotedCall").build(),
+ PromotedNotificationContentBuilder("promotedCall").build(),
callType = CallType.Ongoing,
),
)
@@ -243,9 +246,9 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
- fun shownNotificationChips_notifUpdatesGoThrough() =
+ fun allNotificationChips_notifUpdatesGoThrough() =
kosmos.runTest {
- val latest by collectLastValue(underTest.shownNotificationChips)
+ val latest by collectLastValue(underTest.allNotificationChips)
val firstIcon = mock<StatusBarIconView>()
val secondIcon = mock<StatusBarIconView>()
@@ -256,7 +259,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
activeNotificationModel(
key = "notif",
statusBarChipIcon = firstIcon,
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
)
@@ -269,7 +272,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
activeNotificationModel(
key = "notif",
statusBarChipIcon = secondIcon,
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
)
@@ -282,7 +285,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
activeNotificationModel(
key = "notif",
statusBarChipIcon = thirdIcon,
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
)
@@ -293,16 +296,16 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
- fun shownNotificationChips_promotedNotifDisappearsThenReappears() =
+ fun allNotificationChips_promotedNotifDisappearsThenReappears() =
kosmos.runTest {
- val latest by collectLastValue(underTest.shownNotificationChips)
+ val latest by collectLastValue(underTest.allNotificationChips)
setNotifs(
listOf(
activeNotificationModel(
key = "notif",
statusBarChipIcon = mock(),
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
)
@@ -325,7 +328,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
activeNotificationModel(
key = "notif",
statusBarChipIcon = mock(),
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
)
@@ -335,9 +338,9 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
- fun shownNotificationChips_sortedByFirstAppearanceTime() =
+ fun allNotificationChips_sortedByFirstAppearanceTime() =
kosmos.runTest {
- val latest by collectLastValue(underTest.shownNotificationChips)
+ val latest by collectLastValue(underTest.allNotificationChips)
val firstIcon = mock<StatusBarIconView>()
val secondIcon = mock<StatusBarIconView>()
@@ -348,7 +351,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
activeNotificationModel(
key = "notif1",
statusBarChipIcon = firstIcon,
- promotedContent = PromotedNotificationContentModel.Builder("notif1").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif1").build(),
)
setNotifs(listOf(notif1))
assertThat(latest!!.map { it.key }).containsExactly("notif1").inOrder()
@@ -359,7 +362,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
activeNotificationModel(
key = "notif2",
statusBarChipIcon = secondIcon,
- promotedContent = PromotedNotificationContentModel.Builder("notif2").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif2").build(),
)
setNotifs(listOf(notif1, notif2))
@@ -380,7 +383,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
// WHEN notif1 gets an update
val notif1NewPromotedContent =
- PromotedNotificationContentModel.Builder("notif1").apply {
+ PromotedNotificationContentBuilder("notif1").applyToShared {
this.shortCriticalText = "Arrived"
}
setNotifs(
@@ -411,9 +414,9 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
- fun shownNotificationChips_lastAppVisibleTimeMaintainedAcrossNotifAddsAndRemoves() =
+ fun allNotificationChips_lastAppVisibleTimeMaintainedAcrossNotifAddsAndRemoves() =
kosmos.runTest {
- val latest by collectLastValue(underTest.shownNotificationChips)
+ val latest by collectLastValue(underTest.allNotificationChips)
val notif1Info = NotifInfo("notif1", mock<StatusBarIconView>(), uid = 100)
val notif2Info = NotifInfo("notif2", mock<StatusBarIconView>(), uid = 200)
@@ -426,8 +429,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
key = notif1Info.key,
uid = notif1Info.uid,
statusBarChipIcon = notif1Info.icon,
- promotedContent =
- PromotedNotificationContentModel.Builder(notif1Info.key).build(),
+ promotedContent = PromotedNotificationContentBuilder(notif1Info.key).build(),
)
)
activityManagerRepository.fake.setIsAppVisible(notif1Info.uid, isAppVisible = false)
@@ -443,8 +445,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
key = notif2Info.key,
uid = notif2Info.uid,
statusBarChipIcon = notif2Info.icon,
- promotedContent =
- PromotedNotificationContentModel.Builder(notif2Info.key).build(),
+ promotedContent = PromotedNotificationContentBuilder(notif2Info.key).build(),
)
)
activityManagerRepository.fake.setIsAppVisible(notif2Info.uid, isAppVisible = false)
@@ -468,9 +469,9 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
- fun shownNotificationChips_sortedByLastAppVisibleTime() =
+ fun allNotificationChips_sortedByLastAppVisibleTime() =
kosmos.runTest {
- val latest by collectLastValue(underTest.shownNotificationChips)
+ val latest by collectLastValue(underTest.allNotificationChips)
val notif1Info = NotifInfo("notif1", mock<StatusBarIconView>(), uid = 100)
val notif2Info = NotifInfo("notif2", mock<StatusBarIconView>(), uid = 200)
@@ -482,16 +483,14 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
key = notif1Info.key,
uid = notif1Info.uid,
statusBarChipIcon = notif1Info.icon,
- promotedContent =
- PromotedNotificationContentModel.Builder(notif1Info.key).build(),
+ promotedContent = PromotedNotificationContentBuilder(notif1Info.key).build(),
)
val notif2 =
activeNotificationModel(
key = notif2Info.key,
uid = notif2Info.uid,
statusBarChipIcon = notif2Info.icon,
- promotedContent =
- PromotedNotificationContentModel.Builder(notif2Info.key).build(),
+ promotedContent = PromotedNotificationContentBuilder(notif2Info.key).build(),
)
setNotifs(listOf(notif1, notif2))
assertThat(latest!!.map { it.key }).containsExactly("notif1", "notif2").inOrder()
@@ -500,15 +499,19 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
fakeSystemClock.advanceTime(1000)
activityManagerRepository.fake.setIsAppVisible(notif2Info.uid, isAppVisible = true)
- // THEN notif2 is no longer shown
- assertThat(latest!!.map { it.key }).containsExactly("notif1").inOrder()
+ // THEN notif2 is ranked above notif1 because it was more recently visible
+ assertThat(latest!!.map { it.key }).containsExactly("notif2", "notif1").inOrder()
+ assertThat(latest!![0].isAppVisible).isTrue() // notif2
+ assertThat(latest!![1].isAppVisible).isFalse() // notif1
// WHEN notif2's app is no longer visible
fakeSystemClock.advanceTime(1000)
activityManagerRepository.fake.setIsAppVisible(notif2Info.uid, isAppVisible = false)
- // THEN notif2 is ranked above notif1 because it was more recently visible
+ // THEN notif2 is still ranked above notif1
assertThat(latest!!.map { it.key }).containsExactly("notif2", "notif1").inOrder()
+ assertThat(latest!![0].isAppVisible).isFalse() // notif2
+ assertThat(latest!![1].isAppVisible).isFalse() // notif1
// WHEN the app associated with notif1 becomes visible then un-visible
fakeSystemClock.advanceTime(1000)
@@ -522,9 +525,9 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
- fun shownNotificationChips_newNotificationTakesPriorityOverLastAppVisible() =
+ fun allNotificationChips_newNotificationTakesPriorityOverLastAppVisible() =
kosmos.runTest {
- val latest by collectLastValue(underTest.shownNotificationChips)
+ val latest by collectLastValue(underTest.allNotificationChips)
val notif1Info = NotifInfo("notif1", mock<StatusBarIconView>(), uid = 100)
val notif2Info = NotifInfo("notif2", mock<StatusBarIconView>(), uid = 200)
@@ -537,16 +540,14 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
key = notif1Info.key,
uid = notif1Info.uid,
statusBarChipIcon = notif1Info.icon,
- promotedContent =
- PromotedNotificationContentModel.Builder(notif1Info.key).build(),
+ promotedContent = PromotedNotificationContentBuilder(notif1Info.key).build(),
)
val notif2 =
activeNotificationModel(
key = notif2Info.key,
uid = notif2Info.uid,
statusBarChipIcon = notif2Info.icon,
- promotedContent =
- PromotedNotificationContentModel.Builder(notif2Info.key).build(),
+ promotedContent = PromotedNotificationContentBuilder(notif2Info.key).build(),
)
setNotifs(listOf(notif1, notif2))
assertThat(latest!!.map { it.key }).containsExactly("notif1", "notif2").inOrder()
@@ -567,8 +568,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
key = notif3Info.key,
uid = notif3Info.uid,
statusBarChipIcon = notif3Info.icon,
- promotedContent =
- PromotedNotificationContentModel.Builder(notif3Info.key).build(),
+ promotedContent = PromotedNotificationContentBuilder(notif3Info.key).build(),
)
setNotifs(listOf(notif1, notif2, notif3))
@@ -581,9 +581,9 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
- fun shownNotificationChips_fullSort() =
+ fun allNotificationChips_fullSort() =
kosmos.runTest {
- val latest by collectLastValue(underTest.shownNotificationChips)
+ val latest by collectLastValue(underTest.allNotificationChips)
val notif1Info = NotifInfo("notif1", mock<StatusBarIconView>(), uid = 100)
val notif2Info = NotifInfo("notif2", mock<StatusBarIconView>(), uid = 200)
@@ -597,8 +597,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
key = notif1Info.key,
uid = notif1Info.uid,
statusBarChipIcon = notif1Info.icon,
- promotedContent =
- PromotedNotificationContentModel.Builder(notif1Info.key).build(),
+ promotedContent = PromotedNotificationContentBuilder(notif1Info.key).build(),
)
setNotifs(listOf(notif1))
@@ -609,8 +608,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
key = notif2Info.key,
uid = notif2Info.uid,
statusBarChipIcon = notif2Info.icon,
- promotedContent =
- PromotedNotificationContentModel.Builder(notif2Info.key).build(),
+ promotedContent = PromotedNotificationContentBuilder(notif2Info.key).build(),
)
setNotifs(listOf(notif1, notif2))
@@ -637,7 +635,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
// WHEN notif2 gets an update
val notif2NewPromotedContent =
- PromotedNotificationContentModel.Builder("notif2").apply {
+ PromotedNotificationContentBuilder("notif2").applyToShared {
this.shortCriticalText = "Arrived"
}
setNotifs(
@@ -662,8 +660,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
key = notif3Info.key,
uid = notif3Info.uid,
statusBarChipIcon = notif3Info.icon,
- promotedContent =
- PromotedNotificationContentModel.Builder(notif3Info.key).build(),
+ promotedContent = PromotedNotificationContentBuilder(notif3Info.key).build(),
)
setNotifs(listOf(notif1, notif2, notif3))
@@ -699,9 +696,9 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
- fun shownNotificationChips_notifChangesKey() =
+ fun allNotificationChips_notifChangesKey() =
kosmos.runTest {
- val latest by collectLastValue(underTest.shownNotificationChips)
+ val latest by collectLastValue(underTest.allNotificationChips)
val firstIcon = mock<StatusBarIconView>()
val secondIcon = mock<StatusBarIconView>()
@@ -710,8 +707,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
activeNotificationModel(
key = "notif|uid1",
statusBarChipIcon = firstIcon,
- promotedContent =
- PromotedNotificationContentModel.Builder("notif|uid1").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif|uid1").build(),
)
)
)
@@ -725,8 +721,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
activeNotificationModel(
key = "notif|uid2",
statusBarChipIcon = secondIcon,
- promotedContent =
- PromotedNotificationContentModel.Builder("notif|uid2").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif|uid2").build(),
)
)
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
index 8368fa671ea8..660f0b52f464 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
@@ -24,6 +24,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags.FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY
import com.android.systemui.SysuiTestCase
+import com.android.systemui.activity.data.repository.activityManagerRepository
+import com.android.systemui.activity.data.repository.fake
import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
@@ -43,6 +45,7 @@ import com.android.systemui.statusbar.notification.data.repository.ActiveNotific
import com.android.systemui.statusbar.notification.data.repository.UnconfinedFakeHeadsUpRowRepository
import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
import com.android.systemui.statusbar.notification.headsup.PinnedStatus
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentBuilder
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.When
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
@@ -101,7 +104,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "notif",
statusBarChipIcon = null,
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
)
@@ -121,7 +124,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "notif",
statusBarChipIcon = null,
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
)
@@ -142,7 +145,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
key = "notif",
appName = "Fake App Name",
statusBarChipIcon = icon,
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
)
@@ -172,7 +175,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
key = notifKey,
appName = "Fake App Name",
statusBarChipIcon = null,
- promotedContent = PromotedNotificationContentModel.Builder(notifKey).build(),
+ promotedContent = PromotedNotificationContentBuilder(notifKey).build(),
)
)
)
@@ -195,7 +198,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
val latest by collectLastValue(underTest.chips)
val promotedContentBuilder =
- PromotedNotificationContentModel.Builder("notif").apply {
+ PromotedNotificationContentBuilder("notif").applyToShared {
this.colors =
PromotedNotificationContentModel.Colors(
backgroundColor = 56,
@@ -229,12 +232,12 @@ class NotifChipsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "notif1",
statusBarChipIcon = firstIcon,
- promotedContent = PromotedNotificationContentModel.Builder("notif1").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif1").build(),
),
activeNotificationModel(
key = "notif2",
statusBarChipIcon = secondIcon,
- promotedContent = PromotedNotificationContentModel.Builder("notif2").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif2").build(),
),
activeNotificationModel(
key = "notif3",
@@ -264,13 +267,12 @@ class NotifChipsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = firstKey,
statusBarChipIcon = null,
- promotedContent = PromotedNotificationContentModel.Builder(firstKey).build(),
+ promotedContent = PromotedNotificationContentBuilder(firstKey).build(),
),
activeNotificationModel(
key = secondKey,
statusBarChipIcon = null,
- promotedContent =
- PromotedNotificationContentModel.Builder(secondKey).build(),
+ promotedContent = PromotedNotificationContentBuilder(secondKey).build(),
),
activeNotificationModel(
key = thirdKey,
@@ -286,6 +288,84 @@ class NotifChipsViewModelTest : SysuiTestCase() {
}
@Test
+ fun chips_appStartsAsVisible_isHiddenTrue() =
+ kosmos.runTest {
+ activityManagerRepository.fake.startingIsAppVisibleValue = true
+
+ val latest by collectLastValue(underTest.chips)
+
+ val uid = 433
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ uid = uid,
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
+ )
+ )
+ )
+
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0].isHidden).isTrue()
+ }
+
+ @Test
+ fun chips_appStartsAsNotVisible_isHiddenFalse() =
+ kosmos.runTest {
+ activityManagerRepository.fake.startingIsAppVisibleValue = false
+
+ val latest by collectLastValue(underTest.chips)
+
+ val uid = 433
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ uid = uid,
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
+ )
+ )
+ )
+
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0].isHidden).isFalse()
+ }
+
+ @Test
+ fun chips_isHidden_changesBasedOnAppVisibility() =
+ kosmos.runTest {
+ activityManagerRepository.fake.startingIsAppVisibleValue = false
+
+ val latest by collectLastValue(underTest.chips)
+
+ val uid = 433
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ uid = uid,
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
+ )
+ )
+ )
+
+ activityManagerRepository.fake.setIsAppVisible(uid, isAppVisible = false)
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0].isHidden).isFalse()
+
+ activityManagerRepository.fake.setIsAppVisible(uid, isAppVisible = true)
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0].isHidden).isTrue()
+
+ activityManagerRepository.fake.setIsAppVisible(uid, isAppVisible = false)
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0].isHidden).isFalse()
+ }
+
+ @Test
@DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_hasShortCriticalText_usesTextInsteadOfTime() =
kosmos.runTest {
@@ -294,7 +374,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
fakeSystemClock.setCurrentTimeMillis(currentTime)
val promotedContentBuilder =
- PromotedNotificationContentModel.Builder("notif").apply {
+ PromotedNotificationContentBuilder("notif").applyToShared {
this.shortCriticalText = "Arrived"
this.time = When.Time(currentTime + 30.minutes.inWholeMilliseconds)
}
@@ -321,7 +401,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
val latest by collectLastValue(underTest.chips)
val promotedContentBuilder =
- PromotedNotificationContentModel.Builder("notif").apply { this.time = null }
+ PromotedNotificationContentBuilder("notif").applyToShared { this.time = null }
setNotifs(
listOf(
activeNotificationModel(
@@ -346,7 +426,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
fakeSystemClock.setCurrentTimeMillis(currentTime)
val promotedContentBuilder =
- PromotedNotificationContentModel.Builder("notif").apply {
+ PromotedNotificationContentBuilder("notif").applyToShared {
this.wasPromotedAutomatically = true
this.time = When.Time(currentTime + 30.minutes.inWholeMilliseconds)
}
@@ -374,7 +454,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
fakeSystemClock.setCurrentTimeMillis(currentTime)
val promotedContentBuilder =
- PromotedNotificationContentModel.Builder("notif").apply {
+ PromotedNotificationContentBuilder("notif").applyToShared {
this.wasPromotedAutomatically = false
this.time = When.Time(currentTime + 30.minutes.inWholeMilliseconds)
}
@@ -402,7 +482,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
fakeSystemClock.setCurrentTimeMillis(currentTime)
val promotedContentBuilder =
- PromotedNotificationContentModel.Builder("notif").apply {
+ PromotedNotificationContentBuilder("notif").applyToShared {
this.time = When.Time(currentTime + 13.minutes.inWholeMilliseconds)
}
@@ -422,6 +502,42 @@ class NotifChipsViewModelTest : SysuiTestCase() {
}
@Test
+ fun chips_basicTime_respectsIsAppVisible() =
+ kosmos.runTest {
+ activityManagerRepository.fake.startingIsAppVisibleValue = false
+
+ val latest by collectLastValue(underTest.chips)
+ val currentTime = 3.minutes.inWholeMilliseconds
+ fakeSystemClock.setCurrentTimeMillis(currentTime)
+
+ val promotedContentBuilder =
+ PromotedNotificationContentBuilder("notif").applyToShared {
+ this.time = When.Time(currentTime + 13.minutes.inWholeMilliseconds)
+ }
+ val uid = 3
+
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ uid = 3,
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent = promotedContentBuilder.build(),
+ )
+ )
+ )
+
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0])
+ .isInstanceOf(OngoingActivityChipModel.Active.ShortTimeDelta::class.java)
+ assertThat(latest!![0].isHidden).isFalse()
+
+ activityManagerRepository.fake.setIsAppVisible(uid = uid, isAppVisible = true)
+
+ assertThat(latest!![0].isHidden).isTrue()
+ }
+
+ @Test
@DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_basicTime_timeLessThanOneMinInFuture_isIconOnly() =
kosmos.runTest {
@@ -430,7 +546,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
fakeSystemClock.setCurrentTimeMillis(currentTime)
val promotedContentBuilder =
- PromotedNotificationContentModel.Builder("notif").apply {
+ PromotedNotificationContentBuilder("notif").applyToShared {
this.time = When.Time(currentTime + 500)
}
@@ -458,7 +574,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
fakeSystemClock.setCurrentTimeMillis(currentTime)
val promotedContentBuilder =
- PromotedNotificationContentModel.Builder("notif").apply {
+ PromotedNotificationContentBuilder("notif").applyToShared {
this.time = When.Time(currentTime)
}
@@ -486,7 +602,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
fakeSystemClock.setCurrentTimeMillis(currentTime)
val promotedContentBuilder =
- PromotedNotificationContentModel.Builder("notif").apply {
+ PromotedNotificationContentBuilder("notif").applyToShared {
this.time = When.Time(currentTime - 2.minutes.inWholeMilliseconds)
}
@@ -515,7 +631,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
fakeSystemClock.setCurrentTimeMillis(currentTime)
val promotedContentBuilder =
- PromotedNotificationContentModel.Builder("notif").apply {
+ PromotedNotificationContentBuilder("notif").applyToShared {
this.time = When.Time(currentTime + 3.minutes.inWholeMilliseconds)
}
@@ -555,7 +671,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
val whenElapsed = currentElapsed - 1.minutes.inWholeMilliseconds
val promotedContentBuilder =
- PromotedNotificationContentModel.Builder("notif").apply {
+ PromotedNotificationContentBuilder("notif").applyToShared {
this.time =
When.Chronometer(elapsedRealtimeMillis = whenElapsed, isCountDown = false)
}
@@ -579,6 +695,48 @@ class NotifChipsViewModelTest : SysuiTestCase() {
@Test
@DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
+ fun chips_countUpTime_respectsIsAppVisible() =
+ kosmos.runTest {
+ activityManagerRepository.fake.startingIsAppVisibleValue = true
+
+ val latest by collectLastValue(underTest.chips)
+ val currentTime = 30.minutes.inWholeMilliseconds
+ fakeSystemClock.setCurrentTimeMillis(currentTime)
+
+ val currentElapsed =
+ currentTime + fakeSystemClock.elapsedRealtime() -
+ fakeSystemClock.currentTimeMillis()
+
+ val whenElapsed = currentElapsed - 1.minutes.inWholeMilliseconds
+
+ val promotedContentBuilder =
+ PromotedNotificationContentBuilder("notif").applyToShared {
+ this.time =
+ When.Chronometer(elapsedRealtimeMillis = whenElapsed, isCountDown = false)
+ }
+ val uid = 6
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ uid = uid,
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent = promotedContentBuilder.build(),
+ )
+ )
+ )
+
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0]).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java)
+ assertThat(latest!![0].isHidden).isTrue()
+
+ activityManagerRepository.fake.setIsAppVisible(uid, isAppVisible = false)
+
+ assertThat(latest!![0].isHidden).isFalse()
+ }
+
+ @Test
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_countDownTime_isTimer() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
@@ -592,7 +750,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
val whenElapsed = currentElapsed + 10.minutes.inWholeMilliseconds
val promotedContentBuilder =
- PromotedNotificationContentModel.Builder("notif").apply {
+ PromotedNotificationContentBuilder("notif").applyToShared {
this.time =
When.Chronometer(elapsedRealtimeMillis = whenElapsed, isCountDown = true)
}
@@ -623,7 +781,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
fakeSystemClock.setCurrentTimeMillis(currentTime)
val promotedContentBuilder =
- PromotedNotificationContentModel.Builder("notif").apply {
+ PromotedNotificationContentBuilder("notif").applyToShared {
this.time = When.Time(currentTime + 10.minutes.inWholeMilliseconds)
}
setNotifs(
@@ -653,7 +811,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
fakeSystemClock.setCurrentTimeMillis(currentTime)
val promotedContentBuilder =
- PromotedNotificationContentModel.Builder("notif").apply {
+ PromotedNotificationContentBuilder("notif").applyToShared {
this.time = When.Time(currentTime + 10.minutes.inWholeMilliseconds)
}
setNotifs(
@@ -690,11 +848,11 @@ class NotifChipsViewModelTest : SysuiTestCase() {
fakeSystemClock.setCurrentTimeMillis(currentTime)
val promotedContentBuilder =
- PromotedNotificationContentModel.Builder("notif").apply {
+ PromotedNotificationContentBuilder("notif").applyToShared {
this.time = When.Time(currentTime + 10.minutes.inWholeMilliseconds)
}
val otherPromotedContentBuilder =
- PromotedNotificationContentModel.Builder("other notif").apply {
+ PromotedNotificationContentBuilder("other notif").applyToShared {
this.time = When.Time(currentTime + 10.minutes.inWholeMilliseconds)
}
val icon = createStatusBarIconViewOrNull()
@@ -738,7 +896,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
fakeSystemClock.setCurrentTimeMillis(currentTime)
val promotedContentBuilder =
- PromotedNotificationContentModel.Builder("notif").apply {
+ PromotedNotificationContentBuilder("notif").applyToShared {
this.time = When.Time(currentTime + 10.minutes.inWholeMilliseconds)
}
setNotifs(
@@ -781,7 +939,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key,
statusBarChipIcon = createStatusBarIconViewOrNull(),
- promotedContent = PromotedNotificationContentModel.Builder(key).build(),
+ promotedContent = PromotedNotificationContentBuilder(key).build(),
)
)
)
@@ -809,7 +967,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key,
statusBarChipIcon = createStatusBarIconViewOrNull(),
- promotedContent = PromotedNotificationContentModel.Builder(key).build(),
+ promotedContent = PromotedNotificationContentBuilder(key).build(),
)
)
)
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 608a84bdb604..83b3c9cae3c1 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
@@ -63,7 +63,7 @@ import com.android.systemui.statusbar.notification.data.repository.activeNotific
import com.android.systemui.statusbar.notification.data.repository.addNotif
import com.android.systemui.statusbar.notification.data.repository.addNotifs
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.promoted.shared.model.PromotedNotificationContentBuilder
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.phone.ongoingcall.DisableChipsModernization
@@ -358,7 +358,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
addOngoingCallState(key = "call")
val promotedContentBuilder =
- PromotedNotificationContentModel.Builder("notif").apply {
+ PromotedNotificationContentBuilder("notif").applyToShared {
this.shortCriticalText = "Some text here"
}
activeNotificationListRepository.addNotif(
@@ -741,7 +741,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "notif",
statusBarChipIcon = icon,
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
)
@@ -765,7 +765,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "notif",
statusBarChipIcon = icon,
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
)
@@ -791,14 +791,12 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "firstNotif",
statusBarChipIcon = firstIcon,
- promotedContent =
- PromotedNotificationContentModel.Builder("firstNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("firstNotif").build(),
),
activeNotificationModel(
key = "secondNotif",
statusBarChipIcon = secondIcon,
- promotedContent =
- PromotedNotificationContentModel.Builder("secondNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("secondNotif").build(),
),
)
)
@@ -822,14 +820,12 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "firstNotif",
statusBarChipIcon = firstIcon,
- promotedContent =
- PromotedNotificationContentModel.Builder("firstNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("firstNotif").build(),
),
activeNotificationModel(
key = "secondNotif",
statusBarChipIcon = secondIcon,
- promotedContent =
- PromotedNotificationContentModel.Builder("secondNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("secondNotif").build(),
),
)
)
@@ -857,20 +853,17 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "firstNotif",
statusBarChipIcon = firstIcon,
- promotedContent =
- PromotedNotificationContentModel.Builder("firstNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("firstNotif").build(),
),
activeNotificationModel(
key = "secondNotif",
statusBarChipIcon = secondIcon,
- promotedContent =
- PromotedNotificationContentModel.Builder("secondNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("secondNotif").build(),
),
activeNotificationModel(
key = "thirdNotif",
statusBarChipIcon = thirdIcon,
- promotedContent =
- PromotedNotificationContentModel.Builder("thirdNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("thirdNotif").build(),
),
)
)
@@ -896,26 +889,22 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "firstNotif",
statusBarChipIcon = firstIcon,
- promotedContent =
- PromotedNotificationContentModel.Builder("firstNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("firstNotif").build(),
),
activeNotificationModel(
key = "secondNotif",
statusBarChipIcon = secondIcon,
- promotedContent =
- PromotedNotificationContentModel.Builder("secondNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("secondNotif").build(),
),
activeNotificationModel(
key = "thirdNotif",
statusBarChipIcon = thirdIcon,
- promotedContent =
- PromotedNotificationContentModel.Builder("thirdNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("thirdNotif").build(),
),
activeNotificationModel(
key = "fourthNotif",
statusBarChipIcon = fourthIcon,
- promotedContent =
- PromotedNotificationContentModel.Builder("fourthNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("fourthNotif").build(),
),
)
)
@@ -941,20 +930,17 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "firstNotif",
statusBarChipIcon = createStatusBarIconViewOrNull(),
- promotedContent =
- PromotedNotificationContentModel.Builder("firstNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("firstNotif").build(),
),
activeNotificationModel(
key = "secondNotif",
statusBarChipIcon = createStatusBarIconViewOrNull(),
- promotedContent =
- PromotedNotificationContentModel.Builder("secondNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("secondNotif").build(),
),
activeNotificationModel(
key = "thirdNotif",
statusBarChipIcon = createStatusBarIconViewOrNull(),
- promotedContent =
- PromotedNotificationContentModel.Builder("thirdNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("thirdNotif").build(),
),
)
)
@@ -973,26 +959,22 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "firstNotif",
statusBarChipIcon = createStatusBarIconViewOrNull(),
- promotedContent =
- PromotedNotificationContentModel.Builder("firstNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("firstNotif").build(),
),
activeNotificationModel(
key = "secondNotif",
statusBarChipIcon = createStatusBarIconViewOrNull(),
- promotedContent =
- PromotedNotificationContentModel.Builder("secondNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("secondNotif").build(),
),
activeNotificationModel(
key = "thirdNotif",
statusBarChipIcon = createStatusBarIconViewOrNull(),
- promotedContent =
- PromotedNotificationContentModel.Builder("thirdNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("thirdNotif").build(),
),
activeNotificationModel(
key = "fourthNotif",
statusBarChipIcon = createStatusBarIconViewOrNull(),
- promotedContent =
- PromotedNotificationContentModel.Builder("fourthNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("fourthNotif").build(),
),
)
)
@@ -1016,14 +998,12 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "firstNotif",
statusBarChipIcon = firstIcon,
- promotedContent =
- PromotedNotificationContentModel.Builder("firstNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("firstNotif").build(),
),
activeNotificationModel(
key = "secondNotif",
statusBarChipIcon = createStatusBarIconViewOrNull(),
- promotedContent =
- PromotedNotificationContentModel.Builder("secondNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("secondNotif").build(),
),
)
)
@@ -1050,20 +1030,17 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "firstNotif",
statusBarChipIcon = firstIcon,
- promotedContent =
- PromotedNotificationContentModel.Builder("firstNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("firstNotif").build(),
),
activeNotificationModel(
key = "secondNotif",
statusBarChipIcon = secondIcon,
- promotedContent =
- PromotedNotificationContentModel.Builder("secondNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("secondNotif").build(),
),
activeNotificationModel(
key = "thirdNotif",
statusBarChipIcon = thirdIcon,
- promotedContent =
- PromotedNotificationContentModel.Builder("thirdNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("thirdNotif").build(),
),
)
)
@@ -1092,7 +1069,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "notif",
statusBarChipIcon = createStatusBarIconViewOrNull(),
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
@@ -1114,14 +1091,14 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "notif1",
statusBarChipIcon = createStatusBarIconViewOrNull(),
- promotedContent = PromotedNotificationContentModel.Builder("notif1").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif1").build(),
)
)
activeNotificationListRepository.addNotif(
activeNotificationModel(
key = "notif2",
statusBarChipIcon = createStatusBarIconViewOrNull(),
- promotedContent = PromotedNotificationContentModel.Builder("notif2").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif2").build(),
)
)
@@ -1143,14 +1120,14 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "notif1",
statusBarChipIcon = createStatusBarIconViewOrNull(),
- promotedContent = PromotedNotificationContentModel.Builder("notif1").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif1").build(),
)
)
activeNotificationListRepository.addNotif(
activeNotificationModel(
key = "notif2",
statusBarChipIcon = createStatusBarIconViewOrNull(),
- promotedContent = PromotedNotificationContentModel.Builder("notif2").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif2").build(),
)
)
@@ -1178,7 +1155,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "notif",
statusBarChipIcon = notifIcon,
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
addOngoingCallState(key = callNotificationKey)
@@ -1189,7 +1166,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "notif2",
statusBarChipIcon = notifIcon2,
- promotedContent = PromotedNotificationContentModel.Builder("notif2").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif2").build(),
)
)
@@ -1214,7 +1191,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "notif",
statusBarChipIcon = notifIcon,
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
)
@@ -1265,7 +1242,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "notif",
statusBarChipIcon = notifIcon,
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
@@ -1304,7 +1281,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "notif",
statusBarChipIcon = notifIcon,
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
// And everything else hidden
@@ -1382,7 +1359,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "notif1",
statusBarChipIcon = notif1Icon,
- promotedContent = PromotedNotificationContentModel.Builder("notif1").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif1").build(),
)
)
)
@@ -1436,7 +1413,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "notif2",
statusBarChipIcon = notif2Icon,
- promotedContent = PromotedNotificationContentModel.Builder("notif2").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif2").build(),
)
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt
index d3befa921e9e..29bb29f25797 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt
@@ -29,7 +29,7 @@ import com.android.systemui.statusbar.notification.data.model.activeNotification
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.setActiveNotifs
-import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentBuilder
import com.android.systemui.statusbar.notification.shared.CallType
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -170,7 +170,7 @@ class ActiveNotificationsInteractorTest : SysuiTestCase() {
val promoted1 =
activeNotificationModel(
key = "notif1",
- promotedContent = PromotedNotificationContentModel.Builder("notif1").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif1").build(),
)
val notPromoted2 = activeNotificationModel(key = "notif2", promotedContent = null)
@@ -208,14 +208,14 @@ class ActiveNotificationsInteractorTest : SysuiTestCase() {
val promoted1 =
activeNotificationModel(
key = "notif1",
- promotedContent = PromotedNotificationContentModel.Builder("notif1").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif1").build(),
)
val notPromoted2 = activeNotificationModel(key = "notif2", promotedContent = null)
val notPromoted3 = activeNotificationModel(key = "notif3", promotedContent = null)
val promoted4 =
activeNotificationModel(
key = "notif4",
- promotedContent = PromotedNotificationContentModel.Builder("notif4").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif4").build(),
)
activeNotificationListRepository.activeNotifications.value =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
index 35b19c19d5ce..873357c2201e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
@@ -21,20 +21,21 @@ import android.service.notification.StatusBarNotification
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
import com.android.systemui.statusbar.RankingBuilder
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi
-import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentBuilder
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels
import com.android.systemui.statusbar.notification.shared.byKey
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -42,17 +43,14 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class RenderNotificationsListInteractorTest : SysuiTestCase() {
private val kosmos = testKosmos()
- private val testScope = kosmos.testScope
- private val notifsRepository = kosmos.activeNotificationListRepository
- private val notifsInteractor = kosmos.activeNotificationsInteractor
- private val underTest =
- RenderNotificationListInteractor(notifsRepository, sectionStyleProvider = mock(), context)
+ private val Kosmos.outputInteractor by Kosmos.Fixture { activeNotificationsInteractor }
+ private val Kosmos.underTest by Kosmos.Fixture { renderNotificationListInteractor }
@Test
fun setRenderedList_preservesOrdering() =
- testScope.runTest {
- val notifs by collectLastValue(notifsInteractor.topLevelRepresentativeNotifications)
+ kosmos.runTest {
+ val notifs by collectLastValue(outputInteractor.topLevelRepresentativeNotifications)
val keys = (1..50).shuffled().map { "$it" }
val entries = keys.map { mockNotificationEntry(key = it) }
underTest.setRenderedList(entries)
@@ -64,8 +62,8 @@ class RenderNotificationsListInteractorTest : SysuiTestCase() {
@Test
fun setRenderList_flatMapsRankings() =
- testScope.runTest {
- val ranks by collectLastValue(notifsInteractor.activeNotificationRanks)
+ kosmos.runTest {
+ val ranks by collectLastValue(outputInteractor.activeNotificationRanks)
val single = mockNotificationEntry("single", 0)
val group =
@@ -89,8 +87,8 @@ class RenderNotificationsListInteractorTest : SysuiTestCase() {
@Test
fun setRenderList_singleItems_mapsRankings() =
- testScope.runTest {
- val actual by collectLastValue(notifsInteractor.activeNotificationRanks)
+ kosmos.runTest {
+ val actual by collectLastValue(outputInteractor.activeNotificationRanks)
val expected =
(0..10).shuffled().mapIndexed { index, value -> "$value" to index }.toMap()
@@ -103,8 +101,8 @@ class RenderNotificationsListInteractorTest : SysuiTestCase() {
@Test
fun setRenderList_groupWithNoSummary_flatMapsRankings() =
- testScope.runTest {
- val actual by collectLastValue(notifsInteractor.activeNotificationRanks)
+ kosmos.runTest {
+ val actual by collectLastValue(outputInteractor.activeNotificationRanks)
val expected =
(0..10).shuffled().mapIndexed { index, value -> "$value" to index }.toMap()
@@ -123,14 +121,14 @@ class RenderNotificationsListInteractorTest : SysuiTestCase() {
@Test
@EnableFlags(PromotedNotificationUi.FLAG_NAME)
fun setRenderList_setsPromotionContent() =
- testScope.runTest {
- val actual by collectLastValue(notifsInteractor.topLevelRepresentativeNotifications)
+ kosmos.runTest {
+ val actual by collectLastValue(outputInteractor.topLevelRepresentativeNotifications)
val notPromoted1 = mockNotificationEntry("key1", promotedContent = null)
val promoted2 =
mockNotificationEntry(
"key2",
- promotedContent = PromotedNotificationContentModel.Builder("key2").build(),
+ promotedContent = PromotedNotificationContentBuilder("key2").build(),
)
underTest.setRenderedList(listOf(notPromoted1, promoted2))
@@ -149,7 +147,7 @@ class RenderNotificationsListInteractorTest : SysuiTestCase() {
private fun mockNotificationEntry(
key: String,
rank: Int = 0,
- promotedContent: PromotedNotificationContentModel? = null,
+ promotedContent: PromotedNotificationContentModels? = null,
): NotificationEntry {
val nBuilder = Notification.Builder(context, "a")
val notification = nBuilder.build()
@@ -165,7 +163,7 @@ class RenderNotificationsListInteractorTest : SysuiTestCase() {
whenever(this.representativeEntry).thenReturn(this)
whenever(this.ranking).thenReturn(RankingBuilder().setRank(rank).build())
whenever(this.sbn).thenReturn(mockSbn)
- whenever(this.promotedNotificationContentModel).thenReturn(promotedContent)
+ whenever(this.promotedNotificationContentModels).thenReturn(promotedContent)
}
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
index ee698ae20adb..41120a16e4ea 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
@@ -33,8 +33,10 @@ import com.android.systemui.statusbar.notification.data.repository.notifications
import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
import com.android.systemui.statusbar.notification.domain.interactor.headsUpNotificationIconInteractor
import com.android.systemui.statusbar.notification.promoted.domain.interactor.aodPromotedNotificationInteractor
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentBuilder
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.Style.Base
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels
import com.android.systemui.statusbar.notification.shared.byIsAmbient
import com.android.systemui.statusbar.notification.shared.byIsLastMessageFromReply
import com.android.systemui.statusbar.notification.shared.byIsPromoted
@@ -354,6 +356,6 @@ private val testIcons =
private fun promotedContent(
key: String,
style: PromotedNotificationContentModel.Style,
-): PromotedNotificationContentModel {
- return PromotedNotificationContentModel.Builder(key).apply { this.style = style }.build()
+): PromotedNotificationContentModels {
+ return PromotedNotificationContentBuilder(key).applyToShared { this.style = style }.build()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt
index 0ac944a43de6..cc016b9768b7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt
@@ -33,18 +33,21 @@ import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_PUBLIC
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
import com.android.systemui.statusbar.notification.promoted.AutomaticPromotionCoordinator.Companion.EXTRA_WAS_AUTOMATICALLY_PROMOTED
-import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.Style
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.When
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels
import com.android.systemui.statusbar.notification.row.RowImageInflater
import com.android.systemui.testKosmos
import com.android.systemui.util.time.fakeSystemClock
import com.android.systemui.util.time.systemClock
import com.google.common.truth.Truth.assertThat
+import kotlin.test.assertNotNull
import kotlin.time.Duration
import kotlin.time.Duration.Companion.minutes
import org.junit.Test
@@ -112,12 +115,43 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
setContentText(TEST_CONTENT_TEXT)
}
- val content = extractContent(entry)
+ val content = requireContent(entry)
- assertThat(content).isNotNull()
- assertThat(content?.subText).isEqualTo(TEST_SUB_TEXT)
- assertThat(content?.title).isEqualTo(TEST_CONTENT_TITLE)
- assertThat(content?.text).isEqualTo(TEST_CONTENT_TEXT)
+ content.privateVersion.apply {
+ assertThat(subText).isEqualTo(TEST_SUB_TEXT)
+ assertThat(title).isEqualTo(TEST_CONTENT_TITLE)
+ assertThat(text).isEqualTo(TEST_CONTENT_TEXT)
+ }
+
+ content.publicVersion.apply {
+ assertThat(subText).isNull()
+ assertThat(title).isNull()
+ assertThat(text).isNull()
+ }
+ }
+
+ @Test
+ @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
+ fun extractsContent_commonFields_noRedaction() {
+ val entry = createEntry {
+ setSubText(TEST_SUB_TEXT)
+ setContentTitle(TEST_CONTENT_TITLE)
+ setContentText(TEST_CONTENT_TEXT)
+ }
+
+ val content = requireContent(entry, redactionType = REDACTION_TYPE_NONE)
+
+ content.privateVersion.apply {
+ assertThat(subText).isEqualTo(TEST_SUB_TEXT)
+ assertThat(title).isEqualTo(TEST_CONTENT_TITLE)
+ assertThat(text).isEqualTo(TEST_CONTENT_TEXT)
+ }
+
+ content.publicVersion.apply {
+ assertThat(subText).isEqualTo(TEST_SUB_TEXT)
+ assertThat(title).isEqualTo(TEST_CONTENT_TITLE)
+ assertThat(text).isEqualTo(TEST_CONTENT_TEXT)
+ }
}
@Test
@@ -125,9 +159,9 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
fun extractContent_wasPromotedAutomatically_false() {
val entry = createEntry { extras.putBoolean(EXTRA_WAS_AUTOMATICALLY_PROMOTED, false) }
- val content = extractContent(entry)
+ val content = requireContent(entry).privateVersion
- assertThat(content!!.wasPromotedAutomatically).isFalse()
+ assertThat(content.wasPromotedAutomatically).isFalse()
}
@Test
@@ -135,9 +169,9 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
fun extractContent_wasPromotedAutomatically_true() {
val entry = createEntry { extras.putBoolean(EXTRA_WAS_AUTOMATICALLY_PROMOTED, true) }
- val content = extractContent(entry)
+ val content = requireContent(entry).privateVersion
- assertThat(content!!.wasPromotedAutomatically).isTrue()
+ assertThat(content.wasPromotedAutomatically).isTrue()
}
@Test
@@ -146,10 +180,9 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
fun extractContent_apiFlagOff_shortCriticalTextNotExtracted() {
val entry = createEntry { setShortCriticalText(TEST_SHORT_CRITICAL_TEXT) }
- val content = extractContent(entry)
+ val content = requireContent(entry).privateVersion
- assertThat(content).isNotNull()
- assertThat(content?.text).isNull()
+ assertThat(content.text).isNull()
}
@Test
@@ -161,10 +194,9 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
fun extractContent_apiFlagOn_shortCriticalTextExtracted() {
val entry = createEntry { setShortCriticalText(TEST_SHORT_CRITICAL_TEXT) }
- val content = extractContent(entry)
+ val content = requireContent(entry).privateVersion
- assertThat(content).isNotNull()
- assertThat(content?.shortCriticalText).isEqualTo(TEST_SHORT_CRITICAL_TEXT)
+ assertThat(content.shortCriticalText).isEqualTo(TEST_SHORT_CRITICAL_TEXT)
}
@Test
@@ -176,10 +208,9 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
fun extractContent_noShortCriticalTextSet_textIsNull() {
val entry = createEntry { setShortCriticalText(null) }
- val content = extractContent(entry)
+ val content = requireContent(entry).privateVersion
- assertThat(content).isNotNull()
- assertThat(content?.shortCriticalText).isNull()
+ assertThat(content.shortCriticalText).isNull()
}
@Test
@@ -379,17 +410,14 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
setWhen(providedCurrentTime)
}
- val content = extractContent(entry)
-
- assertThat(content).isNotNull()
+ val content = requireContent(entry).privateVersion
when (expected) {
- ExpectedTime.Null -> assertThat(content?.time).isNull()
+ ExpectedTime.Null -> assertThat(content.time).isNull()
ExpectedTime.Time -> {
- val actual = content?.time as? When.Time
- assertThat(actual).isNotNull()
- assertThat(actual?.currentTimeMillis).isEqualTo(expectedCurrentTime)
+ val actual = assertNotNull(content.time as? When.Time)
+ assertThat(actual.currentTimeMillis).isEqualTo(expectedCurrentTime)
}
ExpectedTime.CountDown,
@@ -398,23 +426,24 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
expectedCurrentTime + systemClock.elapsedRealtime() -
systemClock.currentTimeMillis()
- val actual = content?.time as? When.Chronometer
- assertThat(actual).isNotNull()
- assertThat(actual?.elapsedRealtimeMillis).isEqualTo(expectedElapsedRealtime)
- assertThat(actual?.isCountDown).isEqualTo(expected == ExpectedTime.CountDown)
+ val actual = assertNotNull(content.time as? When.Chronometer)
+ assertThat(actual.elapsedRealtimeMillis).isEqualTo(expectedElapsedRealtime)
+ assertThat(actual.isCountDown).isEqualTo(expected == ExpectedTime.CountDown)
}
}
}
+ // TODO: Add tests for the style of the publicVersion once we implement that
+
@Test
@EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
fun extractContent_fromBaseStyle() {
val entry = createEntry { setStyle(null) }
- val content = extractContent(entry)
+ val content = requireContent(entry)
- assertThat(content).isNotNull()
- assertThat(content?.style).isEqualTo(Style.Base)
+ assertThat(content.privateVersion.style).isEqualTo(Style.Base)
+ assertThat(content.publicVersion.style).isEqualTo(Style.Base)
}
@Test
@@ -422,10 +451,10 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
fun extractContent_fromBigPictureStyle() {
val entry = createEntry { setStyle(BigPictureStyle()) }
- val content = extractContent(entry)
+ val content = requireContent(entry)
- assertThat(content).isNotNull()
- assertThat(content?.style).isEqualTo(Style.BigPicture)
+ assertThat(content.privateVersion.style).isEqualTo(Style.BigPicture)
+ assertThat(content.publicVersion.style).isEqualTo(Style.Base)
}
@Test
@@ -442,12 +471,15 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
)
}
- val content = extractContent(entry)
+ val content = requireContent(entry)
- assertThat(content).isNotNull()
- assertThat(content?.style).isEqualTo(Style.BigText)
- assertThat(content?.title).isEqualTo(TEST_BIG_CONTENT_TITLE)
- assertThat(content?.text).isEqualTo(TEST_BIG_TEXT)
+ assertThat(content.privateVersion.style).isEqualTo(Style.BigText)
+ assertThat(content.privateVersion.title).isEqualTo(TEST_BIG_CONTENT_TITLE)
+ assertThat(content.privateVersion.text).isEqualTo(TEST_BIG_TEXT)
+
+ assertThat(content.publicVersion.style).isEqualTo(Style.Base)
+ assertThat(content.publicVersion.title).isNull()
+ assertThat(content.publicVersion.text).isNull()
}
@Test
@@ -464,12 +496,15 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
)
}
- val content = extractContent(entry)
+ val content = requireContent(entry)
- assertThat(content).isNotNull()
- assertThat(content?.style).isEqualTo(Style.BigText)
- assertThat(content?.title).isEqualTo(TEST_CONTENT_TITLE)
- assertThat(content?.text).isEqualTo(TEST_BIG_TEXT)
+ assertThat(content.privateVersion.style).isEqualTo(Style.BigText)
+ assertThat(content.privateVersion.title).isEqualTo(TEST_CONTENT_TITLE)
+ assertThat(content.privateVersion.text).isEqualTo(TEST_BIG_TEXT)
+
+ assertThat(content.publicVersion.style).isEqualTo(Style.Base)
+ assertThat(content.publicVersion.title).isNull()
+ assertThat(content.publicVersion.text).isNull()
}
@Test
@@ -486,12 +521,15 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
)
}
- val content = extractContent(entry)
+ val content = requireContent(entry)
- assertThat(content).isNotNull()
- assertThat(content?.style).isEqualTo(Style.BigText)
- assertThat(content?.title).isEqualTo(TEST_BIG_CONTENT_TITLE)
- assertThat(content?.text).isEqualTo(TEST_CONTENT_TEXT)
+ assertThat(content.privateVersion.style).isEqualTo(Style.BigText)
+ assertThat(content.privateVersion.title).isEqualTo(TEST_BIG_CONTENT_TITLE)
+ assertThat(content.privateVersion.text).isEqualTo(TEST_CONTENT_TEXT)
+
+ assertThat(content.publicVersion.style).isEqualTo(Style.Base)
+ assertThat(content.publicVersion.title).isNull()
+ assertThat(content.publicVersion.text).isNull()
}
@Test
@@ -506,11 +544,14 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
)
val entry = createEntry { setStyle(CallStyle.forOngoingCall(TEST_PERSON, hangUpIntent)) }
- val content = extractContent(entry)
+ val content = requireContent(entry)
- assertThat(content).isNotNull()
- assertThat(content?.style).isEqualTo(Style.Call)
- assertThat(content?.title).isEqualTo(TEST_PERSON_NAME)
+ assertThat(content.privateVersion.style).isEqualTo(Style.Call)
+ assertThat(content.privateVersion.title).isEqualTo(TEST_PERSON_NAME)
+
+ assertThat(content.publicVersion.style).isEqualTo(Style.Base)
+ assertThat(content.publicVersion.title).isNull()
+ assertThat(content.publicVersion.text).isNull()
}
@Test
@@ -524,13 +565,17 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
setStyle(ProgressStyle().addProgressSegment(Segment(100)).setProgress(75))
}
- val content = extractContent(entry)
+ val content = requireContent(entry)
- assertThat(content).isNotNull()
- assertThat(content?.style).isEqualTo(Style.Progress)
- assertThat(content?.newProgress).isNotNull()
- assertThat(content?.newProgress?.progress).isEqualTo(75)
- assertThat(content?.newProgress?.progressMax).isEqualTo(100)
+ assertThat(content.privateVersion.style).isEqualTo(Style.Progress)
+ val newProgress = assertNotNull(content.privateVersion.newProgress)
+ assertThat(newProgress.progress).isEqualTo(75)
+ assertThat(newProgress.progressMax).isEqualTo(100)
+
+ assertThat(content.publicVersion.style).isEqualTo(Style.Base)
+ assertThat(content.publicVersion.title).isNull()
+ assertThat(content.publicVersion.text).isNull()
+ assertThat(content.publicVersion.newProgress).isNull()
}
@Test
@@ -540,10 +585,11 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
setStyle(MessagingStyle(TEST_PERSON).addMessage("message text", 0L, TEST_PERSON))
}
- val content = extractContent(entry)
+ val content = requireContent(entry)
- assertThat(content).isNotNull()
- assertThat(content?.style).isEqualTo(Style.Ineligible)
+ assertThat(content.privateVersion.style).isEqualTo(Style.Ineligible)
+
+ assertThat(content.publicVersion.style).isEqualTo(Style.Ineligible)
}
@Test
@@ -553,18 +599,13 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
setProgress(TEST_PROGRESS_MAX, TEST_PROGRESS, /* indeterminate= */ false)
}
- val content = extractContent(entry)
-
- assertThat(content).isNotNull()
+ val content = requireContent(entry)
- val oldProgress = content?.oldProgress
- assertThat(oldProgress).isNotNull()
+ val oldProgress = assertNotNull(content.privateVersion.oldProgress)
- assertThat(content).isNotNull()
- assertThat(content?.oldProgress).isNotNull()
- assertThat(content?.oldProgress?.progress).isEqualTo(TEST_PROGRESS)
- assertThat(content?.oldProgress?.max).isEqualTo(TEST_PROGRESS_MAX)
- assertThat(content?.oldProgress?.isIndeterminate).isFalse()
+ assertThat(oldProgress.progress).isEqualTo(TEST_PROGRESS)
+ assertThat(oldProgress.max).isEqualTo(TEST_PROGRESS_MAX)
+ assertThat(oldProgress.isIndeterminate).isFalse()
}
@Test
@@ -574,18 +615,25 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
setProgress(TEST_PROGRESS_MAX, TEST_PROGRESS, /* indeterminate= */ true)
}
- val content = extractContent(entry)
+ val content = requireContent(entry)
+ val oldProgress = assertNotNull(content.privateVersion.oldProgress)
- assertThat(content).isNotNull()
- assertThat(content?.oldProgress).isNotNull()
- assertThat(content?.oldProgress?.progress).isEqualTo(TEST_PROGRESS)
- assertThat(content?.oldProgress?.max).isEqualTo(TEST_PROGRESS_MAX)
- assertThat(content?.oldProgress?.isIndeterminate).isTrue()
+ assertThat(oldProgress.progress).isEqualTo(TEST_PROGRESS)
+ assertThat(oldProgress.max).isEqualTo(TEST_PROGRESS_MAX)
+ assertThat(oldProgress.isIndeterminate).isTrue()
}
- private fun extractContent(entry: NotificationEntry): PromotedNotificationContentModel? {
+ private fun requireContent(
+ entry: NotificationEntry,
+ redactionType: Int = REDACTION_TYPE_PUBLIC,
+ ): PromotedNotificationContentModels = assertNotNull(extractContent(entry, redactionType))
+
+ private fun extractContent(
+ entry: NotificationEntry,
+ redactionType: Int = REDACTION_TYPE_PUBLIC,
+ ): PromotedNotificationContentModels? {
val recoveredBuilder = Notification.Builder(context, entry.sbn.notification)
- return underTest.extractContent(entry, recoveredBuilder, imageModelProvider)
+ return underTest.extractContent(entry, recoveredBuilder, redactionType, imageModelProvider)
}
private fun createEntry(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractorTest.kt
index 873ab5bc9c2c..42c3f6603ad8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractorTest.kt
@@ -44,7 +44,7 @@ import com.android.systemui.statusbar.notification.data.repository.activeNotific
import com.android.systemui.statusbar.notification.data.repository.addNotif
import com.android.systemui.statusbar.notification.domain.interactor.renderNotificationListInteractor
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi
-import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentBuilder
import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallTestHelper.addOngoingCallState
import com.android.systemui.testKosmos
@@ -562,14 +562,14 @@ class PromotedNotificationsInteractorTest : SysuiTestCase() {
activeNotificationModel(
key = "notif1",
statusBarChipIcon = createStatusBarIconViewOrNull(),
- promotedContent = PromotedNotificationContentModel.Builder("notif1").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif1").build(),
)
)
activeNotificationListRepository.addNotif(
activeNotificationModel(
key = "notif2",
statusBarChipIcon = createStatusBarIconViewOrNull(),
- promotedContent = PromotedNotificationContentModel.Builder("notif2").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif2").build(),
)
)
@@ -608,14 +608,14 @@ class PromotedNotificationsInteractorTest : SysuiTestCase() {
activeNotificationModel(
key = "notif1",
statusBarChipIcon = createStatusBarIconViewOrNull(),
- promotedContent = PromotedNotificationContentModel.Builder("notif1").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif1").build(),
)
)
activeNotificationListRepository.addNotif(
activeNotificationModel(
key = "notif2",
statusBarChipIcon = createStatusBarIconViewOrNull(),
- promotedContent = PromotedNotificationContentModel.Builder("notif2").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif2").build(),
)
)
@@ -643,8 +643,7 @@ class PromotedNotificationsInteractorTest : SysuiTestCase() {
collectLastValue(underTest.aodPromotedNotification)
// THEN the ron is first because the call has no content
- assertThat(topPromotedNotificationContent?.identity?.key)
- .isEqualTo("0|test_pkg|0|ron|0")
+ assertThat(topPromotedNotificationContent?.key).isEqualTo("0|test_pkg|0|ron|0")
}
@Test
@@ -663,8 +662,7 @@ class PromotedNotificationsInteractorTest : SysuiTestCase() {
collectLastValue(underTest.aodPromotedNotification)
// THEN the call is the top notification
- assertThat(topPromotedNotificationContent?.identity?.key)
- .isEqualTo("0|test_pkg|0|call|0")
+ assertThat(topPromotedNotificationContent?.key).isEqualTo("0|test_pkg|0|call|0")
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index 99f2596dbf1d..19b1046f1931 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -67,11 +67,11 @@ import com.android.systemui.media.controls.util.MediaFeatureFlag;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips;
import com.android.systemui.statusbar.notification.ConversationNotificationProcessor;
-import com.android.systemui.statusbar.notification.collection.EntryAdapter;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.promoted.FakePromotedNotificationContentExtractor;
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi;
-import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel;
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentBuilder;
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
@@ -389,8 +389,8 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
@Test
@DisableFlags({PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME})
public void testExtractsPromotedContent_notWhenBothFlagsDisabled() throws Exception {
- final PromotedNotificationContentModel content =
- new PromotedNotificationContentModel.Builder("key").build();
+ final PromotedNotificationContentModels content =
+ new PromotedNotificationContentBuilder("key").build();
mPromotedNotificationContentExtractor.resetForEntry(mRow.getEntry(), content);
inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow);
@@ -401,43 +401,43 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
@Test
@EnableFlags(PromotedNotificationUi.FLAG_NAME)
@DisableFlags(StatusBarNotifChips.FLAG_NAME)
- public void testExtractsPromotedContent_whenPromotedNotificationUiFlagEnabled()
+ public void testExtractsPromotedContent_whePromotedNotificationUiFlagEnabled()
throws Exception {
- final PromotedNotificationContentModel content =
- new PromotedNotificationContentModel.Builder("key").build();
+ final PromotedNotificationContentModels content =
+ new PromotedNotificationContentBuilder("key").build();
mPromotedNotificationContentExtractor.resetForEntry(mRow.getEntry(), content);
inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow);
mPromotedNotificationContentExtractor.verifyOneExtractCall();
- assertEquals(content, mRow.getEntry().getPromotedNotificationContentModel());
+ assertEquals(content, mRow.getEntry().getPromotedNotificationContentModels());
}
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
@DisableFlags(PromotedNotificationUi.FLAG_NAME)
public void testExtractsPromotedContent_whenStatusBarNotifChipsFlagEnabled() throws Exception {
- final PromotedNotificationContentModel content =
- new PromotedNotificationContentModel.Builder("key").build();
+ final PromotedNotificationContentModels content =
+ new PromotedNotificationContentBuilder("key").build();
mPromotedNotificationContentExtractor.resetForEntry(mRow.getEntry(), content);
inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow);
mPromotedNotificationContentExtractor.verifyOneExtractCall();
- assertEquals(content, mRow.getEntry().getPromotedNotificationContentModel());
+ assertEquals(content, mRow.getEntry().getPromotedNotificationContentModels());
}
@Test
@EnableFlags({PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME})
public void testExtractsPromotedContent_whenBothFlagsEnabled() throws Exception {
- final PromotedNotificationContentModel content =
- new PromotedNotificationContentModel.Builder("key").build();
+ final PromotedNotificationContentModels content =
+ new PromotedNotificationContentBuilder("key").build();
mPromotedNotificationContentExtractor.resetForEntry(mRow.getEntry(), content);
inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow);
mPromotedNotificationContentExtractor.verifyOneExtractCall();
- assertEquals(content, mRow.getEntry().getPromotedNotificationContentModel());
+ assertEquals(content, mRow.getEntry().getPromotedNotificationContentModels());
}
@Test
@@ -448,7 +448,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow);
mPromotedNotificationContentExtractor.verifyOneExtractCall();
- assertNull(mRow.getEntry().getPromotedNotificationContentModel());
+ assertNull(mRow.getEntry().getPromotedNotificationContentModels());
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
index 063a04ab9f37..dcba3e447dda 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
@@ -44,7 +44,7 @@ import com.android.systemui.statusbar.notification.ConversationNotificationProce
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.promoted.FakePromotedNotificationContentExtractor
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi
-import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentBuilder
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED
@@ -456,7 +456,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
@Test
@DisableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
fun testExtractsPromotedContent_notWhenBothFlagsDisabled() {
- val content = PromotedNotificationContentModel.Builder("key").build()
+ val content = PromotedNotificationContentBuilder("key").build()
promotedNotificationContentExtractor.resetForEntry(row.entry, content)
inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row)
@@ -468,38 +468,38 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
@EnableFlags(PromotedNotificationUi.FLAG_NAME)
@DisableFlags(StatusBarNotifChips.FLAG_NAME)
fun testExtractsPromotedContent_whenPromotedNotificationUiFlagEnabled() {
- val content = PromotedNotificationContentModel.Builder("key").build()
+ val content = PromotedNotificationContentBuilder("key").build()
promotedNotificationContentExtractor.resetForEntry(row.entry, content)
inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row)
promotedNotificationContentExtractor.verifyOneExtractCall()
- Assert.assertEquals(content, row.entry.promotedNotificationContentModel)
+ Assert.assertEquals(content, row.entry.promotedNotificationContentModels)
}
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
@DisableFlags(PromotedNotificationUi.FLAG_NAME)
fun testExtractsPromotedContent_whenStatusBarNotifChipsFlagEnabled() {
- val content = PromotedNotificationContentModel.Builder("key").build()
+ val content = PromotedNotificationContentBuilder("key").build()
promotedNotificationContentExtractor.resetForEntry(row.entry, content)
inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row)
promotedNotificationContentExtractor.verifyOneExtractCall()
- Assert.assertEquals(content, row.entry.promotedNotificationContentModel)
+ Assert.assertEquals(content, row.entry.promotedNotificationContentModels)
}
@Test
@EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
fun testExtractsPromotedContent_whenBothFlagsEnabled() {
- val content = PromotedNotificationContentModel.Builder("key").build()
+ val content = PromotedNotificationContentBuilder("key").build()
promotedNotificationContentExtractor.resetForEntry(row.entry, content)
inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row)
promotedNotificationContentExtractor.verifyOneExtractCall()
- Assert.assertEquals(content, row.entry.promotedNotificationContentModel)
+ Assert.assertEquals(content, row.entry.promotedNotificationContentModels)
}
@Test
@@ -510,7 +510,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row)
promotedNotificationContentExtractor.verifyOneExtractCall()
- Assert.assertNull(row.entry.promotedNotificationContentModel)
+ Assert.assertNull(row.entry.promotedNotificationContentModels)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index c58b4bc9953c..18074d53e87b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -40,7 +40,7 @@ import com.android.systemui.statusbar.notification.data.model.activeNotification
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
-import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentBuilder
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
import com.android.systemui.statusbar.notification.shared.CallType
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
@@ -170,7 +170,7 @@ class OngoingCallControllerTest : SysuiTestCase() {
@Test
fun interactorHasOngoingCallNotif_repoHasPromotedContent() =
testScope.runTest {
- val promotedContent = PromotedNotificationContentModel.Builder("ongoingNotif").build()
+ val promotedContent = PromotedNotificationContentBuilder("ongoingNotif").build()
setNotifOnRepo(
activeNotificationModel(
key = "ongoingNotif",
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt
index 84f1d5cd4895..c071327ae398 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt
@@ -29,7 +29,7 @@ import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository
import com.android.systemui.statusbar.gesture.swipeStatusBarAwayGestureHandler
-import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentBuilder
import com.android.systemui.statusbar.phone.ongoingcall.EnableChipsModernization
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallTestHelper.addOngoingCallState
@@ -75,7 +75,7 @@ class OngoingCallInteractorTest : SysuiTestCase() {
val startTimeMs = 1000L
val testIconView: StatusBarIconView = mock()
val testIntent: PendingIntent = mock()
- val testPromotedContent = PromotedNotificationContentModel.Builder(key).build()
+ val testPromotedContent = PromotedNotificationContentBuilder(key).build()
addOngoingCallState(
key = key,
startTimeMs = startTimeMs,
@@ -106,7 +106,7 @@ class OngoingCallInteractorTest : SysuiTestCase() {
val startTimeMs = 1000L
val testIconView: StatusBarIconView = mock()
val testIntent: PendingIntent = mock()
- val testPromotedContent = PromotedNotificationContentModel.Builder(key).build()
+ val testPromotedContent = PromotedNotificationContentBuilder(key).build()
addOngoingCallState(
key = key,
startTimeMs = startTimeMs,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryKairosAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryKairosAdapterTest.kt
new file mode 100644
index 000000000000..3cf787d50d2f
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryKairosAdapterTest.kt
@@ -0,0 +1,70 @@
+/*
+ * 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.pipeline.mobile.data.repository.prod
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.activated
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.launchKairosNetwork
+import com.android.systemui.kairos.stateOf
+import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig
+import com.android.systemui.statusbar.pipeline.mobile.data.model.testCarrierConfig
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.runCurrent
+import org.junit.runner.RunWith
+import org.mockito.Mockito
+
+@OptIn(ExperimentalKairosApi::class, ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CarrierMergedConnectionRepositoryKairosAdapterTest :
+ CarrierMergedConnectionRepositoryTestBase() {
+
+ var job: Job? = null
+ val kairosNetwork = testScope.backgroundScope.launchKairosNetwork()
+
+ override fun recreateRepo(): MobileConnectionRepositoryKairosAdapter {
+ lateinit var adapter: MobileConnectionRepositoryKairosAdapter
+ job?.cancel()
+ Mockito.clearInvocations(telephonyManager)
+ job =
+ testScope.backgroundScope.launch {
+ kairosNetwork.activateSpec {
+ val repo = activated {
+ CarrierMergedConnectionRepositoryKairos(
+ SUB_ID,
+ logger,
+ telephonyManager,
+ wifiRepository,
+ isInEcmMode = stateOf(false),
+ )
+ }
+ adapter =
+ MobileConnectionRepositoryKairosAdapter(
+ repo,
+ SystemUiCarrierConfig(SUB_ID, testCarrierConfig()),
+ )
+ Unit
+ }
+ }
+ testScope.runCurrent() // ensure the lateinit is set
+ return adapter
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt
index 8e55f2e9a31a..8a6829cf5d21 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt
@@ -25,6 +25,7 @@ import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
@@ -43,16 +44,30 @@ import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidJUnit4::class)
-class CarrierMergedConnectionRepositoryTest : SysuiTestCase() {
+class CarrierMergedConnectionRepositoryTest : CarrierMergedConnectionRepositoryTestBase() {
+ override fun recreateRepo() =
+ CarrierMergedConnectionRepository(
+ SUB_ID,
+ logger,
+ telephonyManager,
+ testScope.backgroundScope.coroutineContext,
+ testScope.backgroundScope,
+ wifiRepository,
+ )
+}
+
+abstract class CarrierMergedConnectionRepositoryTestBase : SysuiTestCase() {
- private lateinit var underTest: CarrierMergedConnectionRepository
+ protected lateinit var underTest: MobileConnectionRepository
- private lateinit var wifiRepository: FakeWifiRepository
- @Mock private lateinit var logger: TableLogBuffer
- @Mock private lateinit var telephonyManager: TelephonyManager
+ protected lateinit var wifiRepository: FakeWifiRepository
+ @Mock protected lateinit var logger: TableLogBuffer
+ @Mock protected lateinit var telephonyManager: TelephonyManager
- private val testDispatcher = UnconfinedTestDispatcher()
- private val testScope = TestScope(testDispatcher)
+ protected val testDispatcher = UnconfinedTestDispatcher()
+ protected val testScope = TestScope(testDispatcher)
+
+ abstract fun recreateRepo(): MobileConnectionRepository
@Before
fun setUp() {
@@ -62,15 +77,7 @@ class CarrierMergedConnectionRepositoryTest : SysuiTestCase() {
wifiRepository = FakeWifiRepository()
- underTest =
- CarrierMergedConnectionRepository(
- SUB_ID,
- logger,
- telephonyManager,
- testScope.backgroundScope.coroutineContext,
- testScope.backgroundScope,
- wifiRepository,
- )
+ underTest = recreateRepo()
}
@Test
@@ -121,10 +128,7 @@ class CarrierMergedConnectionRepositoryTest : SysuiTestCase() {
wifiRepository.setIsWifiDefault(true)
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged.of(
- subscriptionId = SUB_ID,
- level = 3,
- )
+ WifiNetworkModel.CarrierMerged.of(subscriptionId = SUB_ID, level = 3)
)
assertThat(latest).isEqualTo(3)
@@ -141,26 +145,17 @@ class CarrierMergedConnectionRepositoryTest : SysuiTestCase() {
wifiRepository.setIsWifiEnabled(true)
wifiRepository.setIsWifiDefault(true)
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged.of(
- subscriptionId = SUB_ID,
- level = 3,
- )
+ WifiNetworkModel.CarrierMerged.of(subscriptionId = SUB_ID, level = 3)
)
wifiRepository.setWifiActivity(
- DataActivityModel(
- hasActivityIn = true,
- hasActivityOut = false,
- )
+ DataActivityModel(hasActivityIn = true, hasActivityOut = false)
)
assertThat(latest!!.hasActivityIn).isTrue()
assertThat(latest!!.hasActivityOut).isFalse()
wifiRepository.setWifiActivity(
- DataActivityModel(
- hasActivityIn = false,
- hasActivityOut = true,
- )
+ DataActivityModel(hasActivityIn = false, hasActivityOut = true)
)
assertThat(latest!!.hasActivityIn).isFalse()
@@ -178,10 +173,7 @@ class CarrierMergedConnectionRepositoryTest : SysuiTestCase() {
val typeJob = underTest.resolvedNetworkType.onEach { latestType = it }.launchIn(this)
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged.of(
- subscriptionId = SUB_ID + 10,
- level = 3,
- )
+ WifiNetworkModel.CarrierMerged.of(subscriptionId = SUB_ID + 10, level = 3)
)
assertThat(latestLevel).isNotEqualTo(3)
@@ -199,10 +191,7 @@ class CarrierMergedConnectionRepositoryTest : SysuiTestCase() {
val job = underTest.primaryLevel.onEach { latest = it }.launchIn(this)
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged.of(
- subscriptionId = SUB_ID,
- level = 3,
- )
+ WifiNetworkModel.CarrierMerged.of(subscriptionId = SUB_ID, level = 3)
)
wifiRepository.setIsWifiEnabled(false)
@@ -219,10 +208,7 @@ class CarrierMergedConnectionRepositoryTest : SysuiTestCase() {
val job = underTest.primaryLevel.onEach { latest = it }.launchIn(this)
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged.of(
- subscriptionId = SUB_ID,
- level = 3,
- )
+ WifiNetworkModel.CarrierMerged.of(subscriptionId = SUB_ID, level = 3)
)
wifiRepository.setIsWifiDefault(false)
@@ -280,6 +266,7 @@ class CarrierMergedConnectionRepositoryTest : SysuiTestCase() {
fun networkName_usesSimOperatorNameAsInitial() =
testScope.runTest {
whenever(telephonyManager.simOperatorName).thenReturn("Test SIM name")
+ underTest = recreateRepo()
var latest: NetworkNameModel? = null
val job = underTest.networkName.onEach { latest = it }.launchIn(this)
@@ -293,6 +280,10 @@ class CarrierMergedConnectionRepositoryTest : SysuiTestCase() {
fun networkName_updatesOnNetworkUpdate() =
testScope.runTest {
whenever(telephonyManager.simOperatorName).thenReturn("Test SIM name")
+ underTest = recreateRepo()
+
+ wifiRepository.setIsWifiEnabled(true)
+ wifiRepository.setIsWifiDefault(true)
var latest: NetworkNameModel? = null
val job = underTest.networkName.onEach { latest = it }.launchIn(this)
@@ -301,10 +292,7 @@ class CarrierMergedConnectionRepositoryTest : SysuiTestCase() {
whenever(telephonyManager.simOperatorName).thenReturn("New SIM name")
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged.of(
- subscriptionId = SUB_ID,
- level = 3,
- )
+ WifiNetworkModel.CarrierMerged.of(subscriptionId = SUB_ID, level = 3)
)
assertThat(latest).isEqualTo(NetworkNameModel.SimDerived("New SIM name"))
@@ -320,7 +308,7 @@ class CarrierMergedConnectionRepositoryTest : SysuiTestCase() {
assertThat(latest).isTrue()
}
- private companion object {
+ companion object {
const val SUB_ID = 123
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorKairosAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorKairosAdapterTest.kt
new file mode 100644
index 000000000000..e72d0c27e632
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorKairosAdapterTest.kt
@@ -0,0 +1,141 @@
+/*
+ * 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.pipeline.mobile.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.SignalIcon
+import com.android.settingslib.mobile.MobileIconCarrierIdOverrides
+import com.android.systemui.activated
+import com.android.systemui.kairos.BuildScope
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.Incremental
+import com.android.systemui.kairos.State
+import com.android.systemui.kairos.asIncremental
+import com.android.systemui.kairos.buildSpec
+import com.android.systemui.kairos.combine
+import com.android.systemui.kairos.launchKairosNetwork
+import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorKairosAdapterTest.Companion.wrapRepo
+import com.android.systemui.util.mockito.mock
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.runCurrent
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalKairosApi::class, ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class MobileIconInteractorKairosAdapterTest : MobileIconInteractorTestBase() {
+
+ var job: Job? = null
+ val kairosNetwork = testScope.backgroundScope.launchKairosNetwork()
+
+ override fun createInteractor(overrides: MobileIconCarrierIdOverrides): MobileIconInteractor {
+ lateinit var result: MobileIconInteractor
+ job?.cancel()
+ job =
+ testScope.backgroundScope.launch {
+ kairosNetwork.activateSpec {
+ val wrapped = wrap(mobileIconsInteractor)
+ result =
+ MobileIconInteractorKairosAdapter(
+ kairosImpl =
+ activated {
+ MobileIconInteractorKairosImpl(
+ defaultSubscriptionHasDataEnabled =
+ wrapped.activeDataConnectionHasDataEnabled,
+ alwaysShowDataRatIcon = wrapped.alwaysShowDataRatIcon,
+ alwaysUseCdmaLevel = wrapped.alwaysUseCdmaLevel,
+ isSingleCarrier = wrapped.isSingleCarrier,
+ mobileIsDefault = wrapped.mobileIsDefault,
+ defaultMobileIconMapping = wrapped.defaultMobileIconMapping,
+ defaultMobileIconGroup = wrapped.defaultMobileIconGroup,
+ isDefaultConnectionFailed =
+ wrapped.isDefaultConnectionFailed,
+ isForceHidden = wrapped.isForceHidden,
+ connectionRepository = wrapRepo(connectionRepository),
+ context = context,
+ carrierIdOverrides = overrides,
+ )
+ }
+ )
+ Unit
+ }
+ }
+ testScope.runCurrent() // ensure the lateinit is set
+ return result
+ }
+
+ /** Allows us to wrap a (likely fake) MobileIconsInteractor into a Kairos version. */
+ private fun BuildScope.wrap(interactor: MobileIconsInteractor): MobileIconsInteractorKairos {
+ val filteredSubscriptions = interactor.filteredSubscriptions.toState(emptyList())
+ val icons = interactor.icons.toState()
+ return InteractorWrapper(
+ mobileIsDefault = interactor.mobileIsDefault.toState(),
+ filteredSubscriptions = filteredSubscriptions,
+ icons =
+ combine(filteredSubscriptions, icons) { subs, icons ->
+ subs.zip(icons).associate { (subModel, icon) ->
+ subModel.subscriptionId to buildSpec { wrap(icon) }
+ }
+ }
+ .asIncremental()
+ .applyLatestSpecForKey(),
+ isStackable = interactor.isStackable.toState(),
+ activeDataConnectionHasDataEnabled =
+ interactor.activeDataConnectionHasDataEnabled.toState(),
+ activeDataIconInteractor =
+ interactor.activeDataIconInteractor.toState().mapLatestBuild {
+ it?.let { wrap(it) }
+ },
+ alwaysShowDataRatIcon = interactor.alwaysShowDataRatIcon.toState(),
+ alwaysUseCdmaLevel = interactor.alwaysUseCdmaLevel.toState(),
+ isSingleCarrier = interactor.isSingleCarrier.toState(),
+ defaultMobileIconMapping = interactor.defaultMobileIconMapping.toState(),
+ defaultMobileIconGroup = interactor.defaultMobileIconGroup.toState(),
+ isDefaultConnectionFailed = interactor.isDefaultConnectionFailed.toState(),
+ isUserSetUp = interactor.isUserSetUp.toState(),
+ isForceHidden = interactor.isForceHidden.toState(false),
+ isDeviceInEmergencyCallsOnlyMode =
+ interactor.isDeviceInEmergencyCallsOnlyMode.toState(false),
+ )
+ }
+
+ private fun BuildScope.wrap(interactor: MobileIconInteractor): MobileIconInteractorKairos =
+ // unused in tests
+ mock()
+
+ private class InteractorWrapper(
+ override val mobileIsDefault: State<Boolean>,
+ override val filteredSubscriptions: State<List<SubscriptionModel>>,
+ override val icons: Incremental<Int, MobileIconInteractorKairos>,
+ override val isStackable: State<Boolean>,
+ override val activeDataConnectionHasDataEnabled: State<Boolean>,
+ override val activeDataIconInteractor: State<MobileIconInteractorKairos?>,
+ override val alwaysShowDataRatIcon: State<Boolean>,
+ override val alwaysUseCdmaLevel: State<Boolean>,
+ override val isSingleCarrier: State<Boolean>,
+ override val defaultMobileIconMapping: State<Map<String, SignalIcon.MobileIconGroup>>,
+ override val defaultMobileIconGroup: State<SignalIcon.MobileIconGroup>,
+ override val isDefaultConnectionFailed: State<Boolean>,
+ override val isUserSetUp: State<Boolean>,
+ override val isForceHidden: State<Boolean>,
+ override val isDeviceInEmergencyCallsOnlyMode: State<Boolean>,
+ ) : MobileIconsInteractorKairos
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
index 8c70da718c08..974a47587c77 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
@@ -22,6 +22,7 @@ import android.telephony.CellSignalStrength
import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.internal.telephony.flags.Flags
import com.android.settingslib.mobile.MobileIconCarrierIdOverrides
import com.android.settingslib.mobile.MobileIconCarrierIdOverridesImpl
import com.android.settingslib.mobile.TelephonyIcons
@@ -58,21 +59,40 @@ import org.mockito.ArgumentMatchers.anyString
@SmallTest
@RunWith(AndroidJUnit4::class)
-class MobileIconInteractorTest : SysuiTestCase() {
- private val kosmos = testKosmos()
+class MobileIconInteractorTest : MobileIconInteractorTestBase() {
+ override fun createInteractor(overrides: MobileIconCarrierIdOverrides) =
+ MobileIconInteractorImpl(
+ testScope.backgroundScope,
+ mobileIconsInteractor.activeDataConnectionHasDataEnabled,
+ mobileIconsInteractor.alwaysShowDataRatIcon,
+ mobileIconsInteractor.alwaysUseCdmaLevel,
+ mobileIconsInteractor.isSingleCarrier,
+ mobileIconsInteractor.mobileIsDefault,
+ mobileIconsInteractor.defaultMobileIconMapping,
+ mobileIconsInteractor.defaultMobileIconGroup,
+ mobileIconsInteractor.isDefaultConnectionFailed,
+ mobileIconsInteractor.isForceHidden,
+ connectionRepository,
+ context,
+ overrides,
+ )
+}
- private lateinit var underTest: MobileIconInteractor
- private val mobileMappingsProxy = FakeMobileMappingsProxy()
- private val mobileIconsInteractor = FakeMobileIconsInteractor(mobileMappingsProxy, mock())
+abstract class MobileIconInteractorTestBase : SysuiTestCase() {
+ protected val kosmos = testKosmos()
- private val connectionRepository =
+ protected lateinit var underTest: MobileIconInteractor
+ protected val mobileMappingsProxy = FakeMobileMappingsProxy()
+ protected val mobileIconsInteractor = FakeMobileIconsInteractor(mobileMappingsProxy, mock())
+
+ protected val connectionRepository =
FakeMobileConnectionRepository(
SUB_1_ID,
logcatTableLogBuffer(kosmos, "MobileIconInteractorTest"),
)
- private val testDispatcher = UnconfinedTestDispatcher()
- private val testScope = TestScope(testDispatcher)
+ protected val testDispatcher = UnconfinedTestDispatcher()
+ protected val testScope = TestScope(testDispatcher)
@Before
fun setUp() {
@@ -835,24 +855,9 @@ class MobileIconInteractorTest : SysuiTestCase() {
assertThat(latest!!.level).isEqualTo(0)
}
- private fun createInteractor(
+ abstract fun createInteractor(
overrides: MobileIconCarrierIdOverrides = MobileIconCarrierIdOverridesImpl()
- ) =
- MobileIconInteractorImpl(
- testScope.backgroundScope,
- mobileIconsInteractor.activeDataConnectionHasDataEnabled,
- mobileIconsInteractor.alwaysShowDataRatIcon,
- mobileIconsInteractor.alwaysUseCdmaLevel,
- mobileIconsInteractor.isSingleCarrier,
- mobileIconsInteractor.mobileIsDefault,
- mobileIconsInteractor.defaultMobileIconMapping,
- mobileIconsInteractor.defaultMobileIconGroup,
- mobileIconsInteractor.isDefaultConnectionFailed,
- mobileIconsInteractor.isForceHidden,
- connectionRepository,
- context,
- overrides,
- )
+ ): MobileIconInteractor
companion object {
private const val GSM_LEVEL = 1
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorKairosAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorKairosAdapterTest.kt
new file mode 100644
index 000000000000..787731e14cd1
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorKairosAdapterTest.kt
@@ -0,0 +1,223 @@
+/*
+ * 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.pipeline.mobile.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.SignalIcon
+import com.android.settingslib.mobile.MobileMappings
+import com.android.settingslib.mobile.TelephonyIcons
+import com.android.systemui.KairosBuilder
+import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.kairos.BuildScope
+import com.android.systemui.kairos.Events
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.Incremental
+import com.android.systemui.kairos.State
+import com.android.systemui.kairos.activateKairosActivatable
+import com.android.systemui.kairos.asIncremental
+import com.android.systemui.kairos.buildSpec
+import com.android.systemui.kairos.kairos
+import com.android.systemui.kairos.map
+import com.android.systemui.kairos.mapValues
+import com.android.systemui.kairos.stateOf
+import com.android.systemui.kairosBuilder
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.tableLogBufferFactory
+import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
+import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
+import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType
+import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepositoryKairos
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepositoryKairos
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileMappingsProxy
+import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
+import com.android.systemui.statusbar.pipeline.shared.data.repository.connectivityRepository
+import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+
+@OptIn(ExperimentalKairosApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class MobileIconsInteractorKairosAdapterTest : MobileIconsInteractorTestBase() {
+ override fun Kosmos.createInteractor(): MobileIconsInteractor {
+ val userSetupRepo = FakeUserSetupRepository()
+ val repoK =
+ MobileConnectionsRepoWrapper(connectionsRepository).also {
+ activateKairosActivatable(it)
+ }
+ val kairosInteractor =
+ MobileIconsInteractorKairosImpl(
+ mobileConnectionsRepo = repoK,
+ carrierConfigTracker = carrierConfigTracker,
+ tableLogger = mock(),
+ connectivityRepository = connectivityRepository,
+ userSetupRepo = userSetupRepo,
+ context = context,
+ featureFlagsClassic = featureFlagsClassic,
+ )
+ .also { activateKairosActivatable(it) }
+ return MobileIconsInteractorKairosAdapter(
+ kairosInteractor = kairosInteractor,
+ repo = connectionsRepository,
+ repoK = repoK,
+ kairosNetwork = kairos,
+ scope = applicationCoroutineScope,
+ context = context,
+ mobileMappingsProxy = mobileMappingsProxy,
+ userSetupRepo = userSetupRepo,
+ logFactory = tableLogBufferFactory,
+ )
+ .also {
+ activateKairosActivatable(it)
+ runCurrent()
+ }
+ }
+
+ /** Allows us to wrap a (likely fake) MobileConnectionsRepository into a Kairos version. */
+ private class MobileConnectionsRepoWrapper(val unwrapped: MobileConnectionsRepository) :
+ MobileConnectionsRepositoryKairos, KairosBuilder by kairosBuilder() {
+
+ override val mobileConnectionsBySubId: Incremental<Int, MobileConnectionRepositoryKairos> =
+ buildIncremental {
+ unwrapped.subscriptions
+ .toState()
+ .map { it.associate { it.subscriptionId to Unit } }
+ .asIncremental()
+ .mapValues { (subId, _) ->
+ buildSpec { wrapRepo(unwrapped.getRepoForSubId(subId)) }
+ }
+ .applyLatestSpecForKey()
+ }
+ override val subscriptions: State<Collection<SubscriptionModel>> = buildState {
+ unwrapped.subscriptions.toState()
+ }
+ override val activeMobileDataSubscriptionId: State<Int?> = buildState {
+ unwrapped.activeMobileDataSubscriptionId.toState()
+ }
+ override val activeMobileDataRepository: State<MobileConnectionRepositoryKairos?> =
+ buildState {
+ unwrapped.activeMobileDataRepository.toState().mapLatestBuild {
+ it?.let { wrapRepo(it) }
+ }
+ }
+ override val activeSubChangedInGroupEvent: Events<Unit> = buildEvents {
+ unwrapped.activeSubChangedInGroupEvent.toEvents()
+ }
+ override val defaultDataSubId: State<Int?> = buildState {
+ unwrapped.defaultDataSubId.toState()
+ }
+ override val mobileIsDefault: State<Boolean> = buildState {
+ unwrapped.mobileIsDefault.toState()
+ }
+ override val hasCarrierMergedConnection: State<Boolean> = buildState {
+ unwrapped.hasCarrierMergedConnection.toState(false)
+ }
+ override val defaultConnectionIsValidated: State<Boolean> = buildState {
+ unwrapped.defaultConnectionIsValidated.toState()
+ }
+ override val defaultDataSubRatConfig: State<MobileMappings.Config> = buildState {
+ unwrapped.defaultDataSubRatConfig.toState()
+ }
+ override val defaultMobileIconMapping: State<Map<String, SignalIcon.MobileIconGroup>> =
+ buildState {
+ unwrapped.defaultMobileIconMapping.toState(emptyMap())
+ }
+ override val defaultMobileIconGroup: State<SignalIcon.MobileIconGroup> = buildState {
+ unwrapped.defaultMobileIconGroup.toState(TelephonyIcons.THREE_G)
+ }
+ override val isDeviceEmergencyCallCapable: State<Boolean> = buildState {
+ unwrapped.isDeviceEmergencyCallCapable.toState()
+ }
+ override val isAnySimSecure: State<Boolean> = buildState {
+ unwrapped.isDeviceEmergencyCallCapable.toState()
+ }
+ override val isInEcmMode: State<Boolean> = stateOf(false)
+ }
+
+ private class MobileConnectionRepoWrapper(
+ override val subId: Int,
+ override val carrierId: State<Int>,
+ override val inflateSignalStrength: State<Boolean>,
+ override val allowNetworkSliceIndicator: State<Boolean>,
+ override val tableLogBuffer: TableLogBuffer,
+ override val isEmergencyOnly: State<Boolean>,
+ override val isRoaming: State<Boolean>,
+ override val operatorAlphaShort: State<String?>,
+ override val isInService: State<Boolean>,
+ override val isNonTerrestrial: State<Boolean>,
+ override val isGsm: State<Boolean>,
+ override val cdmaLevel: State<Int>,
+ override val primaryLevel: State<Int>,
+ override val satelliteLevel: State<Int>,
+ override val dataConnectionState: State<DataConnectionState>,
+ override val dataActivityDirection: State<DataActivityModel>,
+ override val carrierNetworkChangeActive: State<Boolean>,
+ override val resolvedNetworkType: State<ResolvedNetworkType>,
+ override val numberOfLevels: State<Int>,
+ override val dataEnabled: State<Boolean>,
+ override val cdmaRoaming: State<Boolean>,
+ override val networkName: State<NetworkNameModel>,
+ override val carrierName: State<NetworkNameModel>,
+ override val isAllowedDuringAirplaneMode: State<Boolean>,
+ override val hasPrioritizedNetworkCapabilities: State<Boolean>,
+ override val isInEcmMode: State<Boolean>,
+ ) : MobileConnectionRepositoryKairos
+
+ companion object {
+ /** Allows us to wrap a (likely fake) MobileConnectionRepository into a Kairos version. */
+ fun BuildScope.wrapRepo(
+ conn: MobileConnectionRepository
+ ): MobileConnectionRepositoryKairos =
+ with(conn) {
+ MobileConnectionRepoWrapper(
+ subId = subId,
+ carrierId = carrierId.toState(),
+ inflateSignalStrength = inflateSignalStrength.toState(),
+ allowNetworkSliceIndicator = allowNetworkSliceIndicator.toState(),
+ tableLogBuffer = tableLogBuffer,
+ isEmergencyOnly = isEmergencyOnly.toState(),
+ isRoaming = isRoaming.toState(),
+ operatorAlphaShort = operatorAlphaShort.toState(),
+ isInService = isInService.toState(),
+ isNonTerrestrial = isNonTerrestrial.toState(),
+ isGsm = isGsm.toState(),
+ cdmaLevel = cdmaLevel.toState(),
+ primaryLevel = primaryLevel.toState(),
+ satelliteLevel = satelliteLevel.toState(),
+ dataConnectionState = dataConnectionState.toState(),
+ dataActivityDirection = dataActivityDirection.toState(),
+ carrierNetworkChangeActive = carrierNetworkChangeActive.toState(),
+ resolvedNetworkType = resolvedNetworkType.toState(),
+ numberOfLevels = numberOfLevels.toState(),
+ dataEnabled = dataEnabled.toState(),
+ cdmaRoaming = cdmaRoaming.toState(),
+ networkName = networkName.toState(),
+ carrierName = carrierName.toState(),
+ isAllowedDuringAirplaneMode = isAllowedDuringAirplaneMode.toState(),
+ hasPrioritizedNetworkCapabilities = hasPrioritizedNetworkCapabilities.toState(),
+ isInEcmMode = stateOf(false),
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
index 9e914ad0a660..356e5676954e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
@@ -58,8 +58,35 @@ import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
-class MobileIconsInteractorTest : SysuiTestCase() {
- private val kosmos by lazy {
+class MobileIconsInteractorTest : MobileIconsInteractorTestBase() {
+ override fun Kosmos.createInteractor() =
+ MobileIconsInteractorImpl(
+ mobileConnectionsRepository,
+ carrierConfigTracker,
+ tableLogger = mock(),
+ connectivityRepository,
+ FakeUserSetupRepository(),
+ testScope.backgroundScope,
+ context,
+ featureFlagsClassic,
+ )
+
+ @Test
+ fun iconInteractor_cachedPerSubId() =
+ kosmos.runTest {
+ connectionsRepository.setSubscriptions(listOf(SUB_1))
+ runCurrent()
+
+ val interactor1 = underTest.getMobileConnectionInteractorForSubId(SUB_1_ID)
+ val interactor2 = underTest.getMobileConnectionInteractorForSubId(SUB_1_ID)
+
+ assertThat(interactor1).isNotNull()
+ assertThat(interactor1).isSameInstanceAs(interactor2)
+ }
+}
+
+abstract class MobileIconsInteractorTestBase : SysuiTestCase() {
+ protected val kosmos by lazy {
testKosmos().apply {
mobileConnectionsRepositoryLogbufferName = "MobileIconsInteractorTest"
mobileConnectionsRepository.fake.run {
@@ -78,22 +105,13 @@ class MobileIconsInteractorTest : SysuiTestCase() {
}
// shortcut rename
- private val Kosmos.connectionsRepository by Fixture { mobileConnectionsRepository.fake }
+ protected val Kosmos.connectionsRepository by Fixture { mobileConnectionsRepository.fake }
- private val Kosmos.carrierConfigTracker by Fixture { mock<CarrierConfigTracker>() }
+ protected val Kosmos.carrierConfigTracker by Fixture { mock<CarrierConfigTracker>() }
- private val Kosmos.underTest by Fixture {
- MobileIconsInteractorImpl(
- mobileConnectionsRepository,
- carrierConfigTracker,
- tableLogger = mock(),
- connectivityRepository,
- FakeUserSetupRepository(),
- testScope.backgroundScope,
- context,
- featureFlagsClassic,
- )
- }
+ protected val Kosmos.underTest by Fixture { createInteractor() }
+
+ abstract fun Kosmos.createInteractor(): MobileIconsInteractor
@Test
fun filteredSubscriptions_default() =
@@ -744,12 +762,15 @@ class MobileIconsInteractorTest : SysuiTestCase() {
val latest by collectLastValue(underTest.mobileIsDefault)
connectionsRepository.mobileIsDefault.value = true
+ runCurrent()
assertThat(latest).isTrue()
connectionsRepository.mobileIsDefault.value = false
+ runCurrent()
assertThat(latest).isFalse()
connectionsRepository.hasCarrierMergedConnection.value = true
+ runCurrent()
assertThat(latest).isTrue()
}
@@ -874,16 +895,6 @@ class MobileIconsInteractorTest : SysuiTestCase() {
}
@Test
- fun iconInteractor_cachedPerSubId() =
- kosmos.runTest {
- val interactor1 = underTest.getMobileConnectionInteractorForSubId(SUB_1_ID)
- val interactor2 = underTest.getMobileConnectionInteractorForSubId(SUB_1_ID)
-
- assertThat(interactor1).isNotNull()
- assertThat(interactor1).isSameInstanceAs(interactor2)
- }
-
- @Test
fun deviceBasedEmergencyMode_emergencyCallsOnly_followsDeviceServiceStateFromRepo() =
kosmos.runTest {
val latest by collectLastValue(underTest.isDeviceInEmergencyCallsOnlyMode)
@@ -1007,8 +1018,8 @@ class MobileIconsInteractorTest : SysuiTestCase() {
companion object {
- private const val SUB_1_ID = 1
- private val SUB_1 =
+ const val SUB_1_ID = 1
+ val SUB_1 =
SubscriptionModel(
subscriptionId = SUB_1_ID,
carrierName = "Carrier $SUB_1_ID",
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/topwindoweffects/TopLevelWindowEffectsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/topwindoweffects/TopLevelWindowEffectsTest.kt
index 6d3813c90bfd..bcc6ea3d3b35 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/topwindoweffects/TopLevelWindowEffectsTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/topwindoweffects/TopLevelWindowEffectsTest.kt
@@ -20,8 +20,6 @@ import android.view.View
import android.view.WindowManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.app.viewcapture.ViewCapture
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyevent.data.repository.fakeKeyEventRepository
import com.android.systemui.keyevent.data.repository.keyEventRepository
@@ -58,20 +56,13 @@ class TopLevelWindowEffectsTest : SysuiTestCase() {
private lateinit var windowManager: WindowManager
@Mock
- private lateinit var viewCapture: Lazy<ViewCapture>
-
- @Mock
private lateinit var viewModelFactory: SqueezeEffectViewModel.Factory
private val Kosmos.underTest by Kosmos.Fixture {
TopLevelWindowEffects(
context = mContext,
applicationScope = testScope.backgroundScope,
- windowManager = ViewCaptureAwareWindowManager(
- windowManager = windowManager,
- lazyViewCapture = viewCapture,
- isViewCaptureEnabled = false
- ),
+ windowManager = windowManager,
keyEventInteractor = keyEventInteractor,
viewModelFactory = viewModelFactory,
squeezeEffectInteractor = SqueezeEffectInteractor(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractorTest.kt
index a303da0dae1a..49c06bf77fb6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractorTest.kt
@@ -143,6 +143,7 @@ class VolumeDialogVisibilityInteractorTest : SysuiTestCase() {
assertThat(visibilityModel)
.isInstanceOf(VolumeDialogVisibilityModel.Visible::class.java)
+ assertThat(fakeVolumeDialogController.hasUserActivity).isTrue()
}
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModelTest.kt
new file mode 100644
index 000000000000..0847281d9b42
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModelTest.kt
@@ -0,0 +1,82 @@
+/*
+ * 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.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.accessibility.data.repository.accessibilityRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.volume.Events
+import com.android.systemui.volume.dialog.data.repository.volumeDialogVisibilityRepository
+import com.android.systemui.volume.dialog.domain.interactor.volumeDialogVisibilityInteractor
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel
+import com.google.common.truth.Truth.assertThat
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.test.advanceTimeBy
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class VolumeDialogPluginViewModelTest : SysuiTestCase() {
+
+ private val kosmos: Kosmos = testKosmos()
+
+ private val underTest: VolumeDialogPluginViewModel by lazy {
+ kosmos.volumeDialogPluginViewModel
+ }
+
+ @Before
+ fun setUp() =
+ with(kosmos) {
+ volumeDialogVisibilityRepository.updateVisibility {
+ VolumeDialogVisibilityModel.Visible(Events.SHOW_REASON_VOLUME_CHANGED, false, 0)
+ }
+ }
+
+ @Test
+ fun safetyWarningAppears_timeoutReset() =
+ kosmos.runTest {
+ accessibilityRepository.setRecommendedTimeout(3.seconds)
+ val visibility by collectLastValue(volumeDialogVisibilityInteractor.dialogVisibility)
+ testScope.advanceTimeBy(2.seconds)
+ assertThat(visibility).isInstanceOf(VolumeDialogVisibilityModel.Visible::class.java)
+
+ underTest.onSafetyWarningDialogShown()
+ testScope.advanceTimeBy(2.seconds)
+ assertThat(visibility).isInstanceOf(VolumeDialogVisibilityModel.Visible::class.java)
+ }
+
+ @Test
+ fun csdWarningAppears_timeoutReset() =
+ kosmos.runTest {
+ accessibilityRepository.setRecommendedTimeout(3.seconds)
+ val visibility by collectLastValue(volumeDialogVisibilityInteractor.dialogVisibility)
+ testScope.advanceTimeBy(2.seconds)
+ assertThat(visibility).isInstanceOf(VolumeDialogVisibilityModel.Visible::class.java)
+
+ underTest.onCsdWarningDialogShown()
+ testScope.advanceTimeBy(2.seconds)
+ assertThat(visibility).isInstanceOf(VolumeDialogVisibilityModel.Visible::class.java)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
index 60a15915fb77..046b5d701ddf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
@@ -57,6 +57,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import org.junit.Before;
import org.junit.Test;
@@ -89,6 +90,8 @@ public class ImageWallpaperTest extends SysuiTestCase {
private Context mMockContext;
@Mock
private UserTracker mUserTracker;
+ @Mock
+ private WindowManagerProvider mWindowManagerProvider;
@Mock
private Bitmap mWallpaperBitmap;
@@ -105,7 +108,7 @@ public class ImageWallpaperTest extends SysuiTestCase {
when(mWindowMetrics.getBounds()).thenReturn(
new Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT));
when(mWindowManager.getCurrentWindowMetrics()).thenReturn(mWindowMetrics);
- when(mMockContext.getSystemService(WindowManager.class)).thenReturn(mWindowManager);
+ when(mWindowManagerProvider.getWindowManager(any())).thenReturn(mWindowManager);
// set up display manager
doNothing().when(mDisplayManager).registerDisplayListener(any(), any());
@@ -182,7 +185,7 @@ public class ImageWallpaperTest extends SysuiTestCase {
}
private ImageWallpaper createImageWallpaper() {
- return new ImageWallpaper(mFakeExecutor, mUserTracker) {
+ return new ImageWallpaper(mFakeExecutor, mUserTracker, mWindowManagerProvider) {
@Override
public Engine onCreateEngine() {
return new CanvasEngine() {
diff --git a/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml
index 43808f215a81..4002f7808637 100644
--- a/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml
+++ b/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml
@@ -21,6 +21,9 @@
style="@style/AuthCredentialPanelStyle"
android:layout_width="0dp"
android:layout_height="0dp"
+ android:accessibilityLiveRegion="assertive"
+ android:importantForAccessibility="yes"
+ android:clickable="false"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/rightGuideline"
app:layout_constraintStart_toStartOf="@id/leftGuideline"
diff --git a/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml
index 51117a7845df..3c8cb6860a41 100644
--- a/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml
+++ b/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml
@@ -22,6 +22,9 @@ android:layout_height="match_parent">
style="@style/AuthCredentialPanelStyle"
android:layout_width="0dp"
android:layout_height="0dp"
+ android:accessibilityLiveRegion="assertive"
+ android:importantForAccessibility="yes"
+ android:clickable="false"
android:paddingHorizontal="16dp"
android:paddingVertical="16dp"
android:visibility="visible"
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index a1fa54cf592d..e094155f9041 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -16,17 +16,18 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/volume_dialog"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="match_parent"
android:alpha="0"
- android:clipChildren="false">
+ android:clipChildren="false"
+ android:minWidth="@dimen/volume_dialog_window_width">
<View
android:id="@+id/volume_dialog_background"
android:layout_width="@dimen/volume_dialog_width"
android:layout_height="0dp"
android:layout_marginTop="@dimen/volume_dialog_background_top_margin"
- android:layout_marginBottom="@dimen/volume_dialog_background_vertical_margin"
+ android:layout_marginBottom="@dimen/volume_dialog_background_margin_negative"
android:background="@drawable/volume_dialog_background"
app:layout_constraintBottom_toBottomOf="@id/volume_dialog_bottom_section_container"
app:layout_constraintEnd_toEndOf="@id/volume_dialog_main_slider_container"
diff --git a/packages/SystemUI/res/layout/volume_dialog_slider_floating.xml b/packages/SystemUI/res/layout/volume_dialog_slider_floating.xml
index db800aa4a873..1dba96cafb70 100644
--- a/packages/SystemUI/res/layout/volume_dialog_slider_floating.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_slider_floating.xml
@@ -17,8 +17,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/volume_dialog_floating_slider_background"
- android:paddingHorizontal="@dimen/volume_dialog_floating_sliders_horizontal_padding"
- android:paddingVertical="@dimen/volume_dialog_floating_sliders_vertical_padding">
+ android:paddingHorizontal="@dimen/volume_dialog_floating_sliders_padding"
+ android:paddingVertical="@dimen/volume_dialog_floating_sliders_padding">
<include layout="@layout/volume_dialog_slider" />
</FrameLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_dialog_top_section.xml b/packages/SystemUI/res/layout/volume_dialog_top_section.xml
index b7455471d9a6..9d3598800bbf 100644
--- a/packages/SystemUI/res/layout/volume_dialog_top_section.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_top_section.xml
@@ -22,15 +22,15 @@
android:clipChildren="false"
android:clipToPadding="false"
android:gravity="center"
- android:paddingEnd="@dimen/volume_dialog_buttons_margin"
+ android:paddingEnd="@dimen/volume_dialog_background_margin"
app:layoutDescription="@xml/volume_dialog_ringer_drawer_motion_scene">
<View
android:id="@+id/ringer_buttons_background"
android:layout_width="@dimen/volume_dialog_width"
android:layout_height="0dp"
- android:layout_marginTop="@dimen/volume_dialog_background_vertical_margin"
- android:layout_marginBottom="@dimen/volume_dialog_background_vertical_margin"
+ android:layout_marginTop="@dimen/volume_dialog_background_margin_negative"
+ android:layout_marginBottom="@dimen/volume_dialog_background_margin_negative"
android:background="@drawable/volume_dialog_ringer_background"
android:visibility="gone" />
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 7d983068f34e..55e94028b95e 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -2158,28 +2158,25 @@
<dimen name="contextual_edu_dialog_elevation">2dp</dimen>
<!-- Volume start -->
+ <dimen name="volume_dialog_window_width">176dp</dimen>
<dimen name="volume_dialog_width">60dp</dimen>
<dimen name="volume_dialog_background_corner_radius">30dp</dimen>
- <dimen name="volume_dialog_background_vertical_margin">
- @dimen/volume_dialog_buttons_margin_negative
- </dimen>
<!-- top margin covers half the ringer button + components spacing -->
<dimen name="volume_dialog_background_top_margin">-28dp</dimen>
+ <dimen name="volume_dialog_background_margin">10dp</dimen>
+ <dimen name="volume_dialog_background_margin_negative">-10dp</dimen>
- <dimen name="volume_dialog_window_margin">14dp</dimen>
+ <dimen name="volume_dialog_window_margin">12dp</dimen>
<dimen name="volume_dialog_components_spacing">10dp</dimen>
<dimen name="volume_dialog_floating_sliders_spacing">8dp</dimen>
- <dimen name="volume_dialog_floating_sliders_vertical_padding">10dp</dimen>
<dimen name="volume_dialog_floating_sliders_vertical_padding_negative">
- @dimen/volume_dialog_buttons_margin_negative
+ @dimen/volume_dialog_background_margin_negative
</dimen>
- <dimen name="volume_dialog_floating_sliders_horizontal_padding">4dp</dimen>
+ <dimen name="volume_dialog_floating_sliders_padding">4dp</dimen>
<dimen name="volume_dialog_button_size">40dp</dimen>
<dimen name="volume_dialog_slider_width">52dp</dimen>
<dimen name="volume_dialog_slider_height">254dp</dimen>
- <dimen name="volume_dialog_buttons_margin">10dp</dimen>
- <dimen name="volume_dialog_buttons_margin_negative">-10dp</dimen>
<!--
A primary goal of this margin is to vertically constraint slider height in the landscape
orientation when the vertical space is limited
@@ -2190,8 +2187,10 @@
<dimen name="volume_dialog_background_square_corner_radius">12dp</dimen>
- <dimen name="volume_dialog_ringer_drawer_margin">@dimen/volume_dialog_buttons_margin</dimen>
<dimen name="volume_dialog_ringer_drawer_button_size">@dimen/volume_dialog_button_size</dimen>
+ <dimen name="volume_dialog_ringer_drawer_buttons_spacing">
+ @dimen/volume_dialog_components_spacing
+ </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/biometrics/Android.bp b/packages/SystemUI/shared/biometrics/Android.bp
index 63de81d4a680..208157c69adf 100644
--- a/packages/SystemUI/shared/biometrics/Android.bp
+++ b/packages/SystemUI/shared/biometrics/Android.bp
@@ -14,6 +14,9 @@ android_library {
"src/**/*.java",
"src/**/*.kt",
],
+ static_libs: [
+ "SystemUI-shared-utils",
+ ],
resource_dirs: [
"res",
],
diff --git a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt
index 5b99a3f16fc2..7aa09cf64405 100644
--- a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt
+++ b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt
@@ -41,6 +41,7 @@ import com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD
import com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN
import com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN
import com.android.systemui.biometrics.shared.model.PromptKind
+import com.android.systemui.utils.windowmanager.WindowManagerUtils
object Utils {
private const val TAG = "SysUIBiometricUtils"
@@ -117,10 +118,9 @@ object Utils {
@JvmStatic
fun getNavbarInsets(context: Context): Insets {
- val windowManager: WindowManager? = context.getSystemService(WindowManager::class.java)
- val windowMetrics: WindowMetrics? = windowManager?.maximumWindowMetrics
- return windowMetrics?.windowInsets?.getInsets(WindowInsets.Type.navigationBars())
- ?: Insets.NONE
+ val windowManager: WindowManager = WindowManagerUtils.getWindowManager(context)
+ val windowMetrics: WindowMetrics = windowManager.maximumWindowMetrics
+ return windowMetrics.windowInsets.getInsets(WindowInsets.Type.navigationBars())
}
/** Converts `drawable` to a [Bitmap]. */
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
index c82243934b8b..b1fc560f406b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
@@ -37,6 +37,7 @@ import android.view.Surface;
import android.view.WindowManager;
import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.utils.windowmanager.WindowManagerUtils;
/* Common code */
public class Utilities {
@@ -152,7 +153,7 @@ public class Utilities {
/** @return whether or not {@param context} represents that of a large screen device or not */
@TargetApi(Build.VERSION_CODES.R)
public static boolean isLargeScreen(Context context) {
- return isLargeScreen(context.getSystemService(WindowManager.class), context.getResources());
+ return isLargeScreen(WindowManagerUtils.getWindowManager(context), context.getResources());
}
/** @return whether or not {@param context} represents that of a large screen device or not */
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
index 4db6ab6ea579..570d774b95e9 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
@@ -16,9 +16,6 @@
package com.android.systemui.shared.rotation;
-import static com.android.app.viewcapture.ViewCaptureFactory.getViewCaptureAwareWindowManagerInstance;
-import static com.android.systemui.Flags.enableViewCaptureTracing;
-
import android.annotation.DimenRes;
import android.annotation.IdRes;
import android.annotation.LayoutRes;
@@ -33,6 +30,7 @@ import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.FrameLayout;
@@ -40,8 +38,8 @@ import android.widget.FrameLayout;
import androidx.annotation.BoolRes;
import androidx.core.view.OneShotPreDrawListener;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.systemui.shared.rotation.FloatingRotationButtonPositionCalculator.Position;
+import com.android.systemui.utils.windowmanager.WindowManagerUtils;
/**
* Containing logic for the rotation button on the physical left bottom corner of the screen.
@@ -50,7 +48,7 @@ public class FloatingRotationButton implements RotationButton {
private static final int MARGIN_ANIMATION_DURATION_MILLIS = 300;
- private final ViewCaptureAwareWindowManager mWindowManager;
+ private final WindowManager mWindowManager;
private final ViewGroup mKeyButtonContainer;
private final FloatingRotationButtonView mKeyButtonView;
@@ -91,8 +89,7 @@ public class FloatingRotationButton implements RotationButton {
@DimenRes int taskbarBottomMargin, @DimenRes int buttonDiameter,
@DimenRes int rippleMaxWidth, @BoolRes int floatingRotationBtnPositionLeftResource) {
mContext = context;
- mWindowManager = getViewCaptureAwareWindowManagerInstance(mContext,
- enableViewCaptureTracing());
+ mWindowManager = WindowManagerUtils.getWindowManager(mContext);
mKeyButtonContainer = (ViewGroup) LayoutInflater.from(mContext).inflate(layout, null);
mKeyButtonView = mKeyButtonContainer.findViewById(keyButtonId);
mKeyButtonView.setVisibility(View.VISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 19da5de6b531..59ec6923ce91 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -66,7 +66,6 @@ import android.widget.FrameLayout;
import androidx.annotation.VisibleForTesting;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.util.Preconditions;
import com.android.settingslib.Utils;
import com.android.systemui.biometrics.data.repository.FacePropertyRepository;
@@ -167,7 +166,7 @@ public class ScreenDecorations implements
ViewGroup mScreenDecorHwcWindow;
@VisibleForTesting
ScreenDecorHwcLayer mScreenDecorHwcLayer;
- private ViewCaptureAwareWindowManager mWindowManager;
+ private WindowManager mWindowManager;
private int mRotation;
private UserSettingObserver mColorInversionSetting;
private DelayableExecutor mExecutor;
@@ -337,7 +336,7 @@ public class ScreenDecorations implements
FacePropertyRepository facePropertyRepository,
JavaAdapter javaAdapter,
CameraProtectionLoader cameraProtectionLoader,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
+ WindowManager windowManager,
@ScreenDecorationsThread Handler handler,
@ScreenDecorationsThread DelayableExecutor executor) {
mContext = context;
@@ -353,7 +352,7 @@ public class ScreenDecorations implements
mLogger = logger;
mFacePropertyRepository = facePropertyRepository;
mJavaAdapter = javaAdapter;
- mWindowManager = viewCaptureAwareWindowManager;
+ mWindowManager = windowManager;
mHandler = handler;
mExecutor = executor;
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
index 115242eb13aa..375137c67f7c 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
@@ -44,7 +44,6 @@ import android.window.InputTransferToken;
import androidx.annotation.NonNull;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.dagger.SysUISingleton;
@@ -54,6 +53,7 @@ import com.android.systemui.recents.LauncherProxyService;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.util.settings.SecureSettings;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import java.io.PrintWriter;
import java.util.concurrent.Executor;
@@ -97,20 +97,19 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks
private final WindowMagnifierCallback mWindowMagnifierCallback;
private final SysUiState mSysUiState;
private final SecureSettings mSecureSettings;
- private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
+ private final WindowManagerProvider mWindowManagerProvider;
WindowMagnificationControllerSupplier(Context context, Handler handler,
WindowMagnifierCallback windowMagnifierCallback,
DisplayManager displayManager, SysUiState sysUiState,
- SecureSettings secureSettings,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
+ SecureSettings secureSettings, WindowManagerProvider windowManagerProvider) {
super(displayManager);
mContext = context;
mHandler = handler;
mWindowMagnifierCallback = windowMagnifierCallback;
mSysUiState = sysUiState;
mSecureSettings = secureSettings;
- mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager;
+ mWindowManagerProvider = windowManagerProvider;
}
@Override
@@ -118,6 +117,8 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks
final Context windowContext = mContext.createWindowContext(display,
TYPE_ACCESSIBILITY_OVERLAY,
/* options */ null);
+ final WindowManager windowManager = mWindowManagerProvider
+ .getWindowManager(windowContext);
windowContext.setTheme(com.android.systemui.res.R.style.Theme_SystemUI);
Supplier<SurfaceControlViewHost> scvhSupplier = () ->
@@ -133,7 +134,8 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks
mWindowMagnifierCallback,
mSysUiState,
mSecureSettings,
- scvhSupplier);
+ scvhSupplier,
+ windowManager);
}
}
@@ -148,17 +150,20 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks
private final Executor mExecutor;
private final DisplayManager mDisplayManager;
private final IWindowManager mIWindowManager;
+ private final WindowManagerProvider mWindowManagerProvider;
FullscreenMagnificationControllerSupplier(Context context,
DisplayManager displayManager,
Handler handler,
- Executor executor, IWindowManager iWindowManager) {
+ Executor executor, IWindowManager iWindowManager,
+ WindowManagerProvider windowManagerProvider) {
super(displayManager);
mContext = context;
mHandler = handler;
mExecutor = executor;
mDisplayManager = displayManager;
mIWindowManager = iWindowManager;
+ mWindowManagerProvider = windowManagerProvider;
}
@Override
@@ -174,7 +179,7 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks
mExecutor,
mDisplayManager,
windowContext.getSystemService(AccessibilityManager.class),
- windowContext.getSystemService(WindowManager.class),
+ mWindowManagerProvider.getWindowManager(windowContext),
mIWindowManager,
scvhSupplier);
}
@@ -190,31 +195,32 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks
private final Context mContext;
private final MagnificationSettingsController.Callback mSettingsControllerCallback;
private final SecureSettings mSecureSettings;
- private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
+ private final WindowManagerProvider mWindowManagerProvider;
SettingsSupplier(Context context,
MagnificationSettingsController.Callback settingsControllerCallback,
DisplayManager displayManager,
- SecureSettings secureSettings,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
+ SecureSettings secureSettings, WindowManagerProvider windowManagerProvider) {
super(displayManager);
mContext = context;
mSettingsControllerCallback = settingsControllerCallback;
mSecureSettings = secureSettings;
- mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager;
+ mWindowManagerProvider = windowManagerProvider;
}
@Override
protected MagnificationSettingsController createInstance(Display display) {
final Context windowContext = mContext.createWindowContext(display,
TYPE_ACCESSIBILITY_OVERLAY, /* options */ null);
+ final WindowManager windowManager = mWindowManagerProvider
+ .getWindowManager(windowContext);
windowContext.setTheme(com.android.systemui.res.R.style.Theme_SystemUI);
return new MagnificationSettingsController(
windowContext,
new SfVsyncFrameCallbackProvider(),
mSettingsControllerCallback,
mSecureSettings,
- mViewCaptureAwareWindowManager);
+ windowManager);
}
}
@@ -229,11 +235,11 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks
SecureSettings secureSettings, DisplayTracker displayTracker,
DisplayManager displayManager, AccessibilityLogger a11yLogger,
IWindowManager iWindowManager, AccessibilityManager accessibilityManager,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
+ WindowManagerProvider windowManagerProvider) {
this(context, mainHandler.getLooper(), executor, commandQueue,
modeSwitchesController, sysUiState, launcherProxyService, secureSettings,
displayTracker, displayManager, a11yLogger, iWindowManager, accessibilityManager,
- viewCaptureAwareWindowManager);
+ windowManagerProvider);
}
@VisibleForTesting
@@ -244,7 +250,7 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks
DisplayManager displayManager, AccessibilityLogger a11yLogger,
IWindowManager iWindowManager,
AccessibilityManager accessibilityManager,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
+ WindowManagerProvider windowManagerProvider) {
mHandler = new Handler(looper) {
@Override
public void handleMessage(@NonNull Message msg) {
@@ -263,12 +269,13 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks
mA11yLogger = a11yLogger;
mWindowMagnificationControllerSupplier = new WindowMagnificationControllerSupplier(context,
mHandler, mWindowMagnifierCallback,
- displayManager, sysUiState, secureSettings, viewCaptureAwareWindowManager);
+ displayManager, sysUiState, secureSettings, windowManagerProvider);
mFullscreenMagnificationControllerSupplier = new FullscreenMagnificationControllerSupplier(
- context, displayManager, mHandler, mExecutor, iWindowManager);
+ context, displayManager, mHandler, mExecutor, iWindowManager,
+ windowManagerProvider);
mMagnificationSettingsSupplier = new SettingsSupplier(context,
mMagnificationSettingsControllerCallback, displayManager, secureSettings,
- viewCaptureAwareWindowManager);
+ windowManagerProvider);
mModeSwitchesController.setClickListenerDelegate(
displayId -> mHandler.post(() -> {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
index 4723ab958f86..9eb01de239bc 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
@@ -46,7 +46,6 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.widget.ImageView;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.res.R;
@@ -77,7 +76,6 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL
private final Context mContext;
private final AccessibilityManager mAccessibilityManager;
private final WindowManager mWindowManager;
- private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
private final ImageView mImageView;
private final Runnable mWindowInsetChangeRunnable;
private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
@@ -101,21 +99,20 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL
void onClick(int displayId);
}
- MagnificationModeSwitch(@UiContext Context context, ClickListener clickListener,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
- this(context, createView(context), new SfVsyncFrameCallbackProvider(), clickListener,
- viewCaptureAwareWindowManager);
+ MagnificationModeSwitch(@UiContext Context context, WindowManager windowManager,
+ ClickListener clickListener) {
+ this(context, windowManager, createView(context), new SfVsyncFrameCallbackProvider(),
+ clickListener);
}
@VisibleForTesting
- MagnificationModeSwitch(Context context, @NonNull ImageView imageView,
- SfVsyncFrameCallbackProvider sfVsyncFrameProvider, ClickListener clickListener,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
+ MagnificationModeSwitch(Context context, WindowManager windowManager,
+ @NonNull ImageView imageView, SfVsyncFrameCallbackProvider sfVsyncFrameProvider,
+ ClickListener clickListener) {
mContext = context;
mConfiguration = new Configuration(context.getResources().getConfiguration());
mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
- mWindowManager = mContext.getSystemService(WindowManager.class);
- mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager;
+ mWindowManager = windowManager;
mSfVsyncFrameProvider = sfVsyncFrameProvider;
mClickListener = clickListener;
mParams = createLayoutParams(context);
@@ -282,7 +279,7 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL
mImageView.animate().cancel();
mIsFadeOutAnimating = false;
mImageView.setAlpha(0f);
- mViewCaptureAwareWindowManager.removeView(mImageView);
+ mWindowManager.removeView(mImageView);
mContext.unregisterComponentCallbacks(this);
mIsVisible = false;
}
@@ -316,7 +313,7 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL
mParams.y = mDraggableWindowBounds.bottom;
mToLeftScreenEdge = false;
}
- mViewCaptureAwareWindowManager.addView(mImageView, mParams);
+ mWindowManager.addView(mImageView, mParams);
// Exclude magnification switch button from system gesture area.
setSystemGestureExclusion();
mIsVisible = true;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationSettingsController.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationSettingsController.java
index fc7535a712e3..2d5dc8d23383 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationSettingsController.java
@@ -26,7 +26,6 @@ import android.content.res.Configuration;
import android.util.Range;
import android.view.WindowManager;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.accessibility.common.MagnificationConstants;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
@@ -62,9 +61,9 @@ public class MagnificationSettingsController implements ComponentCallbacks {
SfVsyncFrameCallbackProvider sfVsyncFrameProvider,
@NonNull Callback settingsControllerCallback,
SecureSettings secureSettings,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
- this(context, sfVsyncFrameProvider, settingsControllerCallback, secureSettings, null,
- viewCaptureAwareWindowManager);
+ WindowManager windowManager) {
+ this(context, sfVsyncFrameProvider, settingsControllerCallback, secureSettings,
+ windowManager, null);
}
@VisibleForTesting
@@ -73,8 +72,8 @@ public class MagnificationSettingsController implements ComponentCallbacks {
SfVsyncFrameCallbackProvider sfVsyncFrameProvider,
@NonNull Callback settingsControllerCallback,
SecureSettings secureSettings,
- WindowMagnificationSettings windowMagnificationSettings,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
+ WindowManager windowManager,
+ WindowMagnificationSettings windowMagnificationSettings) {
mContext = context.createWindowContext(
context.getDisplay(),
WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
@@ -88,7 +87,7 @@ public class MagnificationSettingsController implements ComponentCallbacks {
} else {
mWindowMagnificationSettings = new WindowMagnificationSettings(mContext,
mWindowMagnificationSettingsCallback,
- sfVsyncFrameProvider, secureSettings, viewCaptureAwareWindowManager);
+ sfVsyncFrameProvider, secureSettings, windowManager);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MirrorWindowControl.java b/packages/SystemUI/src/com/android/systemui/accessibility/MirrorWindowControl.java
index eb4de6837d41..7f3a869d8222 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MirrorWindowControl.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MirrorWindowControl.java
@@ -18,9 +18,6 @@ package com.android.systemui.accessibility;
import static android.view.WindowManager.LayoutParams;
-import static com.android.app.viewcapture.ViewCaptureFactory.getViewCaptureAwareWindowManagerInstance;
-import static com.android.systemui.Flags.enableViewCaptureTracing;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -32,8 +29,8 @@ import android.util.MathUtils;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.WindowManager;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.systemui.res.R;
/**
@@ -73,12 +70,11 @@ public abstract class MirrorWindowControl {
* @see #setDefaultPosition(LayoutParams)
*/
private final Point mControlPosition = new Point();
- private final ViewCaptureAwareWindowManager mWindowManager;
+ private final WindowManager mWindowManager;
- MirrorWindowControl(Context context) {
+ MirrorWindowControl(Context context, WindowManager windowManager) {
mContext = context;
- mWindowManager = getViewCaptureAwareWindowManagerInstance(mContext,
- enableViewCaptureTracing());
+ mWindowManager = windowManager;
}
public void setWindowDelegate(@Nullable MirrorWindowDelegate windowDelegate) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java b/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java
index 53827e65344a..7d9f8674457c 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java
@@ -24,10 +24,11 @@ import android.annotation.MainThread;
import android.content.Context;
import android.hardware.display.DisplayManager;
import android.view.Display;
+import android.view.WindowManager;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import javax.inject.Inject;
@@ -49,9 +50,9 @@ public class ModeSwitchesController implements ClickListener {
@Inject
public ModeSwitchesController(Context context, DisplayManager displayManager,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
+ WindowManagerProvider windowManagerProvider) {
mSwitchSupplier = new SwitchSupplier(context, displayManager, this::onClick,
- viewCaptureAwareWindowManager);
+ windowManagerProvider);
}
@VisibleForTesting
@@ -118,7 +119,7 @@ public class ModeSwitchesController implements ClickListener {
private final Context mContext;
private final ClickListener mClickListener;
- private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
+ private final WindowManagerProvider mWindowManagerProvider;
/**
* Supplies the switch for the given display.
@@ -128,20 +129,20 @@ public class ModeSwitchesController implements ClickListener {
* @param clickListener The callback that will run when the switch is clicked
*/
SwitchSupplier(Context context, DisplayManager displayManager,
- ClickListener clickListener,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
+ ClickListener clickListener, WindowManagerProvider windowManagerProvider) {
super(displayManager);
mContext = context;
mClickListener = clickListener;
- mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager;
+ mWindowManagerProvider = windowManagerProvider;
}
@Override
protected MagnificationModeSwitch createInstance(Display display) {
final Context uiContext = mContext.createWindowContext(display,
TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, /* options */ null);
- return new MagnificationModeSwitch(uiContext, mClickListener,
- mViewCaptureAwareWindowManager);
+ final WindowManager uiWindowManager = mWindowManagerProvider
+ .getWindowManager(uiContext);
+ return new MagnificationModeSwitch(uiContext, uiWindowManager, mClickListener);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SimpleMirrorWindowControl.java b/packages/SystemUI/src/com/android/systemui/accessibility/SimpleMirrorWindowControl.java
index bc469eed7359..3cde033bf56a 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SimpleMirrorWindowControl.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SimpleMirrorWindowControl.java
@@ -27,6 +27,7 @@ import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
+import android.view.WindowManager;
import com.android.systemui.res.R;
@@ -48,8 +49,8 @@ class SimpleMirrorWindowControl extends MirrorWindowControl implements View.OnCl
private final PointF mLastDrag = new PointF();
private final Handler mHandler;
- SimpleMirrorWindowControl(Context context, Handler handler) {
- super(context);
+ SimpleMirrorWindowControl(Context context, Handler handler, WindowManager windowManager) {
+ super(context, windowManager);
mHandler = handler;
final Resources resource = context.getResources();
mMoveFrameAmountShort = resource.getDimensionPixelSize(
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 8734d05bc894..9cd77e790e8c 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -249,7 +249,8 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
@NonNull WindowMagnifierCallback callback,
SysUiState sysUiState,
SecureSettings secureSettings,
- Supplier<SurfaceControlViewHost> scvhSupplier) {
+ Supplier<SurfaceControlViewHost> scvhSupplier,
+ WindowManager windowManager) {
mContext = context;
mHandler = handler;
mAnimationController = animationController;
@@ -265,7 +266,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
mDisplayId = mContext.getDisplayId();
mRotation = display.getRotation();
- mWm = context.getSystemService(WindowManager.class);
+ mWm = windowManager;
mWindowBounds = new Rect(mWm.getCurrentWindowMetrics().getBounds());
mResources = mContext.getResources();
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
index 3b6f8f87a1a8..bd4b6420bf37 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
@@ -56,7 +56,6 @@ import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.Switch;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView;
@@ -75,7 +74,6 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest
private final Context mContext;
private final AccessibilityManager mAccessibilityManager;
private final WindowManager mWindowManager;
- private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
private final SecureSettings mSecureSettings;
private final Runnable mWindowInsetChangeRunnable;
@@ -137,11 +135,10 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest
@VisibleForTesting
WindowMagnificationSettings(Context context, WindowMagnificationSettingsCallback callback,
SfVsyncFrameCallbackProvider sfVsyncFrameProvider, SecureSettings secureSettings,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
+ WindowManager windowManager) {
mContext = context;
mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
- mWindowManager = mContext.getSystemService(WindowManager.class);
- mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager;
+ mWindowManager = windowManager;
mSfVsyncFrameProvider = sfVsyncFrameProvider;
mCallback = callback;
mSecureSettings = secureSettings;
@@ -324,7 +321,7 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest
// Unregister observer before removing view
mSecureSettings.unregisterContentObserverSync(mMagnificationCapabilityObserver);
- mViewCaptureAwareWindowManager.removeView(mSettingView);
+ mWindowManager.removeView(mSettingView);
mIsVisible = false;
if (resetPosition) {
mParams.x = 0;
@@ -382,7 +379,7 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest
mParams.y = mDraggableWindowBounds.bottom;
}
- mViewCaptureAwareWindowManager.addView(mSettingView, mParams);
+ mWindowManager.addView(mSettingView, mParams);
mSecureSettings.registerContentObserverForUserSync(
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY,
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
index f8e4bda15d01..ef42837ba776 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
@@ -31,7 +31,6 @@ import android.view.accessibility.IUserInitializationCompleteCallback;
import androidx.annotation.MainThread;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
@@ -60,7 +59,6 @@ public class AccessibilityFloatingMenuController implements
private final Context mContext;
private final WindowManager mWindowManager;
- private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
private final DisplayManager mDisplayManager;
private final AccessibilityManager mAccessibilityManager;
private final HearingAidDeviceManager mHearingAidDeviceManager;
@@ -105,7 +103,6 @@ public class AccessibilityFloatingMenuController implements
@Inject
public AccessibilityFloatingMenuController(Context context,
WindowManager windowManager,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
DisplayManager displayManager,
AccessibilityManager accessibilityManager,
AccessibilityButtonTargetsObserver accessibilityButtonTargetsObserver,
@@ -118,7 +115,6 @@ public class AccessibilityFloatingMenuController implements
@Main Handler handler) {
mContext = context;
mWindowManager = windowManager;
- mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager;
mDisplayManager = displayManager;
mAccessibilityManager = accessibilityManager;
mAccessibilityButtonTargetsObserver = accessibilityButtonTargetsObserver;
@@ -205,8 +201,8 @@ public class AccessibilityFloatingMenuController implements
final Context windowContext = mContext.createWindowContext(defaultDisplay,
TYPE_NAVIGATION_BAR_PANEL, /* options= */ null);
mFloatingMenu = new MenuViewLayerController(windowContext, mWindowManager,
- mViewCaptureAwareWindowManager, mAccessibilityManager, mSecureSettings,
- mNavigationModeController, mHearingAidDeviceManager);
+ mAccessibilityManager, mSecureSettings, mNavigationModeController,
+ mHearingAidDeviceManager);
}
mFloatingMenu.show();
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractView.kt b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractView.kt
index 13c1a450832f..52e69efdbc19 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractView.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractView.kt
@@ -48,7 +48,7 @@ import com.android.wm.shell.shared.bubbles.DismissView
*
* @note [setup] method should be called after initialisation
*/
-class DragToInteractView(context: Context) : FrameLayout(context) {
+class DragToInteractView(context: Context, windowManager: WindowManager) : FrameLayout(context) {
/**
* The configuration is used to provide module specific resource ids
*
@@ -86,8 +86,7 @@ class DragToInteractView(context: Context) : FrameLayout(context) {
private val spring = PhysicsAnimator.SpringConfig(STIFFNESS_LOW, DAMPING_RATIO_LOW_BOUNCY)
private val INTERACT_SCRIM_FADE_MS = 200L
- private var wm: WindowManager =
- context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
+ private var wm: WindowManager = windowManager
private var gradientDrawable: GradientDrawable? = null
private val GRADIENT_ALPHA: IntProperty<GradientDrawable> =
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
index 81095220b4a6..8fb260c2df36 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
@@ -212,7 +212,7 @@ class MenuViewLayer extends FrameLayout implements
mMenuAnimationController = mMenuView.getMenuAnimationController();
mMenuAnimationController.setSpringAnimationsEndAction(this::onSpringAnimationsEndAction);
mDismissView = new DismissView(context);
- mDragToInteractView = new DragToInteractView(context);
+ mDragToInteractView = new DragToInteractView(context, windowManager);
DismissViewUtils.setup(mDismissView);
mDismissView.getCircle().setId(R.id.action_remove_menu);
mNotificationFactory = new MenuNotificationFactory(context);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
index 102efcf7badd..7bf7e23b5df5 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
@@ -25,7 +25,6 @@ import android.graphics.PixelFormat;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.settingslib.bluetooth.HearingAidDeviceManager;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.util.settings.SecureSettings;
@@ -35,16 +34,15 @@ import com.android.systemui.util.settings.SecureSettings;
* of {@link IAccessibilityFloatingMenu}.
*/
class MenuViewLayerController implements IAccessibilityFloatingMenu {
- private final ViewCaptureAwareWindowManager mWindowManager;
+ private final WindowManager mWindowManager;
private final MenuViewLayer mMenuViewLayer;
private boolean mIsShowing;
MenuViewLayerController(Context context, WindowManager windowManager,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
AccessibilityManager accessibilityManager, SecureSettings secureSettings,
NavigationModeController navigationModeController,
HearingAidDeviceManager hearingAidDeviceManager) {
- mWindowManager = viewCaptureAwareWindowManager;
+ mWindowManager = windowManager;
MenuViewModel menuViewModel = new MenuViewModel(
context, accessibilityManager, secureSettings, hearingAidDeviceManager);
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java b/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java
index f4e2b82f1773..14ce2cf60919 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java
@@ -33,7 +33,6 @@ import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import com.android.app.animation.Interpolators;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.systemui.res.R;
/**
@@ -41,17 +40,16 @@ import com.android.systemui.res.R;
*/
public class AssistDisclosure {
private final Context mContext;
- private final ViewCaptureAwareWindowManager mWm;
+ private final WindowManager mWm;
private final Handler mHandler;
private AssistDisclosureView mView;
private boolean mViewAdded;
- public AssistDisclosure(Context context, Handler handler,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
+ public AssistDisclosure(Context context, Handler handler, WindowManager windowManager) {
mContext = context;
mHandler = handler;
- mWm = viewCaptureAwareWindowManager;
+ mWm = windowManager;
}
public void postShow() {
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index 2d44d401b0b0..75ec8dfd881e 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -24,8 +24,8 @@ import android.provider.Settings;
import android.service.voice.VisualQueryAttentionResult;
import android.service.voice.VoiceInteractionSession;
import android.util.Log;
+import android.view.WindowManager;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.app.AssistUtils;
import com.android.internal.app.IVisualQueryDetectionAttentionListener;
import com.android.internal.app.IVisualQueryRecognitionStatusListener;
@@ -199,12 +199,12 @@ public class AssistManager {
SelectedUserInteractor selectedUserInteractor,
ActivityManager activityManager,
AssistInteractor interactor,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
+ WindowManager windowManager) {
mContext = context;
mDeviceProvisionedController = controller;
mCommandQueue = commandQueue;
mAssistUtils = assistUtils;
- mAssistDisclosure = new AssistDisclosure(context, uiHandler, viewCaptureAwareWindowManager);
+ mAssistDisclosure = new AssistDisclosure(context, uiHandler, windowManager);
mLauncherProxyService = launcherProxyService;
mPhoneStateMonitor = phoneStateMonitor;
mAssistLogger = assistLogger;
diff --git a/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java b/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java
index 6e257442d139..56273eb9a2cf 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java
@@ -33,7 +33,6 @@ import android.view.WindowManager;
import android.view.animation.PathInterpolator;
import android.widget.FrameLayout;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.assist.AssistLogger;
@@ -67,7 +66,7 @@ public class DefaultUiController implements AssistManager.UiController {
protected InvocationLightsView mInvocationLightsView;
protected final AssistLogger mAssistLogger;
- private final ViewCaptureAwareWindowManager mWindowManager;
+ private final WindowManager mWindowManager;
private final MetricsLogger mMetricsLogger;
private final Lazy<AssistManager> mAssistManagerLazy;
private final WindowManager.LayoutParams mLayoutParams;
@@ -81,12 +80,12 @@ public class DefaultUiController implements AssistManager.UiController {
@Inject
public DefaultUiController(Context context, AssistLogger assistLogger,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
- MetricsLogger metricsLogger, Lazy<AssistManager> assistManagerLazy,
+ WindowManager windowManager, MetricsLogger metricsLogger,
+ Lazy<AssistManager> assistManagerLazy,
NavigationBarController navigationBarController) {
mAssistLogger = assistLogger;
mRoot = new FrameLayout(context);
- mWindowManager = viewCaptureAwareWindowManager;
+ mWindowManager = windowManager;
mMetricsLogger = metricsLogger;
mAssistManagerLazy = assistManagerLazy;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index b8e95ee1dbf0..7bb08edd4773 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -20,7 +20,6 @@ import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_BIOMETRIC_PROMPT_TRANSITION;
-import static com.android.systemui.Flags.enableViewCaptureTracing;
import android.animation.Animator;
import android.annotation.IntDef;
@@ -57,8 +56,6 @@ import android.window.OnBackInvokedDispatcher;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.android.app.animation.Interpolators;
-import com.android.app.viewcapture.ViewCapture;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.widget.LockPatternUtils;
@@ -78,11 +75,10 @@ import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.utils.windowmanager.WindowManagerUtils;
import com.google.android.msdl.domain.MSDLPlayer;
-import kotlin.Lazy;
-
import kotlinx.coroutines.CoroutineScope;
import java.io.PrintWriter;
@@ -132,7 +128,7 @@ public class AuthContainerView extends LinearLayout
private final Config mConfig;
private final int mEffectiveUserId;
private final IBinder mWindowToken = new Binder();
- private final ViewCaptureAwareWindowManager mWindowManager;
+ private final WindowManager mWindowManager;
@Nullable private final AuthContextPlugins mAuthContextPlugins;
private final Interpolator mLinearOutSlowIn;
private final LockPatternUtils mLockPatternUtils;
@@ -298,16 +294,13 @@ public class AuthContainerView extends LinearLayout
@NonNull Provider<CredentialViewModel> credentialViewModelProvider,
@NonNull @Background DelayableExecutor bgExecutor,
@NonNull VibratorHelper vibratorHelper,
- Lazy<ViewCapture> lazyViewCapture,
@NonNull MSDLPlayer msdlPlayer) {
super(config.mContext);
mConfig = config;
mLockPatternUtils = lockPatternUtils;
mEffectiveUserId = userManager.getCredentialOwnerProfile(mConfig.mUserId);
- WindowManager wm = getContext().getSystemService(WindowManager.class);
- mWindowManager = new ViewCaptureAwareWindowManager(wm, lazyViewCapture,
- enableViewCaptureTracing());
+ mWindowManager = WindowManagerUtils.getWindowManager(getContext());
mAuthContextPlugins = authContextPlugins;
mWakefulnessLifecycle = wakefulnessLifecycle;
mApplicationCoroutineScope = applicationCoroutineScope;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 68a282018ba4..f2c071f14466 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -22,7 +22,6 @@ import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_REAR
import static android.view.Display.INVALID_DISPLAY;
import static com.android.systemui.Flags.contAuthPlugin;
-import static com.android.systemui.util.ConvenienceExtensionsKt.toKotlinLazy;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -67,7 +66,6 @@ import android.view.DisplayInfo;
import android.view.MotionEvent;
import android.view.WindowManager;
-import com.android.app.viewcapture.ViewCapture;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.InteractionJankMonitor;
@@ -93,6 +91,7 @@ import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.Execution;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import com.google.android.msdl.domain.MSDLPlayer;
@@ -194,7 +193,7 @@ public class AuthController implements
@NonNull private final VibratorHelper mVibratorHelper;
@NonNull private final MSDLPlayer mMSDLPlayer;
- private final kotlin.Lazy<ViewCapture> mLazyViewCapture;
+ private final WindowManagerProvider mWindowManagerProvider;
@VisibleForTesting
final TaskStackListener mTaskStackListener = new TaskStackListener() {
@@ -693,8 +692,8 @@ public class AuthController implements
@NonNull UdfpsUtils udfpsUtils,
@NonNull VibratorHelper vibratorHelper,
@NonNull KeyguardManager keyguardManager,
- Lazy<ViewCapture> daggerLazyViewCapture,
- @NonNull MSDLPlayer msdlPlayer) {
+ @NonNull MSDLPlayer msdlPlayer,
+ WindowManagerProvider windowManagerProvider) {
mContext = context;
mExecution = execution;
mUserManager = userManager;
@@ -755,7 +754,7 @@ public class AuthController implements
context.registerReceiver(mBroadcastReceiver, filter, Context.RECEIVER_EXPORTED_UNAUDITED);
mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class);
- mLazyViewCapture = toKotlinLazy(daggerLazyViewCapture);
+ mWindowManagerProvider = windowManagerProvider;
}
// TODO(b/229290039): UDFPS controller should manage its dimensions on its own. Remove this.
@@ -1278,7 +1277,7 @@ public class AuthController implements
Log.e(TAG, "unable to get Display for user=" + userId);
return null;
}
- return mContext.createDisplayContext(display).getSystemService(WindowManager.class);
+ return mWindowManagerProvider.getWindowManager(mContext.createDisplayContext(display));
}
private void onDialogDismissed(@BiometricPrompt.DismissedReason int reason) {
@@ -1337,8 +1336,7 @@ public class AuthController implements
return new AuthContainerView(config, mApplicationCoroutineScope, mFpProps, mFaceProps,
wakefulnessLifecycle, userManager, mContextPlugins, lockPatternUtils,
mInteractionJankMonitor, mPromptSelectorInteractor, viewModel,
- mCredentialViewModelProvider, bgExecutor, mVibratorHelper,
- mLazyViewCapture, mMSDLPlayer);
+ mCredentialViewModelProvider, bgExecutor, mVibratorHelper, mMSDLPlayer);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 659d3b46fea9..b16c416fb9df 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -57,12 +57,12 @@ import android.view.HapticFeedbackConstants;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
+import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.InstanceId;
@@ -145,7 +145,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
private final Execution mExecution;
private final FingerprintManager mFingerprintManager;
@NonNull private final LayoutInflater mInflater;
- private final ViewCaptureAwareWindowManager mWindowManager;
+ private final WindowManager mWindowManager;
private final DelayableExecutor mFgExecutor;
@NonNull private final Executor mBiometricExecutor;
@NonNull private final StatusBarStateController mStatusBarStateController;
@@ -659,7 +659,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
@NonNull Execution execution,
@NonNull @ShadeDisplayAware LayoutInflater inflater,
@Nullable FingerprintManager fingerprintManager,
- @NonNull ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
+ @NonNull @Main WindowManager windowManager,
@NonNull StatusBarStateController statusBarStateController,
@Main DelayableExecutor fgExecutor,
@NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager,
@@ -705,7 +705,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
// The fingerprint manager is queried for UDFPS before this class is constructed, so the
// fingerprint manager should never be null.
mFingerprintManager = checkNotNull(fingerprintManager);
- mWindowManager = viewCaptureAwareWindowManager;
+ mWindowManager = windowManager;
mFgExecutor = fgExecutor;
mStatusBarStateController = statusBarStateController;
mKeyguardStateController = keyguardStateController;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index bdf58275effa..61b670715572 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -42,7 +42,6 @@ import android.view.accessibility.AccessibilityManager
import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener
import androidx.annotation.VisibleForTesting
import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
@@ -88,7 +87,7 @@ class UdfpsControllerOverlay
constructor(
private val context: Context,
private val inflater: LayoutInflater,
- private val windowManager: ViewCaptureAwareWindowManager,
+ private val windowManager: WindowManager,
private val accessibilityManager: AccessibilityManager,
private val statusBarStateController: StatusBarStateController,
private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index 54c52b533da4..3b22e13f29a2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -397,7 +397,7 @@ object BiometricViewBinder {
// Talkback directional guidance
udfpsGuidanceView.setOnHoverListener { _, event ->
launch {
- viewModel.onAnnounceAccessibilityHint(
+ viewModel.onUpdateAccessibilityHint(
event,
accessibilityManager.isTouchExplorationEnabled,
)
@@ -406,7 +406,9 @@ object BiometricViewBinder {
}
launch {
viewModel.accessibilityHint.collect { message ->
- if (message.isNotBlank()) view.announceForAccessibility(message)
+ if (message.isNotBlank()) {
+ udfpsGuidanceView.contentDescription = message
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
index 02c378417f3d..fcc0121e8f93 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
@@ -27,7 +27,6 @@ import android.view.View
import android.view.ViewGroup
import android.view.ViewOutlineProvider
import android.view.WindowInsets
-import android.view.WindowManager
import android.view.accessibility.AccessibilityManager
import android.widget.ImageView
import android.widget.TextView
@@ -48,6 +47,7 @@ import com.android.systemui.biometrics.ui.viewmodel.isSmall
import com.android.systemui.biometrics.ui.viewmodel.isTop
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
+import com.android.systemui.utils.windowmanager.WindowManagerUtils
import kotlin.math.abs
import kotlinx.coroutines.flow.combine
import com.android.app.tracing.coroutines.launchTraced as launch
@@ -66,7 +66,7 @@ object BiometricViewSizeBinder {
viewsToHideWhenSmall: List<View>,
jankListener: BiometricJankListener,
) {
- val windowManager = requireNotNull(view.context.getSystemService(WindowManager::class.java))
+ val windowManager = WindowManagerUtils.getWindowManager(view.context)
val accessibilityManager =
requireNotNull(view.context.getSystemService(AccessibilityManager::class.java))
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index 0902d19b6787..4e17a2658ee7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -886,7 +886,7 @@ constructor(
}
/** Sets the message used for UDFPS directional guidance */
- suspend fun onAnnounceAccessibilityHint(
+ suspend fun onUpdateAccessibilityHint(
event: MotionEvent,
touchExplorationEnabled: Boolean,
): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt b/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
index 7d518f4f7e78..718ef51aa161 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
@@ -23,20 +23,19 @@ import android.os.SystemProperties
import android.view.Surface
import android.view.View
import android.view.WindowManager
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.logging.UiEvent
import com.android.internal.logging.UiEventLogger
import com.android.settingslib.Utils
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.res.R
+import com.android.systemui.surfaceeffects.ripple.RippleView
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.surfaceeffects.ripple.RippleView
import com.android.systemui.util.time.SystemClock
import java.io.PrintWriter
import javax.inject.Inject
@@ -58,7 +57,6 @@ class WiredChargingRippleController @Inject constructor(
featureFlags: FeatureFlags,
private val context: Context,
private val windowManager: WindowManager,
- private val viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
private val systemClock: SystemClock,
private val uiEventLogger: UiEventLogger
) {
@@ -163,12 +161,12 @@ class WiredChargingRippleController @Inject constructor(
override fun onViewAttachedToWindow(view: View) {
layoutRipple()
rippleView.startRipple(Runnable {
- viewCaptureAwareWindowManager.removeView(rippleView)
+ windowManager.removeView(rippleView)
})
rippleView.removeOnAttachStateChangeListener(this)
}
})
- viewCaptureAwareWindowManager.addView(rippleView, windowLayoutParams)
+ windowManager.addView(rippleView, windowLayoutParams)
uiEventLogger.log(WiredChargingRippleEvent.CHARGING_RIPPLE_PLAYED)
}
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
index e5e9c4685264..4ca55400561e 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
@@ -28,10 +28,10 @@ import android.util.Slog;
import android.view.Gravity;
import android.view.WindowManager;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.surfaceeffects.ripple.RippleShader.RippleShape;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
/**
* A WirelessChargingAnimation is a view containing view + animation for wireless charging.
@@ -60,11 +60,11 @@ public class WirelessChargingAnimation {
*/
private WirelessChargingAnimation(@NonNull Context context, @Nullable Looper looper,
int transmittingBatteryLevel, int batteryLevel, Callback callback, boolean isDozing,
- RippleShape rippleShape, UiEventLogger uiEventLogger,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
+ RippleShape rippleShape, UiEventLogger uiEventLogger, WindowManager windowManager,
+ WindowManagerProvider windowManagerProvider) {
mCurrentWirelessChargingView = new WirelessChargingView(context, looper,
transmittingBatteryLevel, batteryLevel, callback, isDozing,
- rippleShape, uiEventLogger, viewCaptureAwareWindowManager);
+ rippleShape, uiEventLogger, windowManager, windowManagerProvider);
}
/**
@@ -75,11 +75,11 @@ public class WirelessChargingAnimation {
public static WirelessChargingAnimation makeWirelessChargingAnimation(@NonNull Context context,
@Nullable Looper looper, int transmittingBatteryLevel, int batteryLevel,
Callback callback, boolean isDozing, RippleShape rippleShape,
- UiEventLogger uiEventLogger,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
+ UiEventLogger uiEventLogger, WindowManager windowManager,
+ WindowManagerProvider windowManagerProvider) {
return new WirelessChargingAnimation(context, looper, transmittingBatteryLevel,
- batteryLevel, callback, isDozing, rippleShape, uiEventLogger,
- viewCaptureAwareWindowManager);
+ batteryLevel, callback, isDozing, rippleShape, uiEventLogger, windowManager,
+ windowManagerProvider);
}
/**
@@ -88,10 +88,10 @@ public class WirelessChargingAnimation {
*/
public static WirelessChargingAnimation makeChargingAnimationWithNoBatteryLevel(
@NonNull Context context, RippleShape rippleShape, UiEventLogger uiEventLogger,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
+ WindowManager windowManager, WindowManagerProvider windowManagerProvider) {
return makeWirelessChargingAnimation(context, null,
UNKNOWN_BATTERY_LEVEL, UNKNOWN_BATTERY_LEVEL, null, false,
- rippleShape, uiEventLogger, viewCaptureAwareWindowManager);
+ rippleShape, uiEventLogger, windowManager, windowManagerProvider);
}
/**
@@ -123,19 +123,21 @@ public class WirelessChargingAnimation {
private int mGravity;
private WirelessChargingLayout mView;
private WirelessChargingLayout mNextView;
- private ViewCaptureAwareWindowManager mWM;
+ private WindowManager mWM;
private Callback mCallback;
+ private WindowManagerProvider mWindowManagerProvider;
public WirelessChargingView(Context context, @Nullable Looper looper,
int transmittingBatteryLevel, int batteryLevel, Callback callback,
boolean isDozing, RippleShape rippleShape, UiEventLogger uiEventLogger,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
+ WindowManager windowManager, WindowManagerProvider windowManagerProvider) {
mCallback = callback;
mNextView = new WirelessChargingLayout(context, transmittingBatteryLevel, batteryLevel,
isDozing, rippleShape);
mGravity = Gravity.CENTER_HORIZONTAL | Gravity.CENTER;
mUiEventLogger = uiEventLogger;
- mWM = viewCaptureAwareWindowManager;
+ mWM = windowManager;
+ mWindowManagerProvider = windowManagerProvider;
final WindowManager.LayoutParams params = mParams;
params.height = WindowManager.LayoutParams.MATCH_PARENT;
@@ -207,6 +209,7 @@ public class WirelessChargingAnimation {
if (context == null) {
context = mView.getContext();
}
+ mWM = mWindowManagerProvider.getWindowManager(context);
mParams.packageName = packageName;
mParams.hideTimeoutMilliseconds = DURATION;
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayWindow.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayWindow.java
index dc3b50c93298..059ea3271235 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayWindow.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayWindow.java
@@ -27,7 +27,6 @@ import android.view.Window;
import android.view.WindowInsets;
import android.view.WindowManager;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.policy.PhoneWindow;
import com.android.systemui.clipboardoverlay.dagger.ClipboardOverlayModule.OverlayWindowContext;
import com.android.systemui.screenshot.FloatingWindowUtil;
@@ -45,7 +44,6 @@ public class ClipboardOverlayWindow extends PhoneWindow
private final Context mContext;
private final WindowManager mWindowManager;
- private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
private final WindowManager.LayoutParams mWindowLayoutParams;
private boolean mKeyboardVisible;
@@ -55,7 +53,6 @@ public class ClipboardOverlayWindow extends PhoneWindow
@Inject
ClipboardOverlayWindow(@OverlayWindowContext Context context,
- @OverlayWindowContext ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
@OverlayWindowContext WindowManager windowManager) {
super(context);
mContext = context;
@@ -66,10 +63,9 @@ public class ClipboardOverlayWindow extends PhoneWindow
requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS);
setBackgroundDrawableResource(android.R.color.transparent);
mWindowManager = windowManager;
- mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager;
mWindowLayoutParams = FloatingWindowUtil.getFloatingWindowParams();
mWindowLayoutParams.setTitle("ClipboardOverlay");
- setWindowManager(windowManager, null, null);
+ setWindowManager(mWindowManager, null, null);
setWindowFocusable(false);
}
@@ -86,12 +82,10 @@ public class ClipboardOverlayWindow extends PhoneWindow
attach();
withWindowAttached(() -> {
- WindowInsets currentInsets = mWindowManager.getCurrentWindowMetrics()
- .getWindowInsets();
+ WindowInsets currentInsets = mWindowManager.getCurrentWindowMetrics().getWindowInsets();
mKeyboardVisible = currentInsets.isVisible(WindowInsets.Type.ime());
peekDecorView().getViewTreeObserver().addOnGlobalLayoutListener(() -> {
- WindowInsets insets = mWindowManager.getCurrentWindowMetrics()
- .getWindowInsets();
+ WindowInsets insets = mWindowManager.getCurrentWindowMetrics().getWindowInsets();
boolean keyboardVisible = insets.isVisible(WindowInsets.Type.ime());
if (keyboardVisible != mKeyboardVisible) {
mKeyboardVisible = keyboardVisible;
@@ -112,7 +106,7 @@ public class ClipboardOverlayWindow extends PhoneWindow
void remove() {
final View decorView = peekDecorView();
if (decorView != null && decorView.isAttachedToWindow()) {
- mViewCaptureAwareWindowManager.removeViewImmediate(decorView);
+ mWindowManager.removeViewImmediate(decorView);
}
}
@@ -146,7 +140,7 @@ public class ClipboardOverlayWindow extends PhoneWindow
if (decorView.isAttachedToWindow()) {
return;
}
- mViewCaptureAwareWindowManager.addView(decorView, mWindowLayoutParams);
+ mWindowManager.addView(decorView, mWindowLayoutParams);
decorView.requestApplyInsets();
}
@@ -167,7 +161,7 @@ public class ClipboardOverlayWindow extends PhoneWindow
}
final View decorView = peekDecorView();
if (decorView != null && decorView.isAttachedToWindow()) {
- mViewCaptureAwareWindowManager.updateViewLayout(decorView, mWindowLayoutParams);
+ mWindowManager.updateViewLayout(decorView, mWindowLayoutParams);
}
}
}
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 c86a84b17efe..7a60cce63a33 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java
@@ -19,9 +19,7 @@ package com.android.systemui.clipboardoverlay.dagger;
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;
@@ -31,8 +29,6 @@ import android.view.Display;
import android.view.LayoutInflater;
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;
@@ -40,6 +36,7 @@ import com.android.systemui.clipboardoverlay.IntentCreator;
import com.android.systemui.res.R;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import dagger.Lazy;
import dagger.Module;
@@ -89,21 +86,9 @@ public interface ClipboardOverlayModule {
*/
@Provides
@OverlayWindowContext
- static WindowManager provideWindowManager(@OverlayWindowContext Context context) {
- return context.getSystemService(WindowManager.class);
- }
-
- /**
- *
- */
- @Provides
- @OverlayWindowContext
- static ViewCaptureAwareWindowManager provideViewCaptureAwareWindowManager(
- @OverlayWindowContext WindowManager windowManager,
- Lazy<ViewCapture> daggerLazyViewCapture) {
- return new ViewCaptureAwareWindowManager(windowManager,
- /* lazyViewCapture= */ toKotlinLazy(daggerLazyViewCapture),
- /* isViewCaptureEnabled= */ enableViewCaptureTracing());
+ static WindowManager provideWindowManager(@OverlayWindowContext Context context,
+ WindowManagerProvider windowManagerProvider) {
+ return windowManagerProvider.getWindowManager(context);
}
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingListener.kt b/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingListener.kt
index af8a5fa23ccb..8bfec0a5dac2 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingListener.kt
@@ -21,8 +21,11 @@ import android.app.DreamManager
import android.service.dreams.Flags.allowDreamWhenPostured
import com.android.app.tracing.coroutines.launchInTraced
import com.android.systemui.CoreStartable
+import com.android.systemui.common.domain.interactor.BatteryInteractor
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.communal.posturing.domain.interactor.PosturingInteractor
import com.android.systemui.communal.posturing.shared.model.PosturedState
+import com.android.systemui.communal.shared.model.WhenToDream
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.log.dagger.CommunalTableLog
@@ -30,10 +33,14 @@ import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
+import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
import java.io.PrintWriter
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
@SysUISingleton
@@ -42,19 +49,36 @@ class DevicePosturingListener
constructor(
private val commandRegistry: CommandRegistry,
private val dreamManager: DreamManager,
- private val interactor: PosturingInteractor,
+ private val posturingInteractor: PosturingInteractor,
+ communalSettingsInteractor: CommunalSettingsInteractor,
+ batteryInteractor: BatteryInteractor,
@Background private val bgScope: CoroutineScope,
@CommunalTableLog private val tableLogBuffer: TableLogBuffer,
) : CoreStartable {
private val command = DevicePosturingCommand()
+ // Only subscribe to posturing if applicable to avoid running the posturing CHRE nanoapp
+ // if posturing signal is not needed.
+ private val postured =
+ allOf(
+ batteryInteractor.isDevicePluggedIn,
+ communalSettingsInteractor.whenToDream.map { it == WhenToDream.WHILE_POSTURED },
+ )
+ .flatMapLatestConflated { shouldListen ->
+ if (shouldListen) {
+ posturingInteractor.postured
+ } else {
+ flowOf(false)
+ }
+ }
+
@SuppressLint("MissingPermission")
override fun start() {
if (!allowDreamWhenPostured()) {
return
}
- interactor.postured
+ postured
.distinctUntilChanged()
.logDiffsForTable(
tableLogBuffer = tableLogBuffer,
@@ -78,7 +102,7 @@ constructor(
val state =
when (arg.lowercase()) {
- "true" -> PosturedState.Postured(confidence = 1f)
+ "true" -> PosturedState.Postured
"false" -> PosturedState.NotPostured
"clear" -> PosturedState.Unknown
else -> {
@@ -87,7 +111,7 @@ constructor(
null
}
}
- state?.let { interactor.setValueForDebug(it) }
+ state?.let { posturingInteractor.setValueForDebug(it) }
}
override fun help(pw: PrintWriter) {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractor.kt
index 20bfabdc5fb9..678a5e2f6a1c 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractor.kt
@@ -57,7 +57,15 @@ constructor(
allOf(batteryInteractor.isDevicePluggedIn, dockManager.retrieveIsDocked())
}
WhenToStartHub.WHILE_CHARGING_AND_POSTURED -> {
- allOf(batteryInteractor.isDevicePluggedIn, posturingInteractor.postured)
+ // Only listen to posturing if applicable to avoid running the posturing
+ // CHRE nanoapp when not needed.
+ batteryInteractor.isDevicePluggedIn.flatMapLatestConflated { isCharging ->
+ if (isCharging) {
+ posturingInteractor.postured
+ } else {
+ flowOf(false)
+ }
+ }
}
WhenToStartHub.NEVER -> flowOf(false)
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/posturing/data/model/PositionState.kt b/packages/SystemUI/src/com/android/systemui/communal/posturing/data/model/PositionState.kt
new file mode 100644
index 000000000000..21b8dd785f53
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/posturing/data/model/PositionState.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.communal.posturing.data.model
+
+import androidx.annotation.FloatRange
+
+data class PositionState(
+ val stationary: StationaryState = StationaryState.Unknown,
+ val orientation: OrientationState = OrientationState.Unknown,
+) {
+ sealed interface StationaryState {
+ @get:FloatRange(from = 0.0, to = 1.0) val confidence: Float
+
+ data object Unknown : StationaryState {
+ override val confidence: Float = 0f
+ }
+
+ data class Stationary(override val confidence: Float) : StationaryState
+
+ data class NotStationary(override val confidence: Float) : StationaryState
+ }
+
+ sealed interface OrientationState {
+ @get:FloatRange(from = 0.0, to = 1.0) val confidence: Float
+
+ data object Unknown : OrientationState {
+ override val confidence: Float = 0f
+ }
+
+ data class Postured(override val confidence: Float) : OrientationState
+
+ data class NotPostured(override val confidence: Float) : OrientationState
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/NoOpPosturingRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/NoOpPosturingRepository.kt
index c5f357f556ca..d826685886d9 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/NoOpPosturingRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/NoOpPosturingRepository.kt
@@ -16,7 +16,7 @@
package com.android.systemui.communal.posturing.data.repository
-import com.android.systemui.communal.posturing.shared.model.PosturedState
+import com.android.systemui.communal.posturing.data.model.PositionState
import com.android.systemui.dagger.SysUISingleton
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -25,6 +25,6 @@ import kotlinx.coroutines.flow.asStateFlow
@SysUISingleton
class NoOpPosturingRepository @Inject constructor() : PosturingRepository {
- override val posturedState: Flow<PosturedState> =
- MutableStateFlow(PosturedState.Unknown).asStateFlow()
+ override val positionState: Flow<PositionState> =
+ MutableStateFlow(PositionState()).asStateFlow()
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/PosturingRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/PosturingRepository.kt
index dae1a47f5be0..4de0a1e21d35 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/PosturingRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/PosturingRepository.kt
@@ -16,7 +16,7 @@
package com.android.systemui.communal.posturing.data.repository
-import com.android.systemui.communal.posturing.shared.model.PosturedState
+import com.android.systemui.communal.posturing.data.model.PositionState
import kotlinx.coroutines.flow.Flow
/**
@@ -25,5 +25,5 @@ import kotlinx.coroutines.flow.Flow
*/
interface PosturingRepository {
/** Whether the device is currently stationary and upright. */
- val posturedState: Flow<PosturedState>
+ val positionState: Flow<PositionState>
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractor.kt
index cd81dea9cad1..e487590d87d7 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractor.kt
@@ -16,26 +16,211 @@
package com.android.systemui.communal.posturing.domain.interactor
+import android.annotation.SuppressLint
+import android.hardware.Sensor
+import android.hardware.TriggerEvent
+import android.hardware.TriggerEventListener
+import android.service.dreams.Flags.allowDreamWhenPostured
+import com.android.systemui.communal.posturing.data.model.PositionState
import com.android.systemui.communal.posturing.data.repository.PosturingRepository
import com.android.systemui.communal.posturing.shared.model.PosturedState
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.dagger.CommunalLog
+import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
+import com.android.systemui.util.kotlin.slidingWindow
+import com.android.systemui.util.sensors.AsyncSensorManager
+import com.android.systemui.util.time.SystemClock
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import java.util.concurrent.atomic.AtomicBoolean
import javax.inject.Inject
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filterNot
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.stateIn
@SysUISingleton
-class PosturingInteractor @Inject constructor(repository: PosturingRepository) {
- private val debugPostured = MutableStateFlow<PosturedState>(PosturedState.Unknown)
+class PosturingInteractor
+@Inject
+constructor(
+ repository: PosturingRepository,
+ private val asyncSensorManager: AsyncSensorManager,
+ @Application private val applicationScope: CoroutineScope,
+ @Background private val bgDispatcher: CoroutineDispatcher,
+ @CommunalLog private val logBuffer: LogBuffer,
+ clock: SystemClock,
+) {
+ private val logger = Logger(logBuffer, TAG)
- val postured: Flow<Boolean> =
- combine(repository.posturedState, debugPostured) { postured, debugValue ->
- debugValue.asBoolean() ?: postured.asBoolean() ?: false
- }
+ private val debugPostured = MutableStateFlow<PosturedState>(PosturedState.Unknown)
fun setValueForDebug(value: PosturedState) {
debugPostured.value = value
}
+
+ /**
+ * Detects whether or not the device is stationary, applying a sliding window smoothing
+ * algorithm.
+ */
+ private val stationarySmoothed: Flow<Boolean> =
+ merge(
+ observeTriggerSensor(Sensor.TYPE_PICK_UP_GESTURE)
+ // If pickup detected, avoid triggering posturing at all within the sliding
+ // window by emitting a negative infinity value.
+ .map { Float.NEGATIVE_INFINITY }
+ .onEach { logger.i("pickup gesture detected") },
+ observeTriggerSensor(Sensor.TYPE_SIGNIFICANT_MOTION)
+ // If motion detected, avoid triggering posturing at all within the sliding
+ // window by emitting a negative infinity value.
+ .map { Float.NEGATIVE_INFINITY }
+ .onEach { logger.i("significant motion detected") },
+ repository.positionState
+ .map { it.stationary }
+ .filterNot { it is PositionState.StationaryState.Unknown }
+ .map { stationaryState ->
+ if (stationaryState is PositionState.StationaryState.Stationary) {
+ stationaryState.confidence
+ } else {
+ // If not stationary, then we should effectively disable posturing by
+ // emitting the lowest possible confidence.
+ Float.NEGATIVE_INFINITY
+ }
+ },
+ )
+ .slidingWindow(SLIDING_WINDOW_DURATION, clock)
+ .filterNot { it.isEmpty() }
+ .map { window ->
+ val avgStationaryConfidence = window.average()
+ logger.i({ "stationary confidence: $double1 | window: $str1" }) {
+ str1 = window.formatWindowForDebugging()
+ double1 = avgStationaryConfidence
+ }
+ avgStationaryConfidence > CONFIDENCE_THRESHOLD
+ }
+
+ /**
+ * Detects whether or not the device is in an upright orientation, applying a sliding window
+ * smoothing algorithm.
+ */
+ private val orientationSmoothed: Flow<Boolean> =
+ repository.positionState
+ .map { it.orientation }
+ .filterNot { it is PositionState.OrientationState.Unknown }
+ .map { orientationState ->
+ if (orientationState is PositionState.OrientationState.Postured) {
+ orientationState.confidence
+ } else {
+ // If not postured, then we should effectively disable posturing by
+ // emitting the lowest possible confidence.
+ Float.NEGATIVE_INFINITY
+ }
+ }
+ .slidingWindow(SLIDING_WINDOW_DURATION, clock)
+ .filterNot { it.isEmpty() }
+ .map { window ->
+ val avgOrientationConfidence = window.average()
+ logger.i({ "orientation confidence: $double1 | window: $str1" }) {
+ str1 = window.formatWindowForDebugging()
+ double1 = avgOrientationConfidence
+ }
+ avgOrientationConfidence > CONFIDENCE_THRESHOLD
+ }
+
+ /**
+ * Posturing is composed of the device being stationary and in the correct orientation. If both
+ * conditions are met, then consider it postured.
+ */
+ private val posturedSmoothed: Flow<PosturedState> =
+ allOf(stationarySmoothed, orientationSmoothed)
+ .map { postured ->
+ if (postured) {
+ PosturedState.Postured
+ } else {
+ PosturedState.NotPostured
+ }
+ }
+ .flowOn(bgDispatcher)
+ .stateIn(
+ scope = applicationScope,
+ // Avoid losing the smoothing history if the user plug/unplugs rapidly.
+ started =
+ SharingStarted.WhileSubscribed(
+ stopTimeoutMillis = STOP_TIMEOUT_AFTER_UNSUBSCRIBE.inWholeMilliseconds,
+ replayExpirationMillis = 0,
+ ),
+ initialValue = PosturedState.Unknown,
+ )
+
+ /**
+ * Whether the device is postured.
+ *
+ * NOTE: Due to smoothing, this signal may be delayed to ensure we have a stable reading before
+ * being considered postured.
+ */
+ val postured: Flow<Boolean> by lazy {
+ if (allowDreamWhenPostured()) {
+ combine(posturedSmoothed, debugPostured) { postured, debugValue ->
+ debugValue.asBoolean() ?: postured.asBoolean() ?: false
+ }
+ } else {
+ MutableStateFlow(false)
+ }
+ }
+
+ /**
+ * Helper for observing a trigger sensor, which automatically unregisters itself after it
+ * executes once.
+ */
+ private fun observeTriggerSensor(type: Int): Flow<Unit> = conflatedCallbackFlow {
+ val sensor = asyncSensorManager.getDefaultSensor(type)
+ val isRegistered = AtomicBoolean(false)
+
+ fun registerCallbackInternal(callback: TriggerEventListener) {
+ if (isRegistered.compareAndSet(false, true)) {
+ asyncSensorManager.requestTriggerSensor(callback, sensor)
+ }
+ }
+
+ val callback =
+ object : TriggerEventListener() {
+ override fun onTrigger(event: TriggerEvent) {
+ trySend(Unit)
+ if (isRegistered.getAndSet(false)) {
+ registerCallbackInternal(this)
+ }
+ }
+ }
+
+ if (sensor != null) {
+ registerCallbackInternal(callback)
+ }
+
+ awaitClose {
+ if (isRegistered.getAndSet(false)) {
+ asyncSensorManager.cancelTriggerSensor(callback, sensor)
+ }
+ }
+ }
+
+ companion object {
+ const val TAG = "PosturingInteractor"
+ val SLIDING_WINDOW_DURATION = 10.seconds
+ const val CONFIDENCE_THRESHOLD = 0.8f
+ val STOP_TIMEOUT_AFTER_UNSUBSCRIBE = 5.seconds
+ }
}
fun PosturedState.asBoolean(): Boolean? {
@@ -45,3 +230,8 @@ fun PosturedState.asBoolean(): Boolean? {
PosturedState.Unknown -> null
}
}
+
+@SuppressLint("DefaultLocale")
+fun List<Float>.formatWindowForDebugging(): String {
+ return joinToString(prefix = "[", postfix = "]") { String.format("%.2f", it) }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/posturing/shared/model/PosturedState.kt b/packages/SystemUI/src/com/android/systemui/communal/posturing/shared/model/PosturedState.kt
index 431ca67315eb..c71cf14c4b52 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/posturing/shared/model/PosturedState.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/posturing/shared/model/PosturedState.kt
@@ -18,7 +18,7 @@ package com.android.systemui.communal.posturing.shared.model
sealed interface PosturedState {
/** Represents postured state */
- data class Postured(val confidence: Float) : PosturedState
+ data object Postured : PosturedState
/** Represents unknown/uninitialized state */
data object Unknown : PosturedState
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index 7354f4096801..f45bafdfb17e 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -16,9 +16,6 @@
package com.android.systemui.dagger;
-import static com.android.systemui.Flags.enableViewCaptureTracing;
-import static com.android.systemui.util.ConvenienceExtensionsKt.toKotlinLazy;
-
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
@@ -103,7 +100,6 @@ import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.satellite.SatelliteManager;
-import android.view.Choreographer;
import android.view.CrossWindowBlurListeners;
import android.view.IWindowManager;
import android.view.LayoutInflater;
@@ -115,13 +111,9 @@ import android.view.accessibility.CaptioningManager;
import android.view.inputmethod.InputMethodManager;
import android.view.textclassifier.TextClassificationManager;
-import androidx.annotation.NonNull;
import androidx.asynclayoutinflater.view.AsyncLayoutInflater;
import androidx.core.app.NotificationManagerCompat;
-import com.android.app.viewcapture.ViewCapture;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
-import com.android.app.viewcapture.ViewCaptureFactory;
import com.android.internal.app.IBatteryStats;
import com.android.internal.appwidget.IAppWidgetService;
import com.android.internal.jank.InteractionJankMonitor;
@@ -135,8 +127,9 @@ import com.android.systemui.dagger.qualifiers.TestHarness;
import com.android.systemui.shared.system.PackageManagerWrapper;
import com.android.systemui.user.utils.UserScopedService;
import com.android.systemui.user.utils.UserScopedServiceImpl;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
+import com.android.systemui.utils.windowmanager.WindowManagerProviderImpl;
-import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
@@ -713,38 +706,23 @@ public class FrameworkServicesModule {
@Provides
@Singleton
- static WindowManager provideWindowManager(Context context) {
- return context.getSystemService(WindowManager.class);
- }
-
- /** A window manager working for the default display only. */
- @Provides
- @Singleton
- @Main
- static WindowManager provideMainWindowManager(WindowManager windowManager) {
- return windowManager;
+ static WindowManagerProvider provideWindowManagerProvider() {
+ return new WindowManagerProviderImpl();
}
@Provides
@Singleton
- static ViewCaptureAwareWindowManager provideViewCaptureAwareWindowManager(
- WindowManager windowManager, Lazy<ViewCapture> daggerLazyViewCapture) {
- return new ViewCaptureAwareWindowManager(windowManager,
- /* lazyViewCapture= */ toKotlinLazy(daggerLazyViewCapture),
- /* isViewCaptureEnabled= */ enableViewCaptureTracing());
+ static WindowManager provideWindowManager(Context context,
+ WindowManagerProvider windowManagerProvider) {
+ return windowManagerProvider.getWindowManager(context);
}
+ /** A window manager working for the default display only. */
@Provides
@Singleton
- static ViewCaptureAwareWindowManager.Factory viewCaptureAwareWindowManagerFactory(
- Lazy<ViewCapture> daggerLazyViewCapture) {
- return new ViewCaptureAwareWindowManager.Factory() {
- @NonNull
- @Override
- public ViewCaptureAwareWindowManager create(@NonNull WindowManager windowManager) {
- return provideViewCaptureAwareWindowManager(windowManager, daggerLazyViewCapture);
- }
- };
+ @Main
+ static WindowManager provideMainWindowManager(WindowManager windowManager) {
+ return windowManager;
}
@Provides
@@ -835,12 +813,6 @@ public class FrameworkServicesModule {
@Provides
@Singleton
- static ViewCapture provideViewCapture(Context context) {
- return ViewCaptureFactory.getInstance(context);
- }
-
- @Provides
- @Singleton
@Nullable
static SupervisionManager provideSupervisionManager(Context context) {
return (SupervisionManager) context.getSystemService(Context.SUPERVISION_SERVICE);
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/ui/view/UdfpsAccessibilityOverlay.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/view/UdfpsAccessibilityOverlay.kt
index 7be323073692..9c3b9b273ab5 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/ui/view/UdfpsAccessibilityOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/view/UdfpsAccessibilityOverlay.kt
@@ -20,4 +20,8 @@ import android.content.Context
import android.view.View
/** Overlay to handle under-fingerprint sensor accessibility events. */
-class UdfpsAccessibilityOverlay(context: Context?) : View(context)
+class UdfpsAccessibilityOverlay(context: Context?) : View(context) {
+ init {
+ accessibilityLiveRegion = ACCESSIBILITY_LIVE_REGION_ASSERTIVE
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/UdfpsAccessibilityOverlayViewModel.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/UdfpsAccessibilityOverlayViewModel.kt
index fa849bf5e413..1849bf20abdb 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/UdfpsAccessibilityOverlayViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/UdfpsAccessibilityOverlayViewModel.kt
@@ -56,7 +56,7 @@ abstract class UdfpsAccessibilityOverlayViewModel(
event.getPointerId(0),
event,
overlayParams, /* rotateToPortrait */
- false
+ false,
)
if (
@@ -64,7 +64,7 @@ abstract class UdfpsAccessibilityOverlayViewModel(
event.getPointerId(0),
event,
overlayParams,
- /* rotateTouchToPortrait */ false
+ /* rotateTouchToPortrait */ false,
)
) {
// view only receives motionEvents when [visible] which requires touchExplorationEnabled
@@ -75,10 +75,10 @@ abstract class UdfpsAccessibilityOverlayViewModel(
scaledTouch.x,
scaledTouch.y,
overlayParams,
- /* touchRotatedToPortrait */ false
+ /* touchRotatedToPortrait */ false,
)
if (announceStr != null) {
- v.announceForAccessibility(announceStr)
+ v.contentDescription = announceStr
}
}
// always let the motion events go through to underlying views
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt
index 3390640fa6c6..aaaaacef001a 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt
@@ -31,6 +31,7 @@ import com.android.systemui.display.shared.model.DisplayWindowProperties
import com.android.systemui.res.R
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
+import com.android.systemui.utils.windowmanager.WindowManagerUtils
import com.google.common.collect.HashBasedTable
import com.google.common.collect.Table
import java.io.PrintWriter
@@ -110,7 +111,7 @@ constructor(
return null
}
@SuppressLint("NonInjectedService") // Need to manually get the service
- val windowManager = context.getSystemService(WindowManager::class.java) as WindowManager
+ val windowManager = WindowManagerUtils.getWindowManager(context)
val layoutInflater = LayoutInflater.from(context)
DisplayWindowProperties(displayId, windowType, context, windowManager, layoutInflater)
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index a2bcb98e36fe..fd716eea799a 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -46,7 +46,6 @@ import androidx.lifecycle.LifecycleService;
import androidx.lifecycle.ServiceLifecycleDispatcher;
import androidx.lifecycle.ViewModelStore;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.compose.animation.scene.OverlayKey;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
@@ -117,7 +116,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
@Nullable
private final ComponentName mHomeControlPanelDreamComponent;
private final UiEventLogger mUiEventLogger;
- private final ViewCaptureAwareWindowManager mWindowManager;
+ private final WindowManager mWindowManager;
private final String mWindowTitle;
// A reference to the {@link Window} used to hold the dream overlay.
@@ -378,7 +377,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
Context context,
DreamOverlayLifecycleOwner lifecycleOwner,
@Main DelayableExecutor executor,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
+ WindowManager windowManager,
ComplicationComponent.Factory complicationComponentFactory,
DreamComplicationComponent.Factory dreamComplicationComponentFactory,
DreamOverlayComponent.Factory dreamOverlayComponentFactory,
@@ -403,7 +402,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
super(executor);
mContext = context;
mExecutor = executor;
- mWindowManager = viewCaptureAwareWindowManager;
+ mWindowManager = windowManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mScrimManager = scrimManager;
mLowLightDreamComponent = lowLightDreamComponent;
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/docking/binder/KeyboardDockingIndicationViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyboard/docking/binder/KeyboardDockingIndicationViewBinder.kt
index c2974a8d5429..c08a8e297174 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/docking/binder/KeyboardDockingIndicationViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/docking/binder/KeyboardDockingIndicationViewBinder.kt
@@ -20,7 +20,6 @@ import android.content.Context
import android.graphics.Paint
import android.graphics.PixelFormat
import android.view.WindowManager
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyboard.docking.ui.KeyboardDockingIndicationView
@@ -38,7 +37,7 @@ constructor(
context: Context,
@Application private val applicationScope: CoroutineScope,
private val viewModel: KeyboardDockingIndicationViewModel,
- private val windowManager: ViewCaptureAwareWindowManager,
+ private val windowManager: WindowManager
) {
private val windowLayoutParams =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
index a3796ab5ee27..cc03e49b462b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
@@ -145,7 +145,7 @@ constructor(
KeyguardState.GLANCEABLE_HUB
} else if (isOccluded && !isDreaming) {
KeyguardState.OCCLUDED
- } else if (hubV2 && isDreaming) {
+ } else if (isDreaming) {
KeyguardState.DREAMING
} else if (hubV2 && isIdleOnCommunal) {
if (SceneContainerFlag.isEnabled) return@collect
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/SideFpsProgressBar.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/SideFpsProgressBar.kt
index 1fd609dba637..853f1769994e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/SideFpsProgressBar.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/SideFpsProgressBar.kt
@@ -26,7 +26,6 @@ import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.WindowManager
import android.widget.ProgressBar
import androidx.core.view.isGone
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.res.R
import javax.inject.Inject
@@ -38,7 +37,7 @@ class SideFpsProgressBar
@Inject
constructor(
private val layoutInflater: LayoutInflater,
- private val windowManager: ViewCaptureAwareWindowManager,
+ private val windowManager: WindowManager,
) {
private var overlayView: View? = null
@@ -91,7 +90,7 @@ constructor(
) {
if (overlayView == null) {
overlayView = layoutInflater.inflate(R.layout.sidefps_progress_bar, null, false)
- windowManager.addView(requireNotNull(overlayView), overlayViewParams)
+ windowManager.addView(overlayView, overlayViewParams)
progressBar?.pivotX = 0.0f
progressBar?.pivotY = 0.0f
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt
index 2cd5016cb206..d053dd2b54d1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt
@@ -248,7 +248,7 @@ constructor(
// Update loading state with actual active value
mediaFilterRepository.selectedUserEntries.value[lastActiveId]?.let {
mediaFilterRepository.addMediaDataLoadingState(
- MediaDataLoadingModel.Loaded(lastActiveId, immediately)
+ MediaDataLoadingModel.Loaded(lastActiveId)
)
mediaLogger.logMediaLoaded(lastActiveId, it.active, "expiring reactivated id")
listeners.forEach { listener ->
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaDataLoadingModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaDataLoadingModel.kt
index c8a02faea58a..323e5cb76aba 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaDataLoadingModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaDataLoadingModel.kt
@@ -26,13 +26,10 @@ sealed class MediaDataLoadingModel {
/** Media data has been loaded. */
data class Loaded(
override val instanceId: InstanceId,
- val immediatelyUpdateUi: Boolean = true,
val receivedSmartspaceCardLatency: Int = 0,
val isSsReactivated: Boolean = false,
) : MediaDataLoadingModel()
/** Media data has been removed. */
- data class Removed(
- override val instanceId: InstanceId,
- ) : MediaDataLoadingModel()
+ data class Removed(override val instanceId: InstanceId) : MediaDataLoadingModel()
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
index 71b3223b77be..800220ee962a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
@@ -68,7 +68,7 @@ import com.android.systemui.media.controls.ui.view.MediaHostState
import com.android.systemui.media.controls.ui.view.MediaScrollView
import com.android.systemui.media.controls.ui.view.MediaViewHolder
import com.android.systemui.media.controls.ui.viewmodel.MediaCarouselViewModel
-import com.android.systemui.media.controls.ui.viewmodel.MediaCommonViewModel
+import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
@@ -324,8 +324,8 @@ constructor(
private var widthInSceneContainerPx = 0
private var heightInSceneContainerPx = 0
- private val controllerById = mutableMapOf<String, MediaViewController>()
- private val commonViewModels = mutableListOf<MediaCommonViewModel>()
+ private val controllerById = mutableMapOf<InstanceId, MediaViewController>()
+ private val controlViewModels = mutableListOf<MediaControlViewModel>()
private val isOnGone =
keyguardTransitionInteractor
@@ -566,10 +566,10 @@ constructor(
private fun listenForMediaItemsChanges(scope: CoroutineScope): Job {
return scope.launch {
mediaCarouselViewModel.mediaItems.collectLatest {
- val diffUtilCallback = MediaViewModelCallback(commonViewModels, it)
+ val diffUtilCallback = MediaViewModelCallback(controlViewModels, it)
val listUpdateCallback =
MediaViewModelListUpdateCallback(
- old = commonViewModels,
+ old = controlViewModels,
new = it,
onAdded = this@MediaCarouselController::onAdded,
onUpdated = this@MediaCarouselController::onUpdated,
@@ -590,7 +590,7 @@ constructor(
}
private fun onAdded(
- commonViewModel: MediaCommonViewModel,
+ controlViewModel: MediaControlViewModel,
position: Int,
configChanged: Boolean = false,
) {
@@ -601,64 +601,52 @@ constructor(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
)
- when (commonViewModel) {
- is MediaCommonViewModel.MediaControl -> {
- val viewHolder = MediaViewHolder.create(LayoutInflater.from(context), mediaContent)
- viewController.widthInSceneContainerPx = widthInSceneContainerPx
- viewController.heightInSceneContainerPx = heightInSceneContainerPx
- viewController.attachPlayer(viewHolder)
- viewController.mediaViewHolder?.player?.layoutParams = lp
- if (configChanged) {
- commonViewModel.controlViewModel.onMediaConfigChanged()
- }
- MediaControlViewBinder.bind(
- viewHolder,
- commonViewModel.controlViewModel,
- viewController,
- falsingManager,
- backgroundDispatcher,
- mainDispatcher,
- )
- mediaContent.addView(viewHolder.player, position)
- controllerById[commonViewModel.instanceId.toString()] = viewController
- }
+ val viewHolder = MediaViewHolder.create(LayoutInflater.from(context), mediaContent)
+ viewController.widthInSceneContainerPx = widthInSceneContainerPx
+ viewController.heightInSceneContainerPx = heightInSceneContainerPx
+ viewController.attachPlayer(viewHolder)
+ viewController.mediaViewHolder?.player?.layoutParams = lp
+ if (configChanged) {
+ controlViewModel.onMediaConfigChanged()
}
+ MediaControlViewBinder.bind(
+ viewHolder,
+ controlViewModel,
+ viewController,
+ falsingManager,
+ backgroundDispatcher,
+ mainDispatcher,
+ )
+ mediaContent.addView(viewHolder.player, position)
+ controllerById[controlViewModel.instanceId] = viewController
viewController.setListening(mediaCarouselScrollHandler.visibleToUser && currentlyExpanded)
updateViewControllerToState(viewController, noAnimation = true)
updatePageIndicator()
- if (
- commonViewModel is MediaCommonViewModel.MediaControl && commonViewModel.isMediaFromRec
- ) {
- mediaCarouselScrollHandler.scrollToPlayer(
- mediaCarouselScrollHandler.visibleMediaIndex,
- destIndex = 0,
- )
- }
mediaCarouselScrollHandler.onPlayersChanged()
mediaFrame.requiresRemeasuring = true
- commonViewModel.onAdded(commonViewModel)
+ controlViewModel.onAdded(controlViewModel)
}
- private fun onUpdated(commonViewModel: MediaCommonViewModel, position: Int) {
- commonViewModel.onUpdated(commonViewModel)
+ private fun onUpdated(controlViewModel: MediaControlViewModel, position: Int) {
+ controlViewModel.onUpdated(controlViewModel)
updatePageIndicator()
mediaCarouselScrollHandler.onPlayersChanged()
}
- private fun onRemoved(commonViewModel: MediaCommonViewModel) {
- val id = (commonViewModel as MediaCommonViewModel.MediaControl).instanceId.toString()
+ private fun onRemoved(controlViewModel: MediaControlViewModel) {
+ val id = controlViewModel.instanceId
controllerById.remove(id)?.let {
mediaCarouselScrollHandler.onPrePlayerRemoved(it.mediaViewHolder!!.player)
mediaContent.removeView(it.mediaViewHolder!!.player)
it.onDestroy()
mediaCarouselScrollHandler.onPlayersChanged()
updatePageIndicator()
- commonViewModel.onRemoved(true)
+ controlViewModel.onRemoved(true)
}
}
- private fun onMoved(commonViewModel: MediaCommonViewModel, from: Int, to: Int) {
- val id = (commonViewModel as MediaCommonViewModel.MediaControl).instanceId.toString()
+ private fun onMoved(controlViewModel: MediaControlViewModel, from: Int, to: Int) {
+ val id = controlViewModel.instanceId
controllerById[id]?.let {
mediaContent.removeViewAt(from)
mediaContent.addView(it.mediaViewHolder!!.player, to)
@@ -667,19 +655,12 @@ constructor(
mediaCarouselScrollHandler.onPlayersChanged()
}
- private fun setNewViewModelsList(viewModels: List<MediaCommonViewModel>) {
- commonViewModels.clear()
- commonViewModels.addAll(viewModels)
+ private fun setNewViewModelsList(viewModels: List<MediaControlViewModel>) {
+ controlViewModels.clear()
+ controlViewModels.addAll(viewModels)
// Ensure we only show the needed UMOs in media carousel.
- val viewIds =
- viewModels
- .map { mediaCommonViewModel ->
- (mediaCommonViewModel as MediaCommonViewModel.MediaControl)
- .instanceId
- .toString()
- }
- .toHashSet()
+ val viewIds = viewModels.map { controlViewModel -> controlViewModel.instanceId }.toHashSet()
controllerById
.filter { !viewIds.contains(it.key) }
.forEach {
@@ -976,9 +957,8 @@ constructor(
ColorStateList.valueOf(context.getColor(R.color.media_paging_indicator))
if (recreateMedia) {
mediaContent.removeAllViews()
- commonViewModels.forEachIndexed { index, viewModel ->
- val mediaControlViewModel = (viewModel as MediaCommonViewModel.MediaControl)
- controllerById[mediaControlViewModel.instanceId.toString()]?.onDestroy()
+ controlViewModels.forEachIndexed { index, viewModel ->
+ controllerById[viewModel.instanceId]?.onDestroy()
onAdded(viewModel, index, configChanged = true)
}
}
@@ -1309,7 +1289,7 @@ constructor(
println("dataKeys: ${MediaPlayerData.dataKeys()}")
println("orderedPlayerSortKeys: ${MediaPlayerData.playerKeys()}")
println("visiblePlayerSortKeys: ${MediaPlayerData.visiblePlayerKeys()}")
- println("commonViewModels: $commonViewModels")
+ println("controlViewModels: $controlViewModels")
println("smartspaceMediaData: ${MediaPlayerData.smartspaceMediaData}")
println("shouldPrioritizeSs: ${MediaPlayerData.shouldPrioritizeSs}")
println("current size: $currentCarouselWidth x $currentCarouselHeight")
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelCallback.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelCallback.kt
index 2fc44ad3cce6..ad2d46263faf 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelCallback.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelCallback.kt
@@ -17,12 +17,12 @@
package com.android.systemui.media.controls.ui.util
import androidx.recyclerview.widget.DiffUtil
-import com.android.systemui.media.controls.ui.viewmodel.MediaCommonViewModel
+import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel
/** A [DiffUtil.Callback] to calculate difference between old and new media view-model list. */
class MediaViewModelCallback(
- private val old: List<MediaCommonViewModel>,
- private val new: List<MediaCommonViewModel>,
+ private val old: List<MediaControlViewModel>,
+ private val new: List<MediaControlViewModel>,
) : DiffUtil.Callback() {
override fun getOldListSize(): Int {
@@ -36,27 +36,12 @@ class MediaViewModelCallback(
override fun areItemsTheSame(oldIndex: Int, newIndex: Int): Boolean {
val oldItem = old[oldIndex]
val newItem = new[newIndex]
- return if (
- oldItem is MediaCommonViewModel.MediaControl &&
- newItem is MediaCommonViewModel.MediaControl
- ) {
- oldItem.instanceId == newItem.instanceId
- } else {
- false
- }
+ return oldItem.instanceId == newItem.instanceId
}
override fun areContentsTheSame(oldIndex: Int, newIndex: Int): Boolean {
val oldItem = old[oldIndex]
val newItem = new[newIndex]
- return if (
- oldItem is MediaCommonViewModel.MediaControl &&
- newItem is MediaCommonViewModel.MediaControl
- ) {
- oldItem.immediatelyUpdateUi == newItem.immediatelyUpdateUi &&
- oldItem.updateTime == newItem.updateTime
- } else {
- false
- }
+ return oldItem.updateTime == newItem.updateTime
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelListUpdateCallback.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelListUpdateCallback.kt
index 6022b7b1fc13..1db8c58d4140 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelListUpdateCallback.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelListUpdateCallback.kt
@@ -17,17 +17,17 @@
package com.android.systemui.media.controls.ui.util
import androidx.recyclerview.widget.ListUpdateCallback
-import com.android.systemui.media.controls.ui.viewmodel.MediaCommonViewModel
+import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel
import kotlin.math.min
/** A [ListUpdateCallback] to apply media events needed to reach the new state. */
class MediaViewModelListUpdateCallback(
- private val old: List<MediaCommonViewModel>,
- private val new: List<MediaCommonViewModel>,
- private val onAdded: (MediaCommonViewModel, Int) -> Unit,
- private val onUpdated: (MediaCommonViewModel, Int) -> Unit,
- private val onRemoved: (MediaCommonViewModel) -> Unit,
- private val onMoved: (MediaCommonViewModel, Int, Int) -> Unit,
+ private val old: List<MediaControlViewModel>,
+ private val new: List<MediaControlViewModel>,
+ private val onAdded: (MediaControlViewModel, Int) -> Unit,
+ private val onUpdated: (MediaControlViewModel, Int) -> Unit,
+ private val onRemoved: (MediaControlViewModel) -> Unit,
+ private val onMoved: (MediaControlViewModel, Int, Int) -> Unit,
) : ListUpdateCallback {
override fun onInserted(position: Int, count: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt
index dfaee4434bcf..54d3151694bd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt
@@ -56,7 +56,7 @@ constructor(
val hasAnyMediaOrRecommendations: StateFlow<Boolean> = interactor.hasAnyMediaOrRecommendation
val hasActiveMediaOrRecommendations: StateFlow<Boolean> =
interactor.hasActiveMediaOrRecommendation
- val mediaItems: StateFlow<List<MediaCommonViewModel>> =
+ val mediaItems: StateFlow<List<MediaControlViewModel>> =
interactor.currentMedia
.map { sortedItems ->
val mediaList = buildList {
@@ -91,8 +91,7 @@ constructor(
var updateHostVisibility: () -> Unit = {}
- private val mediaControlByInstanceId =
- mutableMapOf<InstanceId, MediaCommonViewModel.MediaControl>()
+ private val mediaControlByInstanceId = mutableMapOf<InstanceId, MediaControlViewModel>()
private var modelsPendingRemoval: MutableSet<MediaCommonModel> = mutableSetOf()
@@ -108,18 +107,16 @@ constructor(
interactor.reorderMedia()
}
- private fun toViewModel(
- commonModel: MediaCommonModel.MediaControl
- ): MediaCommonViewModel.MediaControl {
+ private fun toViewModel(commonModel: MediaCommonModel.MediaControl): MediaControlViewModel {
val instanceId = commonModel.mediaLoadedModel.instanceId
- return mediaControlByInstanceId[instanceId]?.copy(
- immediatelyUpdateUi = commonModel.mediaLoadedModel.immediatelyUpdateUi,
- updateTime = commonModel.updateTime,
- )
- ?: MediaCommonViewModel.MediaControl(
+ return mediaControlByInstanceId[instanceId]?.copy(updateTime = commonModel.updateTime)
+ ?: MediaControlViewModel(
+ applicationContext = applicationContext,
+ backgroundDispatcher = backgroundDispatcher,
+ backgroundExecutor = backgroundExecutor,
+ interactor = controlInteractorFactory.create(instanceId),
+ logger = logger,
instanceId = instanceId,
- immediatelyUpdateUi = commonModel.mediaLoadedModel.immediatelyUpdateUi,
- controlViewModel = createMediaControlViewModel(instanceId),
onAdded = {
mediaLogger.logMediaCardAdded(instanceId)
onMediaControlAddedOrUpdated(it, commonModel)
@@ -130,31 +127,20 @@ constructor(
mediaLogger.logMediaCardRemoved(instanceId)
},
onUpdated = { onMediaControlAddedOrUpdated(it, commonModel) },
- isMediaFromRec = commonModel.isMediaFromRec,
updateTime = commonModel.updateTime,
)
.also { mediaControlByInstanceId[instanceId] = it }
}
- private fun createMediaControlViewModel(instanceId: InstanceId): MediaControlViewModel {
- return MediaControlViewModel(
- applicationContext = applicationContext,
- backgroundDispatcher = backgroundDispatcher,
- backgroundExecutor = backgroundExecutor,
- interactor = controlInteractorFactory.create(instanceId),
- logger = logger,
- )
- }
-
private fun onMediaControlAddedOrUpdated(
- commonViewModel: MediaCommonViewModel,
+ controlViewModel: MediaControlViewModel,
commonModel: MediaCommonModel.MediaControl,
) {
if (commonModel.canBeRemoved && !Utils.useMediaResumption(applicationContext)) {
// This media control is due for removal as it is now paused + timed out, and resumption
// setting is off.
if (isReorderingAllowed()) {
- commonViewModel.onRemoved(true)
+ controlViewModel.onRemoved(true)
} else {
modelsPendingRemoval.add(commonModel)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCommonViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCommonViewModel.kt
deleted file mode 100644
index d493d57051f7..000000000000
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCommonViewModel.kt
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.media.controls.ui.viewmodel
-
-import com.android.internal.logging.InstanceId
-
-/** Models media view model UI state. */
-sealed class MediaCommonViewModel {
-
- abstract val onAdded: (MediaCommonViewModel) -> Unit
- abstract val onRemoved: (Boolean) -> Unit
- abstract val onUpdated: (MediaCommonViewModel) -> Unit
-
- data class MediaControl(
- val instanceId: InstanceId,
- val immediatelyUpdateUi: Boolean,
- val controlViewModel: MediaControlViewModel,
- override val onAdded: (MediaCommonViewModel) -> Unit,
- override val onRemoved: (Boolean) -> Unit,
- override val onUpdated: (MediaCommonViewModel) -> Unit,
- val isMediaFromRec: Boolean = false,
- val updateTime: Long = 0,
- ) : MediaCommonViewModel()
-}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
index 015274a10330..31cfb8479dd1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
@@ -45,12 +45,17 @@ import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
/** Models UI state and handles user input for a media control. */
-class MediaControlViewModel(
+data class MediaControlViewModel(
@Application private val applicationContext: Context,
@Background private val backgroundDispatcher: CoroutineDispatcher,
@Background private val backgroundExecutor: Executor,
private val interactor: MediaControlInteractor,
private val logger: MediaUiEventLogger,
+ val instanceId: InstanceId,
+ val onAdded: (MediaControlViewModel) -> Unit,
+ val onRemoved: (Boolean) -> Unit,
+ val onUpdated: (MediaControlViewModel) -> Unit,
+ val updateTime: Long = 0,
) {
val player: Flow<MediaPlayerViewModel?> =
interactor.mediaControl
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterBase.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterBase.java
index ac1672db9375..e3990d25f94e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterBase.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterBase.java
@@ -237,8 +237,7 @@ public abstract class MediaOutputAdapterBase extends RecyclerView.Adapter<Recycl
clickListener = v -> onItemClick(v, device);
}
} else {
- deviceStatusIcon = getDeviceStatusIcon(device,
- device.hasOngoingSession());
+ deviceStatusIcon = getDeviceStatusIcon(device);
clickListener = getClickListenerBasedOnSelectionBehavior(device);
}
deviceDisabled = clickListener == null;
@@ -302,12 +301,8 @@ public abstract class MediaOutputAdapterBase extends RecyclerView.Adapter<Recycl
}
@Nullable
- private Drawable getDeviceStatusIcon(MediaDevice device, boolean hasOngoingSession) {
- if (hasOngoingSession) {
- return mContext.getDrawable(R.drawable.ic_sound_bars_anim);
- } else {
- return Api34Impl.getDeviceStatusIconBasedOnSelectionBehavior(device, mContext);
- }
+ private Drawable getDeviceStatusIcon(MediaDevice device) {
+ return Api34Impl.getDeviceStatusIconBasedOnSelectionBehavior(device, mContext);
}
protected void onExpandGroupButtonClicked() {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterLegacy.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterLegacy.java
index 6ab4a52dc919..7ab6b3cfb8b1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterLegacy.java
@@ -231,7 +231,7 @@ public class MediaOutputAdapterLegacy extends MediaOutputAdapterBase {
updateFullItemClickListener(clickListener);
updateContentAlpha(deviceDisabled);
updateSubtitle(subtitle);
- updateDeviceStatusIcon(deviceStatusIcon);
+ updateDeviceStatusIcon(deviceStatusIcon, ongoingSessionStatus, connectionState);
updateItemBackground(connectionState);
}
@@ -523,11 +523,20 @@ public class MediaOutputAdapterLegacy extends MediaOutputAdapterBase {
mStatusIcon.setAlpha(alphaValue);
}
- private void updateDeviceStatusIcon(@Nullable Drawable deviceStatusIcon) {
- if (deviceStatusIcon == null) {
+ private void updateDeviceStatusIcon(@Nullable Drawable deviceStatusIcon,
+ @Nullable OngoingSessionStatus ongoingSessionStatus,
+ ConnectionState connectionState) {
+ boolean showOngoingSession =
+ ongoingSessionStatus != null && connectionState == ConnectionState.DISCONNECTED;
+ if (deviceStatusIcon == null && !showOngoingSession) {
mStatusIcon.setVisibility(View.GONE);
} else {
- mStatusIcon.setImageDrawable(deviceStatusIcon);
+ if (showOngoingSession) {
+ mStatusIcon.setImageDrawable(
+ mContext.getDrawable(R.drawable.ic_sound_bars_anim));
+ } else {
+ mStatusIcon.setImageDrawable(deviceStatusIcon);
+ }
mStatusIcon.setImageTintList(ColorStateList.valueOf(
mController.getColorSchemeLegacy().getColorItemContent()));
if (deviceStatusIcon instanceof AnimatedVectorDrawable) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 2a23620839e5..6a2c4519e1a5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -35,7 +35,6 @@ import android.view.ViewGroup
import android.view.WindowManager
import android.view.accessibility.AccessibilityManager
import com.android.app.animation.Interpolators
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.internal.logging.InstanceId
import com.android.internal.widget.CachingIconView
import com.android.systemui.common.shared.model.ContentDescription
@@ -73,7 +72,7 @@ constructor(
private val commandQueue: CommandQueue,
context: Context,
logger: MediaTttReceiverLogger,
- viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
+ windowManager: WindowManager,
@Main private val mainExecutor: DelayableExecutor,
accessibilityManager: AccessibilityManager,
configurationController: ConfigurationController,
@@ -90,7 +89,7 @@ constructor(
TemporaryViewDisplayController<ChipReceiverInfo, MediaTttReceiverLogger>(
context,
logger,
- viewCaptureAwareWindowManager,
+ windowManager,
mainExecutor,
accessibilityManager,
configurationController,
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt
index 9265bfb2f66b..5ffa7fa0ef8d 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt
@@ -26,13 +26,13 @@ import android.graphics.Shader
import android.util.AttributeSet
import android.view.View
import android.view.WindowManager
-import androidx.core.content.getSystemService
import androidx.core.content.res.use
import com.android.systemui.res.R
import com.android.systemui.mediaprojection.appselector.data.RecentTask
import com.android.systemui.shared.recents.model.ThumbnailData
import com.android.systemui.shared.recents.utilities.PreviewPositionHelper
import com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen
+import com.android.systemui.utils.windowmanager.WindowManagerUtils
/**
* Custom view that shows a thumbnail preview of one recent task based on [ThumbnailData].
@@ -53,7 +53,7 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
}
}
- private val windowManager: WindowManager = context.getSystemService()!!
+ private val windowManager: WindowManager = WindowManagerUtils.getWindowManager(context)
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
private val backgroundPaint =
Paint(Paint.ANTI_ALIAS_FLAG).apply { color = defaultBackgroundColor }
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt
index c829471f53f3..57fd1e790f0b 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt
@@ -19,6 +19,7 @@ package com.android.systemui.mediaprojection.appselector.view
import android.content.Context
import android.content.res.Configuration
import android.graphics.Rect
+import android.view.WindowManager
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorScope
@@ -36,6 +37,7 @@ constructor(
private val context: Context,
private val windowMetricsProvider: WindowMetricsProvider,
private val configurationController: ConfigurationController,
+ private val windowManager: WindowManager,
) : CallbackController<TaskPreviewSizeListener>, ConfigurationListener, DefaultLifecycleObserver {
/** Returns the size of the task preview on the screen in pixels */
@@ -65,7 +67,7 @@ constructor(
val width = maxWindowBounds.width()
var height = maximumWindowHeight
- val isLargeScreen = isLargeScreen(context)
+ val isLargeScreen = isLargeScreen(windowManager, context.resources)
if (isLargeScreen) {
val taskbarSize = windowMetricsProvider.currentWindowInsets.bottom
height -= taskbarSize
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java
index 39482bea0111..c5c8c01f8b39 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java
@@ -16,17 +16,12 @@
package com.android.systemui.navigationbar;
-import static com.android.systemui.Flags.enableViewCaptureTracing;
-import static com.android.systemui.util.ConvenienceExtensionsKt.toKotlinLazy;
-
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import com.android.app.displaylib.PerDisplayRepository;
-import com.android.app.viewcapture.ViewCapture;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationBarComponent.NavigationBarScope;
@@ -34,8 +29,8 @@ import com.android.systemui.navigationbar.views.NavigationBarFrame;
import com.android.systemui.navigationbar.views.NavigationBarView;
import com.android.systemui.res.R;
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
-import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
@@ -70,8 +65,9 @@ public interface NavigationBarModule {
@Provides
@NavigationBarScope
@DisplayId
- static WindowManager provideWindowManager(@DisplayId Context context) {
- return context.getSystemService(WindowManager.class);
+ static WindowManager provideWindowManager(@DisplayId Context context,
+ WindowManagerProvider windowManagerProvider) {
+ return windowManagerProvider.getWindowManager(context);
}
/** A SysUiState for the navigation bar display. */
@@ -87,15 +83,4 @@ public interface NavigationBarModule {
return defaultState;
}
}
-
- /** A ViewCaptureAwareWindowManager specific to the display's context. */
- @Provides
- @NavigationBarScope
- @DisplayId
- static ViewCaptureAwareWindowManager provideViewCaptureAwareWindowManager(
- @DisplayId WindowManager windowManager, Lazy<ViewCapture> daggerLazyViewCapture) {
- return new ViewCaptureAwareWindowManager(windowManager,
- /* lazyViewCapture= */ toKotlinLazy(daggerLazyViewCapture),
- /* isViewCaptureEnabled= */ enableViewCaptureTracing());
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
index 44c828731e24..6d7492686985 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -33,7 +33,6 @@ import androidx.annotation.VisibleForTesting
import androidx.core.os.postDelayed
import androidx.core.view.isVisible
import androidx.dynamicanimation.animation.DynamicAnimation
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.internal.jank.Cuj
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.util.LatencyTracker
@@ -85,7 +84,7 @@ class BackPanelController
@AssistedInject
constructor(
@Assisted context: Context,
- private val windowManager: ViewCaptureAwareWindowManager,
+ private val windowManager: WindowManager,
private val viewConfiguration: ViewConfiguration,
@Assisted private val mainHandler: Handler,
private val systemClock: SystemClock,
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
index 8b5b3adeef1f..ad0acbdaf702 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
@@ -104,7 +104,6 @@ import android.view.inputmethod.InputMethodManager;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
@@ -199,7 +198,6 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
private final Context mContext;
private final Bundle mSavedState;
private final WindowManager mWindowManager;
- private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
private final AccessibilityManager mAccessibilityManager;
private final DeviceProvisionedController mDeviceProvisionedController;
private final StatusBarStateController mStatusBarStateController;
@@ -560,7 +558,6 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
@Nullable Bundle savedState,
@DisplayId Context context,
@DisplayId WindowManager windowManager,
- @DisplayId ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
Lazy<AssistManager> assistManagerLazy,
AccessibilityManager accessibilityManager,
DeviceProvisionedController deviceProvisionedController,
@@ -605,7 +602,6 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
mContext = context;
mSavedState = savedState;
mWindowManager = windowManager;
- mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager;
mAccessibilityManager = accessibilityManager;
mDeviceProvisionedController = deviceProvisionedController;
mStatusBarStateController = statusBarStateController;
@@ -726,7 +722,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mView);
try {
- mViewCaptureAwareWindowManager.addView(
+ mWindowManager.addView(
mFrame,
getBarLayoutParams(
mContext.getResources()
@@ -783,7 +779,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
mCommandQueue.removeCallback(this);
Trace.beginSection("NavigationBar#removeViewImmediate");
try {
- mViewCaptureAwareWindowManager.removeViewImmediate(mView.getRootView());
+ mWindowManager.removeViewImmediate(mView.getRootView());
} catch (IllegalArgumentException e) {
// Wrapping this in a try/catch to avoid crashes when a display is instantly removed
// after being added, and initialization hasn't finished yet.
@@ -888,7 +884,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
resetSecondaryHandle();
getBarTransitions().removeDarkIntensityListener(mOrientationHandleIntensityListener);
try {
- mViewCaptureAwareWindowManager.removeView(mOrientationHandle);
+ mWindowManager.removeView(mOrientationHandle);
} catch (IllegalArgumentException e) {
// Wrapping this in a try/catch to avoid crashes when a display is instantly removed
// after being added, and initialization hasn't finished yet.
@@ -967,7 +963,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
mOrientationParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION
| WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT;
try {
- mViewCaptureAwareWindowManager.addView(mOrientationHandle, mOrientationParams);
+ mWindowManager.addView(mOrientationHandle, mOrientationParams);
} catch (WindowManager.InvalidDisplayException e) {
// Wrapping this in a try/catch to avoid crashes when a display is instantly removed
// after being added, and initialization hasn't finished yet.
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java
index cbc4c26b2f94..d2974e9f90bd 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java
@@ -87,6 +87,7 @@ import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.LightBarTransitionsController;
+import com.android.systemui.utils.windowmanager.WindowManagerUtils;
import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.pip.Pip;
@@ -729,7 +730,7 @@ public class NavigationBarView extends FrameLayout {
} else {
return;
}
- WindowManager wm = getContext().getSystemService(WindowManager.class);
+ WindowManager wm = WindowManagerUtils.getWindowManager(getContext());
wm.updateViewLayout((View) getParent(), lp);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt
index 9319961f5b68..0a5222e3df44 100644
--- a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt
@@ -21,8 +21,7 @@ import com.android.compose.animation.scene.Swipe
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.compose.animation.scene.UserActionResult.HideOverlay
-import com.android.compose.animation.scene.UserActionResult.ShowOverlay
-import com.android.compose.animation.scene.UserActionResult.ShowOverlay.HideCurrentOverlays
+import com.android.compose.animation.scene.UserActionResult.ReplaceByOverlay
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.ui.viewmodel.SceneContainerArea
import com.android.systemui.scene.ui.viewmodel.UserActionsViewModel
@@ -38,11 +37,8 @@ class NotificationsShadeOverlayActionsViewModel @AssistedInject constructor() :
mapOf(
Swipe.Up to HideOverlay(Overlays.NotificationsShade),
Back to HideOverlay(Overlays.NotificationsShade),
- Swipe.Down(fromSource = SceneContainerArea.EndHalf) to
- ShowOverlay(
- Overlays.QuickSettingsShade,
- hideCurrentOverlays = HideCurrentOverlays.Some(Overlays.NotificationsShade),
- ),
+ Swipe.Down(fromSource = SceneContainerArea.TopEdgeEndHalf) to
+ ReplaceByOverlay(Overlays.QuickSettingsShade),
)
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningView.java b/packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningView.java
index 2ecca2d8c776..f37a86e2b9d5 100644
--- a/packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningView.java
+++ b/packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningView.java
@@ -16,8 +16,6 @@
package com.android.systemui.power;
-import static com.android.systemui.Flags.enableViewCaptureTracing;
-
import android.animation.Animator;
import android.animation.AnimatorInflater;
import android.animation.AnimatorListenerAdapter;
@@ -30,28 +28,21 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.FrameLayout;
-
-import com.android.app.viewcapture.ViewCapture;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.systemui.res.R;
-import kotlin.Lazy;
-
/**
* View that shows a warning shortly before the device goes into sleep
* after prolonged user inactivity when bound to.
*/
public class InattentiveSleepWarningView extends FrameLayout {
private final IBinder mWindowToken = new Binder();
- private final ViewCaptureAwareWindowManager mWindowManager;
+ private final WindowManager mWindowManager;
private Animator mFadeOutAnimator;
private boolean mDismissing;
- InattentiveSleepWarningView(Context context, Lazy<ViewCapture> lazyViewCapture) {
+ InattentiveSleepWarningView(Context context, WindowManager windowManager) {
super(context);
- WindowManager wm = mContext.getSystemService(WindowManager.class);
- mWindowManager = new ViewCaptureAwareWindowManager(wm, lazyViewCapture,
- enableViewCaptureTracing());
+ mWindowManager = windowManager;
final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
layoutInflater.inflate(R.layout.inattentive_sleep_warning, this, true /* attachToRoot */);
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 861a7ce282af..4e1bfd15e58c 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -16,8 +16,6 @@
package com.android.systemui.power;
-import static com.android.systemui.util.ConvenienceExtensionsKt.toKotlinLazy;
-
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -42,11 +40,11 @@ import android.service.vr.IVrStateCallbacks;
import android.text.format.DateUtils;
import android.util.Log;
import android.util.Slog;
+import android.view.WindowManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.app.viewcapture.ViewCapture;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.fuelgauge.Estimate;
import com.android.settingslib.utils.ThreadUtils;
@@ -59,8 +57,6 @@ import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.ConfigurationController;
-import kotlin.Lazy;
-
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.concurrent.Future;
@@ -120,9 +116,9 @@ public class PowerUI implements
private IThermalEventListener mSkinThermalEventListener;
private IThermalEventListener mUsbThermalEventListener;
private final Context mContext;
+ private final WindowManager mWindowManager;
private final BroadcastDispatcher mBroadcastDispatcher;
private final CommandQueue mCommandQueue;
- private final Lazy<ViewCapture> mLazyViewCapture;
@Nullable
private final IVrManager mVrManager;
private final WakefulnessLifecycle.Observer mWakefulnessObserver =
@@ -164,7 +160,7 @@ public class PowerUI implements
WakefulnessLifecycle wakefulnessLifecycle,
PowerManager powerManager,
UserTracker userTracker,
- dagger.Lazy<ViewCapture> daggerLazyViewCapture) {
+ WindowManager windowManager) {
mContext = context;
mBroadcastDispatcher = broadcastDispatcher;
mCommandQueue = commandQueue;
@@ -174,7 +170,7 @@ public class PowerUI implements
mPowerManager = powerManager;
mWakefulnessLifecycle = wakefulnessLifecycle;
mUserTracker = userTracker;
- mLazyViewCapture = toKotlinLazy(daggerLazyViewCapture);
+ mWindowManager = windowManager;
}
public void start() {
@@ -649,7 +645,7 @@ public class PowerUI implements
@Override
public void showInattentiveSleepWarning() {
if (mOverlayView == null) {
- mOverlayView = new InattentiveSleepWarningView(mContext, mLazyViewCapture);
+ mOverlayView = new InattentiveSleepWarningView(mContext, mWindowManager);
}
mOverlayView.show();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesDndTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesDndTile.kt
index 43e3de2040d7..0236256ec9ae 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesDndTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesDndTile.kt
@@ -37,6 +37,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.asQSTileIcon
+import com.android.systemui.qs.flags.QsInCompose
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigProvider
@@ -88,7 +89,11 @@ constructor(
init {
lifecycle.coroutineScope.launch {
- lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) {
+ lifecycle.repeatOnLifecycle(
+ // TODO: b/403434908 - Workaround for "not listening to tile updates". Can be reset
+ // to RESUMED if either b/403434908 is fixed or QsInCompose is inlined.
+ if (QsInCompose.isEnabled) Lifecycle.State.RESUMED else Lifecycle.State.CREATED
+ ) {
dataInteractor.tileData().collect { refreshState(it) }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt
index fcfa46f07ab4..6a842c31a3aa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt
@@ -31,7 +31,6 @@ import com.android.systemui.animation.Expandable
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.modes.shared.ModesUi
-import com.android.systemui.modes.shared.ModesUiIcons
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.qs.QSTile
@@ -40,6 +39,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.asQSTileIcon
+import com.android.systemui.qs.flags.QsInCompose
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigProvider
@@ -88,10 +88,12 @@ constructor(
private val config = qsTileConfigProvider.getConfig(TILE_SPEC)
init {
- /* Check if */ ModesUiIcons.isUnexpectedlyInLegacyMode()
-
lifecycle.coroutineScope.launch {
- lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) {
+ lifecycle.repeatOnLifecycle(
+ // TODO: b/403434908 - Workaround for "not listening to tile updates". Can be reset
+ // to RESUMED if either b/403434908 is fixed or QsInCompose is inlined.
+ if (QsInCompose.isEnabled) Lifecycle.State.RESUMED else Lifecycle.State.CREATED
+ ) {
dataInteractor.tileData().collect { refreshState(it) }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentController.java
index 6b5a22a4fc09..945e051606b9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentController.java
@@ -65,7 +65,6 @@ import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.annotation.WorkerThread;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.logging.UiEventLogger;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
@@ -186,7 +185,7 @@ public class InternetDetailsContentController implements AccessPointController.A
private GlobalSettings mGlobalSettings;
private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private ConnectivityManager.NetworkCallback mConnectivityManagerNetworkCallback;
- private ViewCaptureAwareWindowManager mWindowManager;
+ private WindowManager mWindowManager;
private ToastFactory mToastFactory;
private SignalDrawable mSignalDrawable;
private SignalDrawable mSecondarySignalDrawable; // For the secondary mobile data sub in DSDS
@@ -252,7 +251,7 @@ public class InternetDetailsContentController implements AccessPointController.A
@Main Handler handler, @Main Executor mainExecutor,
BroadcastDispatcher broadcastDispatcher, KeyguardUpdateMonitor keyguardUpdateMonitor,
GlobalSettings globalSettings, KeyguardStateController keyguardStateController,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager, ToastFactory toastFactory,
+ @ShadeDisplayAware WindowManager windowManager, ToastFactory toastFactory,
@Background Handler workerHandler,
CarrierConfigTracker carrierConfigTracker,
LocationController locationController,
@@ -284,7 +283,7 @@ public class InternetDetailsContentController implements AccessPointController.A
mAccessPointController = accessPointController;
mWifiIconInjector = new WifiUtils.InternetIconInjector(mContext);
mConnectivityManagerNetworkCallback = new DataConnectivityListener();
- mWindowManager = viewCaptureAwareWindowManager;
+ mWindowManager = windowManager;
mToastFactory = toastFactory;
mSignalDrawable = new SignalDrawable(mContext);
mSecondarySignalDrawable = new SignalDrawable(mContext);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt
index f9b1a36621b2..11622be3d417 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt
@@ -77,7 +77,7 @@ constructor(
val showMedia: Boolean by
hydrator.hydratedStateOf(
traceName = "showMedia",
- source = mediaCarouselInteractor.hasActiveMediaOrRecommendation,
+ source = mediaCarouselInteractor.hasAnyMediaOrRecommendation,
)
override suspend fun onActivated(): Nothing {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt
index 52c4e2fac6d5..2f7bc0992411 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt
@@ -21,8 +21,7 @@ import com.android.compose.animation.scene.Swipe
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.compose.animation.scene.UserActionResult.HideOverlay
-import com.android.compose.animation.scene.UserActionResult.ShowOverlay
-import com.android.compose.animation.scene.UserActionResult.ShowOverlay.HideCurrentOverlays
+import com.android.compose.animation.scene.UserActionResult.ReplaceByOverlay
import com.android.systemui.qs.panels.ui.viewmodel.EditModeViewModel
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.ui.viewmodel.SceneContainerArea
@@ -47,12 +46,8 @@ constructor(private val editModeViewModel: EditModeViewModel) : UserActionsViewM
put(Back, HideOverlay(Overlays.QuickSettingsShade))
}
put(
- Swipe.Down(fromSource = SceneContainerArea.StartHalf),
- ShowOverlay(
- Overlays.NotificationsShade,
- hideCurrentOverlays =
- HideCurrentOverlays.Some(Overlays.QuickSettingsShade),
- ),
+ Swipe.Down(fromSource = SceneContainerArea.TopEdgeStartHalf),
+ ReplaceByOverlay(Overlays.NotificationsShade),
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 432a35a1a3dd..a1281ec23f92 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -16,9 +16,7 @@
package com.android.systemui.recents;
-import static com.android.systemui.Flags.enableViewCaptureTracing;
import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen;
-import static com.android.systemui.util.ConvenienceExtensionsKt.toKotlinLazy;
import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE;
import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE;
import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE;
@@ -55,8 +53,6 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
-import com.android.app.viewcapture.ViewCapture;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.systemui.CoreStartable;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
@@ -87,7 +83,6 @@ public class ScreenPinningRequest implements
private final Lazy<NavigationBarController> mNavigationBarControllerLazy;
private final AccessibilityManager mAccessibilityService;
private final WindowManager mWindowManager;
- private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
private final BroadcastDispatcher mBroadcastDispatcher;
private final UserTracker mUserTracker;
@@ -112,15 +107,12 @@ public class ScreenPinningRequest implements
Lazy<NavigationBarController> navigationBarControllerLazy,
BroadcastDispatcher broadcastDispatcher,
UserTracker userTracker,
- Lazy<ViewCapture> daggerLazyViewCapture) {
+ WindowManager windowManager) {
mContext = context;
mNavigationBarControllerLazy = navigationBarControllerLazy;
mAccessibilityService = (AccessibilityManager)
mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
- mWindowManager = (WindowManager)
- mContext.getSystemService(Context.WINDOW_SERVICE);
- mViewCaptureAwareWindowManager = new ViewCaptureAwareWindowManager(mWindowManager,
- toKotlinLazy(daggerLazyViewCapture), enableViewCaptureTracing());
+ mWindowManager = windowManager;
mNavBarMode = navigationModeController.addListener(this);
mBroadcastDispatcher = broadcastDispatcher;
mUserTracker = userTracker;
@@ -131,7 +123,7 @@ public class ScreenPinningRequest implements
public void clearPrompt() {
if (mRequestWindow != null) {
- mViewCaptureAwareWindowManager.removeView(mRequestWindow);
+ mWindowManager.removeView(mRequestWindow);
mRequestWindow = null;
}
}
@@ -152,7 +144,7 @@ public class ScreenPinningRequest implements
// show the confirmation
WindowManager.LayoutParams lp = getWindowLayoutParams();
- mViewCaptureAwareWindowManager.addView(mRequestWindow, lp);
+ mWindowManager.addView(mRequestWindow, lp);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerSwipeDetector.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerSwipeDetector.kt
index ede453dbe6b3..760b97e6245b 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerSwipeDetector.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerSwipeDetector.kt
@@ -58,6 +58,22 @@ sealed class SceneContainerArea(private val resolveArea: (LayoutDirection) -> Re
}
)
+ data object TopEdgeStartHalf :
+ SceneContainerArea(
+ resolveArea = {
+ if (it == LayoutDirection.Ltr) Resolved.TopEdgeLeftHalf
+ else Resolved.TopEdgeRightHalf
+ }
+ )
+
+ data object TopEdgeEndHalf :
+ SceneContainerArea(
+ resolveArea = {
+ if (it == LayoutDirection.Ltr) Resolved.TopEdgeRightHalf
+ else Resolved.TopEdgeLeftHalf
+ }
+ )
+
override fun resolve(layoutDirection: LayoutDirection): Resolved {
return resolveArea(layoutDirection)
}
@@ -72,6 +88,12 @@ sealed class SceneContainerArea(private val resolveArea: (LayoutDirection) -> Re
data object RightEdge : Resolved
data object RightHalf : Resolved
+
+ /** The left half of the top edge of the display. */
+ data object TopEdgeLeftHalf : Resolved
+
+ /** The right half of the top edge of the display. */
+ data object TopEdgeRightHalf : Resolved
}
}
@@ -108,9 +130,14 @@ class SceneContainerSwipeDetector(val edgeSize: Dp) : SwipeSourceDetector {
Edge.Resolved.Left -> SceneContainerArea.Resolved.LeftEdge
Edge.Resolved.Bottom -> SceneContainerArea.Resolved.BottomEdge
Edge.Resolved.Right -> SceneContainerArea.Resolved.RightEdge
- else -> {
- // Note: This intentionally includes Edge.Resolved.Top. At the moment, we don't need
- // to detect swipes on the top edge, and consider them part of the right/left half.
+ Edge.Resolved.Top -> {
+ if (position.x < layoutSize.width * 0.5f) {
+ SceneContainerArea.Resolved.TopEdgeLeftHalf
+ } else {
+ SceneContainerArea.Resolved.TopEdgeRightHalf
+ }
+ }
+ null -> {
if (position.x < layoutSize.width * 0.5f) {
SceneContainerArea.Resolved.LeftHalf
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotWindow.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotWindow.kt
index c4fe7a428084..644e12cba6fc 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotWindow.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotWindow.kt
@@ -31,7 +31,6 @@ import android.view.Window
import android.view.WindowInsets
import android.view.WindowManager
import android.window.WindowContext
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.internal.policy.PhoneWindow
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@@ -42,7 +41,6 @@ class ScreenshotWindow
@AssistedInject
constructor(
private val windowManager: WindowManager,
- private val viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
private val context: Context,
@Assisted private val display: Display,
) {
@@ -97,7 +95,7 @@ constructor(
Log.d(TAG, "attachWindow")
}
attachRequested = true
- viewCaptureAwareWindowManager.addView(decorView, params)
+ windowManager.addView(decorView, params)
decorView.requestApplyInsets()
decorView.requireViewById<ViewGroup>(R.id.content).apply {
@@ -135,7 +133,7 @@ constructor(
if (LogConfig.DEBUG_WINDOW) {
Log.d(TAG, "Removing screenshot window")
}
- viewCaptureAwareWindowManager.removeViewImmediate(decorView)
+ windowManager.removeViewImmediate(decorView)
detachRequested = false
}
if (attachRequested && !detachRequested) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
index c671f7d9db14..4ad4ab3410d6 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
@@ -102,6 +102,7 @@ import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.SplitShadeStateController;
import com.android.systemui.util.LargeScreenUtils;
import com.android.systemui.util.kotlin.JavaAdapter;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import dalvik.annotation.optimization.NeverCompile;
@@ -299,6 +300,8 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum
private final Runnable mQsCollapseExpandAction = this::collapseOrExpandQs;
private final QS.ScrollListener mQsScrollListener = this::onScroll;
+ private final WindowManagerProvider mWindowManagerProvider;
+
@Inject
public QuickSettingsControllerImpl(
Lazy<NotificationPanelViewController> panelViewControllerLazy,
@@ -336,7 +339,8 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum
CastController castController,
SplitShadeStateController splitShadeStateController,
Lazy<CommunalTransitionViewModel> communalTransitionViewModelLazy,
- Lazy<LargeScreenHeaderHelper> largeScreenHeaderHelperLazy
+ Lazy<LargeScreenHeaderHelper> largeScreenHeaderHelperLazy,
+ WindowManagerProvider windowManagerProvider
) {
SceneContainerFlag.assertInLegacyMode();
mPanelViewControllerLazy = panelViewControllerLazy;
@@ -387,6 +391,8 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum
mLockscreenShadeTransitionController.addCallback(new LockscreenShadeTransitionCallback());
dumpManager.registerDumpable(this);
+
+ mWindowManagerProvider = windowManagerProvider;
}
@VisibleForTesting
@@ -532,7 +538,7 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum
* on ACTION_DOWN, and safely queried repeatedly thereafter during ACTION_MOVE events.
*/
void updateGestureInsetsCache() {
- WindowManager wm = this.mPanelView.getContext().getSystemService(WindowManager.class);
+ WindowManager wm = mWindowManagerProvider.getWindowManager(this.mPanelView.getContext());
WindowMetrics windowMetrics = wm.getCurrentWindowMetrics();
mCachedGestureInsets = windowMetrics.getWindowInsets().getInsets(
WindowInsets.Type.systemGestures());
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
index e6834ad6cdda..b211f0729318 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
@@ -97,6 +97,11 @@ constructor(
loggingReason = "ShadeControllerSceneImpl.instantCollapseShade",
transitionKey = Instant,
)
+
+ shadeInteractor.collapseQuickSettingsShade(
+ loggingReason = "ShadeControllerSceneImpl.instantCollapseShade",
+ transitionKey = Instant,
+ )
}
override fun animateCollapseShade(
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt
index cd224735cc62..446d4b450edc 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt
@@ -49,6 +49,8 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackRebind
import com.android.systemui.statusbar.phone.ConfigurationControllerImpl
import com.android.systemui.statusbar.phone.ConfigurationForwarder
import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.utils.windowmanager.WindowManagerProvider
+import com.android.systemui.utils.windowmanager.WindowManagerUtils
import dagger.Module
import dagger.Provides
import dagger.multibindings.ClassKey
@@ -111,9 +113,10 @@ object ShadeDisplayAwareModule {
fun provideShadeWindowManager(
defaultWindowManager: WindowManager,
@ShadeDisplayAware context: Context,
+ windowManagerProvider: WindowManagerProvider
): WindowManager {
return if (ShadeWindowGoesAround.isEnabled) {
- context.getSystemService(WindowManager::class.java) as WindowManager
+ windowManagerProvider.getWindowManager(context)
} else {
defaultWindowManager
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
index 52de0abf7d3c..043742245227 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
@@ -187,12 +187,18 @@ constructor(
override fun collapseNotificationsShade(loggingReason: String, transitionKey: TransitionKey?) {
if (shadeModeInteractor.isDualShade) {
- // TODO(b/356596436): Hide without animation if transitionKey is Instant.
- sceneInteractor.hideOverlay(
- overlay = Overlays.NotificationsShade,
- loggingReason = loggingReason,
- transitionKey = transitionKey,
- )
+ if (transitionKey == Instant) {
+ sceneInteractor.instantlyHideOverlay(
+ overlay = Overlays.NotificationsShade,
+ loggingReason = loggingReason,
+ )
+ } else {
+ sceneInteractor.hideOverlay(
+ overlay = Overlays.NotificationsShade,
+ loggingReason = loggingReason,
+ transitionKey = transitionKey,
+ )
+ }
} else if (transitionKey == Instant) {
// TODO(b/356596436): Define instant transition instead of snapToScene().
sceneInteractor.snapToScene(
@@ -215,12 +221,18 @@ constructor(
bypassNotificationsShade: Boolean,
) {
if (shadeModeInteractor.isDualShade) {
- // TODO(b/356596436): Hide without animation if transitionKey is Instant.
- sceneInteractor.hideOverlay(
- overlay = Overlays.QuickSettingsShade,
- loggingReason = loggingReason,
- transitionKey = transitionKey,
- )
+ if (transitionKey == Instant) {
+ sceneInteractor.instantlyHideOverlay(
+ overlay = Overlays.QuickSettingsShade,
+ loggingReason = loggingReason,
+ )
+ } else {
+ sceneInteractor.hideOverlay(
+ overlay = Overlays.QuickSettingsShade,
+ loggingReason = loggingReason,
+ transitionKey = transitionKey,
+ )
+ }
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
index 88242762da78..c264e3525026 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
@@ -276,15 +276,17 @@ constructor(
data object Weak : HeaderChipHighlight {
override fun backgroundColor(colorScheme: ColorScheme): Color =
- colorScheme.primary.copy(alpha = 0.1f)
+ colorScheme.surface.copy(alpha = 0.1f)
- override fun foregroundColor(colorScheme: ColorScheme): Color = colorScheme.primary
+ override fun foregroundColor(colorScheme: ColorScheme): Color = colorScheme.onSurface
}
data object Strong : HeaderChipHighlight {
- override fun backgroundColor(colorScheme: ColorScheme): Color = colorScheme.secondary
+ override fun backgroundColor(colorScheme: ColorScheme): Color =
+ colorScheme.primaryContainer
- override fun foregroundColor(colorScheme: ColorScheme): Color = colorScheme.onSecondary
+ override fun foregroundColor(colorScheme: ColorScheme): Color =
+ colorScheme.onPrimaryContainer
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt
index cf3b08c041be..8d2d9efb56eb 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt
@@ -71,6 +71,8 @@ fun dualShadeActions(): Array<Pair<UserAction, UserActionResult>> {
Swipe.Down to ShowOverlay(Overlays.NotificationsShade),
Swipe.Down(fromSource = SceneContainerArea.EndHalf) to
ShowOverlay(Overlays.QuickSettingsShade),
+ Swipe.Down(fromSource = SceneContainerArea.TopEdgeEndHalf) to
+ ShowOverlay(Overlays.QuickSettingsShade),
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java b/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java
index 97e62d79b374..2a9a47d83dd4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java
@@ -28,9 +28,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
import static android.window.DisplayAreaOrganizer.KEY_ROOT_DISPLAY_AREA_ID;
-import static com.android.systemui.Flags.enableViewCaptureTracing;
-import static com.android.systemui.util.ConvenienceExtensionsKt.toKotlinLazy;
-
import android.animation.ArgbEvaluator;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
@@ -76,16 +73,13 @@ import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
-import com.android.app.viewcapture.ViewCapture;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.res.R;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.systemui.util.settings.SecureSettings;
-
-import kotlin.Lazy;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import javax.inject.Inject;
@@ -112,13 +106,14 @@ public class ImmersiveModeConfirmation implements CoreStartable, CommandQueue.Ca
private long mShowDelayMs = 0L;
private final IBinder mWindowToken = new Binder();
private final CommandQueue mCommandQueue;
+ private final WindowManagerProvider mWindowManagerProvider;
private ClingWindowView mClingWindow;
/** The wrapper on the last {@link WindowManager} used to add the confirmation window. */
@Nullable
- private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
+ private WindowManager mWindowManager;
/**
- * The WindowContext that is registered with {@link #mViewCaptureAwareWindowManager} with
+ * The WindowContext that is registered with {@link #mWindowManager} with
* options to specify the {@link RootDisplayArea} to attach the confirmation window.
*/
@Nullable
@@ -136,21 +131,18 @@ public class ImmersiveModeConfirmation implements CoreStartable, CommandQueue.Ca
private ContentObserver mContentObserver;
- private Lazy<ViewCapture> mLazyViewCapture;
-
@Inject
public ImmersiveModeConfirmation(Context context, CommandQueue commandQueue,
- SecureSettings secureSettings,
- dagger.Lazy<ViewCapture> daggerLazyViewCapture,
- @Background Handler backgroundHandler) {
+ SecureSettings secureSettings, @Background Handler backgroundHandler,
+ WindowManagerProvider windowManagerProvider) {
mSysUiContext = context;
final Display display = mSysUiContext.getDisplay();
mDisplayContext = display.getDisplayId() == DEFAULT_DISPLAY
? mSysUiContext : mSysUiContext.createDisplayContext(display);
mCommandQueue = commandQueue;
mSecureSettings = secureSettings;
- mLazyViewCapture = toKotlinLazy(daggerLazyViewCapture);
mBackgroundHandler = backgroundHandler;
+ mWindowManagerProvider = windowManagerProvider;
}
boolean loadSetting(int currentUserId) {
@@ -257,14 +249,14 @@ public class ImmersiveModeConfirmation implements CoreStartable, CommandQueue.Ca
private void handleHide() {
if (mClingWindow != null) {
if (DEBUG) Log.d(TAG, "Hiding immersive mode confirmation");
- if (mViewCaptureAwareWindowManager != null) {
+ if (mWindowManager != null) {
try {
- mViewCaptureAwareWindowManager.removeView(mClingWindow);
+ mWindowManager.removeView(mClingWindow);
} catch (WindowManager.InvalidDisplayException e) {
Log.w(TAG, "Fail to hide the immersive confirmation window because of "
+ e);
}
- mViewCaptureAwareWindowManager = null;
+ mWindowManager = null;
mWindowContext = null;
}
mClingWindow = null;
@@ -525,8 +517,8 @@ public class ImmersiveModeConfirmation implements CoreStartable, CommandQueue.Ca
* confirmation window.
*/
@NonNull
- private ViewCaptureAwareWindowManager createWindowManager(int rootDisplayAreaId) {
- if (mViewCaptureAwareWindowManager != null) {
+ private WindowManager createWindowManager(int rootDisplayAreaId) {
+ if (mWindowManager != null) {
throw new IllegalStateException(
"Must not create a new WindowManager while there is an existing one");
}
@@ -535,10 +527,8 @@ public class ImmersiveModeConfirmation implements CoreStartable, CommandQueue.Ca
mWindowContextRootDisplayAreaId = rootDisplayAreaId;
mWindowContext = mDisplayContext.createWindowContext(
IMMERSIVE_MODE_CONFIRMATION_WINDOW_TYPE, options);
- WindowManager wm = mWindowContext.getSystemService(WindowManager.class);
- mViewCaptureAwareWindowManager = new ViewCaptureAwareWindowManager(wm, mLazyViewCapture,
- enableViewCaptureTracing());
- return mViewCaptureAwareWindowManager;
+ mWindowManager = mWindowManagerProvider.getWindowManager(mWindowContext);
+ return mWindowManager;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
index ef0660fbcd1c..3d7d08910502 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
@@ -79,6 +79,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.phone.CentralSurfaces;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.bottomsheet.BottomSheetDialog;
@@ -148,43 +149,44 @@ public final class KeyboardShortcutListSearch {
private KeyCharacterMap mBackupKeyCharacterMap;
@VisibleForTesting
- KeyboardShortcutListSearch(Context context, WindowManager windowManager, int deviceId) {
+ KeyboardShortcutListSearch(Context context, @NonNull WindowManager windowManager,
+ int deviceId) {
this.mContext = new ContextThemeWrapper(
context, R.style.KeyboardShortcutHelper);
this.mPackageManager = AppGlobals.getPackageManager();
- if (windowManager != null) {
- this.mWindowManager = windowManager;
- } else {
- this.mWindowManager = mContext.getSystemService(WindowManager.class);
- }
+ this.mWindowManager = windowManager;
loadResources(this.mContext);
createHardcodedShortcuts(deviceId);
}
- private static KeyboardShortcutListSearch getInstance(Context context, int deviceId) {
+ private static KeyboardShortcutListSearch getInstance(Context context, int deviceId,
+ WindowManagerProvider windowManagerProvider) {
if (sInstance == null) {
- sInstance = new KeyboardShortcutListSearch(context, null, deviceId);
+ WindowManager windowManager = windowManagerProvider.getWindowManager(context);
+ sInstance = new KeyboardShortcutListSearch(context, windowManager, deviceId);
}
return sInstance;
}
- public static void show(Context context, int deviceId) {
+ public static void show(Context context, int deviceId,
+ WindowManagerProvider windowManagerProvider) {
MetricsLogger.visible(context,
MetricsProto.MetricsEvent.KEYBOARD_SHORTCUTS_HELPER);
synchronized (sLock) {
if (sInstance != null && !sInstance.mContext.equals(context)) {
dismiss();
}
- getInstance(context, deviceId).showKeyboardShortcuts(deviceId);
+ getInstance(context, deviceId, windowManagerProvider).showKeyboardShortcuts(deviceId);
}
}
- public static void toggle(Context context, int deviceId) {
+ public static void toggle(Context context, int deviceId,
+ WindowManagerProvider windowManagerProvider) {
synchronized (sLock) {
if (isShowing()) {
dismiss();
} else {
- show(context, deviceId);
+ show(context, deviceId, windowManagerProvider);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
index 2157d754ce87..bd6006c8faa6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -70,6 +70,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settingslib.Utils;
import com.android.systemui.res.R;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import java.util.ArrayList;
import java.util.Collections;
@@ -141,38 +142,38 @@ public final class KeyboardShortcuts {
this.mContext = new ContextThemeWrapper(
context, android.R.style.Theme_DeviceDefault_Settings);
this.mPackageManager = AppGlobals.getPackageManager();
- if (windowManager != null) {
- this.mWindowManager = windowManager;
- } else {
- this.mWindowManager = mContext.getSystemService(WindowManager.class);
- }
+ this.mWindowManager = windowManager;
loadResources(context);
}
- private static KeyboardShortcuts getInstance(Context context) {
+ private static KeyboardShortcuts getInstance(Context context,
+ WindowManagerProvider windowManagerProvider) {
if (sInstance == null) {
- sInstance = new KeyboardShortcuts(context, null);
+ WindowManager windowManager = windowManagerProvider.getWindowManager(context);
+ sInstance = new KeyboardShortcuts(context, windowManager);
}
return sInstance;
}
- public static void show(Context context, int deviceId) {
+ public static void show(Context context, int deviceId,
+ WindowManagerProvider windowManagerProvider) {
MetricsLogger.visible(context,
MetricsProto.MetricsEvent.KEYBOARD_SHORTCUTS_HELPER);
synchronized (sLock) {
if (sInstance != null && !sInstance.mContext.equals(context)) {
dismiss();
}
- getInstance(context).showKeyboardShortcuts(deviceId);
+ getInstance(context, windowManagerProvider).showKeyboardShortcuts(deviceId);
}
}
- public static void toggle(Context context, int deviceId) {
+ public static void toggle(Context context, int deviceId,
+ WindowManagerProvider windowManagerProvider) {
synchronized (sLock) {
if (isShowing()) {
dismiss();
} else {
- show(context, deviceId);
+ show(context, deviceId, windowManagerProvider);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java
index 815f1fcfdec6..54c84aa139cc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java
@@ -24,6 +24,7 @@ import android.content.Intent;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.shared.recents.utilities.Utilities;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import javax.inject.Inject;
@@ -31,10 +32,13 @@ import javax.inject.Inject;
public class KeyboardShortcutsReceiver extends BroadcastReceiver {
private final FeatureFlags mFeatureFlags;
+ private final WindowManagerProvider mWindowManagerProvider;
@Inject
- public KeyboardShortcutsReceiver(FeatureFlags featureFlags) {
+ public KeyboardShortcutsReceiver(FeatureFlags featureFlags,
+ WindowManagerProvider windowManagerProvider) {
mFeatureFlags = featureFlags;
+ mWindowManagerProvider = windowManagerProvider;
}
@Override
@@ -44,13 +48,14 @@ public class KeyboardShortcutsReceiver extends BroadcastReceiver {
}
if (isTabletLayoutFlagEnabled() && Utilities.isLargeScreen(context)) {
if (Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS.equals(intent.getAction())) {
- KeyboardShortcutListSearch.show(context, -1 /* deviceId unknown */);
+ KeyboardShortcutListSearch.show(context, -1 /* deviceId unknown */,
+ mWindowManagerProvider);
} else if (Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS.equals(intent.getAction())) {
KeyboardShortcutListSearch.dismiss();
}
} else {
if (Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS.equals(intent.getAction())) {
- KeyboardShortcuts.show(context, -1 /* deviceId unknown */);
+ KeyboardShortcuts.show(context, -1 /* deviceId unknown */, mWindowManagerProvider);
} else if (Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS.equals(intent.getAction())) {
KeyboardShortcuts.dismiss();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelper.kt
index 2d9ccb7b09b0..33ff6c014125 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelper.kt
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.chips.mediaprojection.ui.view
import android.app.ActivityManager
import android.content.DialogInterface
import android.content.pm.PackageManager
+import android.util.Log
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.mediaprojection.data.model.MediaProjectionState
@@ -54,10 +55,6 @@ constructor(
// dialog to animate back into the chip just for the chip to disappear in a few frames.
dialogTransitionAnimator.disableAllCurrentDialogsExitAnimations()
stopAction.invoke()
- // TODO(b/332662551): If the projection is stopped, there's a brief moment where the
- // dialog closes and the chip re-shows because the system APIs haven't come back and
- // told SysUI that the projection has officially stopped. It would be great for the chip
- // to not re-show at all.
}
}
@@ -85,8 +82,17 @@ constructor(
val appInfo = packageManager.getApplicationInfo(packageName, 0)
appInfo.loadLabel(packageManager)
} catch (e: PackageManager.NameNotFoundException) {
- // TODO(b/332662551): Log this error.
+ Log.w(
+ TAG,
+ "Failed to find application info for package: $packageName when creating " +
+ "end media projection dialog",
+ e,
+ )
null
}
}
+
+ companion object {
+ private const val TAG = "EndMediaProjectionDialogHelper"
+ }
}
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 9380dfe32bf5..0560c9e1f774 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
@@ -145,7 +145,9 @@ constructor(
/**
* Emits all notifications that are eligible to show as chips in the status bar. This is
- * different from which chips will *actually* show, see [shownNotificationChips] for that.
+ * different from which chips will *actually* show, because
+ * [com.android.systemui.statusbar.chips.notification.ui.viewmodel.NotifChipsViewModel] will
+ * hide chips that have [NotificationChipModel.isAppVisible] as true.
*/
val allNotificationChips: Flow<List<NotificationChipModel>> =
if (StatusBarNotifChips.isEnabled) {
@@ -186,15 +188,6 @@ constructor(
initialValue = emptyList(),
)
- /** Emits the notifications that should actually be *shown* as chips in the status bar. */
- val shownNotificationChips: Flow<List<NotificationChipModel>> =
- allNotificationChips.map { chipsList ->
- // If the app that posted this notification is visible, we want to hide the chip
- // because information between the status bar chip and the app itself could be
- // out-of-sync (like a timer that's slightly off)
- chipsList.filter { !it.isAppVisible }
- }
-
/*
Stable sort the promoted notifications by two criteria:
Criteria #1: Whichever app was most recently visible has higher ranking.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/model/NotificationChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/model/NotificationChipModel.kt
index 1f2079d83e6f..356731cb3777 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/model/NotificationChipModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/model/NotificationChipModel.kt
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.chips.notification.domain.model
import com.android.systemui.statusbar.StatusBarIconView
-import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels
/** Modeling all the data needed to render a status bar notification chip. */
data class NotificationChipModel(
@@ -25,7 +25,7 @@ data class NotificationChipModel(
/** The user-readable name of the app that posted this notification. */
val appName: String,
val statusBarChipIconView: StatusBarIconView?,
- val promotedContent: PromotedNotificationContentModel,
+ val promotedContent: PromotedNotificationContentModels,
/** The time when the notification first appeared as promoted. */
val creationTime: Long,
/** True if the app managing this notification is currently visible to the user. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
index b303751b4d6e..e45524b59837 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
@@ -55,12 +55,12 @@ constructor(
private val systemClock: SystemClock,
) {
/**
- * A flow modeling the notification chips that should be shown. Emits an empty list if there are
- * no notifications that should show a status bar chip.
+ * A flow modeling the current notification chips. Emits an empty list if there are no
+ * notifications that are eligible to show a status bar chip.
*/
val chips: Flow<List<OngoingActivityChipModel.Active>> =
combine(
- notifChipsInteractor.shownNotificationChips,
+ notifChipsInteractor.allNotificationChips,
headsUpNotificationInteractor.statusBarHeadsUpState,
) { notifications, headsUpState ->
notifications.map { it.toActivityChipModel(headsUpState) }
@@ -72,6 +72,8 @@ constructor(
headsUpState: TopPinnedState
): OngoingActivityChipModel.Active {
StatusBarNotifChips.unsafeAssertInNewMode()
+ // Chips are never shown when locked, so it's safe to use the version with sensitive content
+ val chipContent = promotedContent.privateVersion
val contentDescription = getContentDescription(this.appName)
val icon =
if (this.statusBarChipIconView != null) {
@@ -96,6 +98,10 @@ constructor(
notifChipsInteractor.onPromotedNotificationChipTapped(this@toActivityChipModel.key)
}
}
+ // If the app that posted this notification is visible, we want to hide the chip
+ // because information between the status bar chip and the app itself could be
+ // out-of-sync (like a timer that's slightly off)
+ val isHidden = this.isAppVisible
val onClickListenerLegacy =
View.OnClickListener {
StatusBarChipsModernization.assertInLegacyMode()
@@ -120,24 +126,23 @@ constructor(
colors = colors,
onClickListenerLegacy = onClickListenerLegacy,
clickBehavior = clickBehavior,
+ isHidden = isHidden,
)
}
- if (this.promotedContent.shortCriticalText != null) {
+ if (chipContent.shortCriticalText != null) {
return OngoingActivityChipModel.Active.Text(
key = this.key,
icon = icon,
colors = colors,
- text = this.promotedContent.shortCriticalText,
+ text = chipContent.shortCriticalText,
onClickListenerLegacy = onClickListenerLegacy,
clickBehavior = clickBehavior,
+ isHidden = isHidden,
)
}
- if (
- Flags.promoteNotificationsAutomatically() &&
- this.promotedContent.wasPromotedAutomatically
- ) {
+ if (Flags.promoteNotificationsAutomatically() && chipContent.wasPromotedAutomatically) {
// When we're promoting notifications automatically, the `when` time set on the
// notification will likely just be set to the current time, which would cause the chip
// to always show "now". We don't want early testers to get that experience since it's
@@ -148,32 +153,35 @@ constructor(
colors = colors,
onClickListenerLegacy = onClickListenerLegacy,
clickBehavior = clickBehavior,
+ isHidden = isHidden,
)
}
- if (this.promotedContent.time == null) {
+ if (chipContent.time == null) {
return OngoingActivityChipModel.Active.IconOnly(
key = this.key,
icon = icon,
colors = colors,
onClickListenerLegacy = onClickListenerLegacy,
clickBehavior = clickBehavior,
+ isHidden = isHidden,
)
}
- when (this.promotedContent.time) {
+ when (chipContent.time) {
is PromotedNotificationContentModel.When.Time -> {
return if (
- this.promotedContent.time.currentTimeMillis >=
+ chipContent.time.currentTimeMillis >=
systemClock.currentTimeMillis() + FUTURE_TIME_THRESHOLD_MILLIS
) {
OngoingActivityChipModel.Active.ShortTimeDelta(
key = this.key,
icon = icon,
colors = colors,
- time = this.promotedContent.time.currentTimeMillis,
+ time = chipContent.time.currentTimeMillis,
onClickListenerLegacy = onClickListenerLegacy,
clickBehavior = clickBehavior,
+ isHidden = isHidden,
)
} else {
// Don't show a `when` time that's close to now or in the past because it's
@@ -190,6 +198,7 @@ constructor(
colors = colors,
onClickListenerLegacy = onClickListenerLegacy,
clickBehavior = clickBehavior,
+ isHidden = isHidden,
)
}
}
@@ -198,10 +207,11 @@ constructor(
key = this.key,
icon = icon,
colors = colors,
- startTimeMs = this.promotedContent.time.elapsedRealtimeMillis,
- isEventInFuture = this.promotedContent.time.isCountDown,
+ startTimeMs = chipContent.time.elapsedRealtimeMillis,
+ isEventInFuture = chipContent.time.isCountDown,
onClickListenerLegacy = onClickListenerLegacy,
clickBehavior = clickBehavior,
+ isHidden = isHidden,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt
index b2683762cb2a..fe6065d0bb42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt
@@ -46,8 +46,9 @@ sealed interface ColorsModel {
}
/** The chip should match the system theme main color. */
- // TODO(b/347717946): The chip's color isn't getting updated when the user switches theme, it
- // only gets updated when a different configuration change happens, like a rotation.
+ // Note: When StatusBarChipsModernization is disabled, the chip's color doesn't get
+ // updated when the user switches theme. It only gets updated when a different
+ // configuration change happens, like a rotation.
data object SystemThemed : ColorsModel {
override fun background(context: Context): ColorStateList =
ColorStateList.valueOf(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
index e28f3684b0fa..ba60e2ce2aa6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
@@ -235,7 +235,6 @@ sealed class OngoingActivityChipModel {
override val isImportantForPrivacy: Boolean = false,
override val icon: ChipIcon,
override val colors: ColorsModel,
- // TODO(b/361346412): Enforce a max length requirement?
val text: String,
override val onClickListenerLegacy: View.OnClickListener? = null,
override val clickBehavior: ClickBehavior,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStore.kt
index 7fc5e8abe904..587d80fc3848 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStore.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStore.kt
@@ -18,7 +18,6 @@ package com.android.systemui.statusbar.data.repository
import android.view.Display
import android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
@@ -48,7 +47,6 @@ constructor(
private val windowControllerFactory: PrivacyDotWindowController.Factory,
private val displayWindowPropertiesRepository: DisplayWindowPropertiesRepository,
private val privacyDotViewControllerStore: PrivacyDotViewControllerStore,
- private val viewCaptureAwareWindowManagerFactory: ViewCaptureAwareWindowManager.Factory,
) :
PrivacyDotWindowControllerStore,
StatusBarPerDisplayStoreImpl<PrivacyDotWindowController>(
@@ -72,8 +70,7 @@ constructor(
return windowControllerFactory.create(
displayId = displayId,
privacyDotViewController = privacyDotViewController,
- viewCaptureAwareWindowManager =
- viewCaptureAwareWindowManagerFactory.create(displayWindowProperties.windowManager),
+ windowManager = displayWindowProperties.windowManager,
inflater = displayWindowProperties.layoutInflater,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotWindowController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotWindowController.kt
index e2bcfb752e6c..7999ca9fd3e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotWindowController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotWindowController.kt
@@ -24,10 +24,10 @@ import android.view.DisplayCutout.BOUNDS_POSITION_RIGHT
import android.view.DisplayCutout.BOUNDS_POSITION_TOP
import android.view.LayoutInflater
import android.view.View
+import android.view.WindowManager
import android.view.WindowManager.InvalidDisplayException
import android.view.WindowManager.LayoutParams.WRAP_CONTENT
import android.widget.FrameLayout
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.systemui.ScreenDecorations
import com.android.systemui.ScreenDecorationsThread
import com.android.systemui.decor.DecorProvider
@@ -54,7 +54,7 @@ class PrivacyDotWindowController
constructor(
@Assisted private val displayId: Int,
@Assisted private val privacyDotViewController: PrivacyDotViewController,
- @Assisted private val viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
+ @Assisted private val windowManager: WindowManager,
@Assisted private val inflater: LayoutInflater,
@ScreenDecorationsThread private val uiExecutor: Executor,
private val dotFactory: PrivacyDotDecorProviderFactory,
@@ -106,7 +106,7 @@ constructor(
try {
// Wrapping this in a try/catch to avoid crashes when a display is instantly removed
// after being added, and initialization hasn't finished yet.
- viewCaptureAwareWindowManager.addView(rootView, params)
+ windowManager.addView(rootView, params)
} catch (e: InvalidDisplayException) {
Log.e(
TAG,
@@ -118,7 +118,7 @@ constructor(
}
fun stop() {
- dotViews.forEach { viewCaptureAwareWindowManager.removeView(it) }
+ dotViews.forEach { windowManager.removeView(it) }
}
@AssistedFactory
@@ -126,7 +126,7 @@ constructor(
fun create(
displayId: Int,
privacyDotViewController: PrivacyDotViewController,
- viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
+ windowManager: WindowManager,
inflater: LayoutInflater,
): PrivacyDotWindowController
}
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 4558017a98c8..b5ab0920a470 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
@@ -68,6 +68,7 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.No
import com.android.systemui.statusbar.notification.headsup.PinnedStatus;
import com.android.systemui.statusbar.notification.icon.IconPack;
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel;
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
import com.android.systemui.statusbar.notification.row.NotificationGuts;
@@ -198,7 +199,7 @@ public final class NotificationEntry extends ListEntry {
// TODO(b/377565433): Move into NotificationContentModel during/after
// NotificationRowContentBinderRefactor.
- private PromotedNotificationContentModel mPromotedNotificationContentModel;
+ private PromotedNotificationContentModels mPromotedNotificationContentModels;
/**
* True if both
@@ -1106,9 +1107,9 @@ public final class NotificationEntry extends ListEntry {
* Gets the content needed to render this notification as a promoted notification on various
* surfaces (like status bar chips and AOD).
*/
- public PromotedNotificationContentModel getPromotedNotificationContentModel() {
+ public PromotedNotificationContentModels getPromotedNotificationContentModels() {
if (PromotedNotificationContentModel.featureFlagEnabled()) {
- return mPromotedNotificationContentModel;
+ return mPromotedNotificationContentModels;
} else {
Log.wtf(TAG, "getting promoted content without feature flag enabled", new Throwable());
return null;
@@ -1127,10 +1128,10 @@ public final class NotificationEntry extends ListEntry {
* Sets the content needed to render this notification as a promoted notification on various
* surfaces (like status bar chips and AOD).
*/
- public void setPromotedNotificationContentModel(
- @Nullable PromotedNotificationContentModel promotedNotificationContentModel) {
+ public void setPromotedNotificationContentModels(
+ @Nullable PromotedNotificationContentModels promotedNotificationContentModels) {
if (PromotedNotificationContentModel.featureFlagEnabled()) {
- this.mPromotedNotificationContentModel = promotedNotificationContentModel;
+ this.mPromotedNotificationContentModels = promotedNotificationContentModels;
} else {
Log.wtf(TAG, "setting promoted content without feature flag enabled", new Throwable());
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
index a0eab43f854b..26b86f9ed74d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
@@ -15,6 +15,8 @@
*/
package com.android.systemui.statusbar.notification.collection.coordinator
+import com.android.systemui.Flags.notificationSkipSilentUpdates
+
import android.app.Notification
import android.app.Notification.GROUP_ALERT_SUMMARY
import android.util.ArrayMap
@@ -465,15 +467,32 @@ constructor(
}
hunMutator.updateNotification(posted.key, pinnedStatus)
}
- } else {
+ } else { // shouldHeadsUpEver = false
if (posted.isHeadsUpEntry) {
- // We don't want this to be interrupting anymore, let's remove it
- // If the notification is pinned by the user, the only way a user can un-pin
- // it is by tapping the status bar notification chip. Since that's a clear
- // user action, we should remove the HUN immediately instead of waiting for
- // any sort of minimum timeout.
- val shouldRemoveImmediately = posted.isPinnedByUser
- hunMutator.removeNotification(posted.key, shouldRemoveImmediately)
+ if (notificationSkipSilentUpdates()) {
+ if (posted.isPinnedByUser) {
+ // We don't want this to be interrupting anymore, let's remove it
+ // If the notification is pinned by the user, the only way a user
+ // can un-pin it by tapping the status bar notification chip. Since
+ // that's a clear user action, we should remove the HUN immediately
+ // instead of waiting for any sort of minimum timeout.
+ // TODO(b/401068530) Ensure that status bar chip HUNs are not
+ // removed for silent update
+ hunMutator.removeNotification(posted.key,
+ /* releaseImmediately= */ true)
+ } else {
+ // Do NOT remove HUN for non-user update.
+ // Let the HUN show for its remaining duration.
+ }
+ } else {
+ // We don't want this to be interrupting anymore, let's remove it
+ // If the notification is pinned by the user, the only way a user can
+ // un-pin it is by tapping the status bar notification chip. Since
+ // that's a clear user action, we should remove the HUN immediately
+ // instead of waiting for any sort of minimum timeout.
+ val shouldRemoveImmediately = posted.isPinnedByUser
+ hunMutator.removeNotification(posted.key, shouldRemoveImmediately)
+ }
} else {
// Don't let the bind finish
cancelHeadsUpBind(posted.entry)
@@ -573,24 +592,34 @@ constructor(
isBinding = isBinding,
)
}
- // Handle cancelling heads up here, rather than in the OnBeforeFinalizeFilter, so
- // that
- // work can be done before the ShadeListBuilder is run. This prevents re-entrant
- // behavior between this Coordinator, HeadsUpManager, and VisualStabilityManager.
- if (posted?.shouldHeadsUpEver == false) {
- if (posted.isHeadsUpEntry) {
- // We don't want this to be interrupting anymore, let's remove it
- mHeadsUpManager.removeNotification(
- posted.key,
- /* removeImmediately= */ false,
- "onEntryUpdated",
- )
- } else if (posted.isBinding) {
+ if (notificationSkipSilentUpdates()) {
+ // TODO(b/403703828) Move canceling to OnBeforeFinalizeFilter, since we are not
+ // removing from HeadsUpManager and don't need to deal with re-entrant behavior
+ // between HeadsUpCoordinator, HeadsUpManager, and VisualStabilityManager.
+ if (posted?.shouldHeadsUpEver == false
+ && !posted.isHeadsUpEntry && posted.isBinding) {
// Don't let the bind finish
cancelHeadsUpBind(posted.entry)
}
+ } else {
+ // Handle cancelling heads up here, rather than in the OnBeforeFinalizeFilter,
+ // so that work can be done before the ShadeListBuilder is run. This prevents
+ // re-entrant behavior between this Coordinator, HeadsUpManager, and
+ // VisualStabilityManager.
+ if (posted?.shouldHeadsUpEver == false) {
+ if (posted.isHeadsUpEntry) {
+ // We don't want this to be interrupting anymore, let's remove it
+ mHeadsUpManager.removeNotification(
+ posted.key,
+ /* removeImmediately= */ false,
+ "onEntryUpdated",
+ )
+ } else if (posted.isBinding) {
+ // Don't let the bind finish
+ cancelHeadsUpBind(posted.entry)
+ }
+ }
}
-
// Update last updated time for this entry
setUpdateTime(entry, mSystemClock.currentTimeMillis())
}
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 1e5aa01714be..6042bff4bb97 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
@@ -83,7 +83,6 @@ import com.android.systemui.statusbar.notification.logging.dagger.NotificationsL
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor;
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractorImpl;
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel;
-import com.android.systemui.statusbar.notification.row.NotificationActionClickManager;
import com.android.systemui.statusbar.notification.row.NotificationEntryProcessorFactory;
import com.android.systemui.statusbar.notification.row.NotificationEntryProcessorFactoryLooperImpl;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -325,7 +324,7 @@ public interface NotificationsModule {
if (PromotedNotificationContentModel.featureFlagEnabled()) {
return implProvider.get();
} else {
- return (entry, recoveredBuilder, imageModelProvider) -> null;
+ return (entry, recoveredBuilder, redactionType, imageModelProvider) -> null;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt
index 2f9d86b45d1f..e7cc342ab65c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt
@@ -36,6 +36,7 @@ import com.android.systemui.statusbar.notification.collection.provider.SectionSt
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels
import com.android.systemui.statusbar.notification.shared.ActiveNotificationEntryModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
@@ -141,7 +142,7 @@ private class ActiveNotificationsStoreBuilder(
private fun NotificationEntry.toModel(): ActiveNotificationModel {
val promotedContent =
if (PromotedNotificationContentModel.featureFlagEnabled()) {
- promotedNotificationContentModel
+ promotedNotificationContentModels
} else {
null
}
@@ -199,7 +200,7 @@ private fun ActiveNotificationsStore.createOrReuse(
isGroupSummary: Boolean,
bucket: Int,
callType: CallType,
- promotedContent: PromotedNotificationContentModel?,
+ promotedContent: PromotedNotificationContentModels?,
): ActiveNotificationModel {
return individuals[key]?.takeIf {
it.isCurrent(
@@ -281,7 +282,7 @@ private fun ActiveNotificationModel.isCurrent(
isGroupSummary: Boolean,
bucket: Int,
callType: CallType,
- promotedContent: PromotedNotificationContentModel?,
+ promotedContent: PromotedNotificationContentModels?,
): Boolean {
return when {
key != this.key -> false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
index d09546fe80ca..3caaf542e787 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
@@ -67,6 +67,7 @@ public class FooterView extends StackScrollerDecorView {
private FooterViewButton mSettingsButton;
private FooterViewButton mHistoryButton;
private boolean mShouldBeHidden;
+ private boolean mIsBlurSupported;
// Footer label
private TextView mSeenNotifsFooterTextView;
@@ -390,15 +391,20 @@ public class FooterView extends StackScrollerDecorView {
if (!notificationFooterBackgroundTintOptimization()) {
if (notificationShadeBlur()) {
- Color backgroundColor = Color.valueOf(
- SurfaceEffectColors.surfaceEffect1(getContext()));
- scHigh = ColorUtils.setAlphaComponent(backgroundColor.toArgb(), 0xFF);
- // Apply alpha on background drawables.
- int backgroundAlpha = (int) (backgroundColor.alpha() * 0xFF);
- clearAllBg.setAlpha(backgroundAlpha);
- settingsBg.setAlpha(backgroundAlpha);
- if (historyBg != null) {
- historyBg.setAlpha(backgroundAlpha);
+ if (mIsBlurSupported) {
+ Color backgroundColor = Color.valueOf(
+ SurfaceEffectColors.surfaceEffect1(getContext()));
+ scHigh = ColorUtils.setAlphaComponent(backgroundColor.toArgb(), 0xFF);
+ // Apply alpha on background drawables.
+ int backgroundAlpha = (int) (backgroundColor.alpha() * 0xFF);
+ clearAllBg.setAlpha(backgroundAlpha);
+ settingsBg.setAlpha(backgroundAlpha);
+ if (historyBg != null) {
+ historyBg.setAlpha(backgroundAlpha);
+ }
+ } else {
+ scHigh = mContext.getColor(
+ com.android.internal.R.color.materialColorSurfaceContainer);
}
} else {
scHigh = mContext.getColor(
@@ -438,6 +444,16 @@ public class FooterView extends StackScrollerDecorView {
}
}
+ public void setIsBlurSupported(boolean isBlurSupported) {
+ if (notificationShadeBlur()) {
+ if (mIsBlurSupported == isBlurSupported) {
+ return;
+ }
+ mIsBlurSupported = isBlurSupported;
+ updateColors();
+ }
+ }
+
@Override
@NonNull
public ExpandableViewState createExpandableViewState() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
index 3383ce98549f..3213754ca9a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
@@ -20,6 +20,7 @@ import android.view.View
import androidx.lifecycle.lifecycleScope
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.internal.jank.InteractionJankMonitor
+import com.android.systemui.Flags.notificationShadeBlur
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.statusbar.notification.NotificationActivityStarter
import com.android.systemui.statusbar.notification.NotificationActivityStarter.SettingsIntent
@@ -81,6 +82,14 @@ object FooterViewBinder {
launch { bindHistoryButton(footer, viewModel, notificationActivityStarter) }
}
launch { bindMessage(footer, viewModel) }
+
+ if (notificationShadeBlur()) {
+ launch {
+ viewModel.isBlurSupported.collect { supported ->
+ footer.setIsBlurSupported(supported)
+ }
+ }
+ }
}
private suspend fun bindClearAllButton(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt
index c895c41960d4..c1fc72c618ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt
@@ -31,6 +31,7 @@ import com.android.systemui.util.kotlin.sample
import com.android.systemui.util.ui.AnimatableEvent
import com.android.systemui.util.ui.AnimatedValue
import com.android.systemui.util.ui.toAnimatedValueFlow
+import com.android.systemui.window.domain.interactor.WindowRootViewBlurInteractor
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.Flow
@@ -48,6 +49,7 @@ constructor(
notificationSettingsInteractor: NotificationSettingsInteractor,
seenNotificationsInteractor: SeenNotificationsInteractor,
shadeInteractor: ShadeInteractor,
+ windowRootViewBlurInteractor: WindowRootViewBlurInteractor,
) {
/** A message to show instead of the footer buttons. */
val message: FooterMessageViewModel =
@@ -119,6 +121,8 @@ constructor(
}
}
+ val isBlurSupported = windowRootViewBlurInteractor.isBlurCurrentlySupported
+
private val manageOrHistoryButtonText: Flow<Int> =
notificationSettingsInteractor.isNotificationHistoryEnabled.map { shouldLaunchHistory ->
if (shouldLaunchHistory) R.string.manage_notifications_history_text
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt
index 27b2788f0b08..a8a7e885d1f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt
@@ -40,6 +40,8 @@ import android.graphics.drawable.Icon
import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.shade.ShadeDisplayAware
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.RedactionType
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.promoted.AutomaticPromotionCoordinator.Companion.EXTRA_AUTOMATICALLY_EXTRACTED_SHORT_CRITICAL_TEXT
import com.android.systemui.statusbar.notification.promoted.AutomaticPromotionCoordinator.Companion.EXTRA_WAS_AUTOMATICALLY_PROMOTED
@@ -48,6 +50,7 @@ import com.android.systemui.statusbar.notification.promoted.shared.model.Promote
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.OldProgress
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.Style
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.When
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels
import com.android.systemui.statusbar.notification.row.shared.ImageModel
import com.android.systemui.statusbar.notification.row.shared.ImageModelProvider
import com.android.systemui.statusbar.notification.row.shared.ImageModelProvider.ImageSizeClass.MediumSquare
@@ -60,8 +63,9 @@ interface PromotedNotificationContentExtractor {
fun extractContent(
entry: NotificationEntry,
recoveredBuilder: Notification.Builder,
+ @RedactionType redactionType: Int,
imageModelProvider: ImageModelProvider,
- ): PromotedNotificationContentModel?
+ ): PromotedNotificationContentModels?
}
@SysUISingleton
@@ -76,8 +80,9 @@ constructor(
override fun extractContent(
entry: NotificationEntry,
recoveredBuilder: Notification.Builder,
+ @RedactionType redactionType: Int,
imageModelProvider: ImageModelProvider,
- ): PromotedNotificationContentModel? {
+ ): PromotedNotificationContentModels? {
if (!PromotedNotificationContentModel.featureFlagEnabled()) {
logger.logExtractionSkipped(entry, "feature flags disabled")
return null
@@ -95,7 +100,55 @@ constructor(
return null
}
- val contentBuilder = PromotedNotificationContentModel.Builder(entry.key)
+ val privateVersion =
+ extractPrivateContent(
+ key = entry.key,
+ notification = notification,
+ recoveredBuilder = recoveredBuilder,
+ lastAudiblyAlertedMs = entry.lastAudiblyAlertedMs,
+ imageModelProvider = imageModelProvider,
+ )
+ val publicVersion =
+ if (redactionType == REDACTION_TYPE_NONE) {
+ privateVersion
+ } else {
+ if (notification.publicVersion == null) {
+ privateVersion.toDefaultPublicVersion()
+ } else {
+ // TODO(b/400991304): implement extraction for [Notification.publicVersion]
+ privateVersion.toDefaultPublicVersion()
+ }
+ }
+ return PromotedNotificationContentModels(
+ privateVersion = privateVersion,
+ publicVersion = publicVersion,
+ )
+ .also { logger.logExtractionSucceeded(entry, it) }
+ }
+
+ private fun PromotedNotificationContentModel.toDefaultPublicVersion():
+ PromotedNotificationContentModel =
+ PromotedNotificationContentModel.Builder(key = identity.key).let {
+ it.style = if (style == Style.Ineligible) Style.Ineligible else Style.Base
+ it.smallIcon = smallIcon
+ it.iconLevel = iconLevel
+ it.appName = appName
+ it.time = time
+ it.lastAudiblyAlertedMs = lastAudiblyAlertedMs
+ it.profileBadgeResId = profileBadgeResId
+ it.colors = colors
+ it.build()
+ }
+
+ private fun extractPrivateContent(
+ key: String,
+ notification: Notification,
+ recoveredBuilder: Notification.Builder,
+ lastAudiblyAlertedMs: Long,
+ imageModelProvider: ImageModelProvider,
+ ): PromotedNotificationContentModel {
+
+ val contentBuilder = PromotedNotificationContentModel.Builder(key)
// TODO: Pitch a fit if style is unsupported or mandatory fields are missing once
// FLAG_PROMOTED_ONGOING is set reliably and we're not testing status bar chips.
@@ -108,7 +161,7 @@ constructor(
contentBuilder.subText = notification.subText()
contentBuilder.time = notification.extractWhen()
contentBuilder.shortCriticalText = notification.shortCriticalText()
- contentBuilder.lastAudiblyAlertedMs = entry.lastAudiblyAlertedMs
+ contentBuilder.lastAudiblyAlertedMs = lastAudiblyAlertedMs
contentBuilder.profileBadgeResId = null // TODO
contentBuilder.title = notification.title(recoveredBuilder.style)
contentBuilder.text = notification.text(recoveredBuilder.style)
@@ -124,7 +177,7 @@ constructor(
recoveredBuilder.extractStyleContent(notification, contentBuilder, imageModelProvider)
- return contentBuilder.build().also { logger.logExtractionSucceeded(entry, it) }
+ return contentBuilder.build()
}
private fun Notification.smallIconModel(imageModelProvider: ImageModelProvider): ImageModel? =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt
index 5f9678a06b59..6b6203d6ea4d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt
@@ -23,7 +23,7 @@ import com.android.systemui.log.core.LogLevel.ERROR
import com.android.systemui.log.core.LogLevel.INFO
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.logKey
-import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels
import javax.inject.Inject
@OptIn(ExperimentalStdlibApi::class)
@@ -56,7 +56,7 @@ constructor(@PromotedNotificationLog private val buffer: LogBuffer) {
fun logExtractionSucceeded(
entry: NotificationEntry,
- content: PromotedNotificationContentModel,
+ content: PromotedNotificationContentModels,
) {
buffer.log(
EXTRACTION_TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/AODPromotedNotificationInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/AODPromotedNotificationInteractor.kt
index ec4ee4560ea1..d9778bdde0a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/AODPromotedNotificationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/AODPromotedNotificationInteractor.kt
@@ -22,6 +22,7 @@ import com.android.systemui.statusbar.notification.promoted.shared.model.Promote
import com.android.systemui.util.kotlin.FlowDumperImpl
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
@SysUISingleton
@@ -34,6 +35,16 @@ constructor(
/** The content to show as the promoted notification on AOD */
val content: Flow<PromotedNotificationContentModel?> =
promotedNotificationsInteractor.aodPromotedNotification
+ .map {
+ // TODO(b/400991304): show the private version when unlocked
+ it?.publicVersion
+ }
+ .distinctUntilNewInstance()
val isPresent: Flow<Boolean> = content.map { it != null }.dumpWhileCollecting("isPresent")
+
+ /**
+ * Returns flow where all subsequent repetitions of the same object instance are filtered out.
+ */
+ private fun <T> Flow<T>.distinctUntilNewInstance() = distinctUntilChanged { a, b -> a === b }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractor.kt
index 96d41f1b3aaf..08e7528631c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractor.kt
@@ -25,8 +25,8 @@ import com.android.systemui.statusbar.chips.notification.domain.interactor.Statu
import com.android.systemui.statusbar.chips.screenrecord.domain.interactor.ScreenRecordChipInteractor
import com.android.systemui.statusbar.chips.screenrecord.domain.model.ScreenRecordChipModel
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
-import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.Style.Ineligible
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
import javax.inject.Inject
@@ -201,13 +201,13 @@ constructor(
* The top promoted notification represented by a chip, with the order determined by the order
* of the chips, not the notifications.
*/
- private val topPromotedChipNotification: Flow<PromotedNotificationContentModel?> =
+ private val topPromotedChipNotification: Flow<PromotedNotificationContentModels?> =
orderedChipNotifications
.map { list -> list.firstNotNullOfOrNull { it.promotedContent } }
.distinctUntilNewInstance()
/** This is the AOD promoted notification, which should avoid regular changing. */
- val aodPromotedNotification: Flow<PromotedNotificationContentModel?> =
+ val aodPromotedNotification: Flow<PromotedNotificationContentModels?> =
combine(
topPromotedChipNotification,
activeNotificationsInteractor.topLevelRepresentativeNotifications,
@@ -229,13 +229,13 @@ constructor(
.flowOn(backgroundDispatcher)
private fun List<ActiveNotificationModel>.firstAodEligibleOrNull():
- PromotedNotificationContentModel? {
+ PromotedNotificationContentModels? {
return this.firstNotNullOfOrNull { it.promotedContent?.takeIfAodEligible() }
}
- private fun PromotedNotificationContentModel.takeIfAodEligible():
- PromotedNotificationContentModel? {
- return this.takeUnless { it.style == Ineligible }
+ private fun PromotedNotificationContentModels.takeIfAodEligible():
+ PromotedNotificationContentModels? {
+ return this.takeUnless { it.privateVersion.style == Ineligible }
}
/**
@@ -251,7 +251,7 @@ constructor(
*/
private data class NotifAndPromotedContent(
val key: String,
- val promotedContent: PromotedNotificationContentModel?,
+ val promotedContent: PromotedNotificationContentModels?,
) {
/**
* Define the equals of this object to only check the reference equality of the promoted
@@ -269,7 +269,7 @@ constructor(
/** Define the hashCode to be very quick, even if it increases collisions. */
override fun hashCode(): Int {
var result = key.hashCode()
- result = 31 * result + (promotedContent?.identity?.hashCode() ?: 0)
+ result = 31 * result + (promotedContent?.key?.hashCode() ?: 0)
return result
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt
index 57b07204fc6a..ffacf62fccce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt
@@ -29,6 +29,31 @@ import com.android.systemui.statusbar.notification.row.ImageResult
import com.android.systemui.statusbar.notification.row.LazyImage
import com.android.systemui.statusbar.notification.row.shared.ImageModel
+data class PromotedNotificationContentModels(
+ /** The potentially redacted version of the content that will be exposed to the public */
+ val publicVersion: PromotedNotificationContentModel,
+ /** The unredacted version of the content that will be kept private */
+ val privateVersion: PromotedNotificationContentModel,
+) {
+ val key: String
+ get() = privateVersion.identity.key
+
+ init {
+ check(publicVersion.identity.key == privateVersion.identity.key) {
+ "public and private models must have the same key"
+ }
+ }
+
+ fun toRedactedString(): String {
+ val publicVersionString =
+ "==privateVersion".takeIf { privateVersion === publicVersion }
+ ?: publicVersion.toRedactedString()
+ return ("PromotedNotificationContentModels(" +
+ "privateVersion=${privateVersion.toRedactedString()}, " +
+ "publicVersion=$publicVersionString)")
+ }
+}
+
/**
* The content needed to render a promoted notification to surfaces besides the notification stack,
* like the skeleton view on AOD or the status bar chip.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
index ba80f016cad4..4c7c46dfae94 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
@@ -51,7 +51,7 @@ import java.util.Objects;
*/
public class HybridConversationNotificationView extends HybridNotificationView {
- private static final int MAX_SUMMARIZATION_LINES = 2;
+ private static final int MAX_SUMMARIZATION_LINES = 1;
private ImageView mConversationIconView;
private TextView mConversationSenderName;
private ViewStub mConversationFacePileStub;
@@ -295,7 +295,6 @@ public class HybridConversationNotificationView extends HybridNotificationView {
if (AsyncHybridViewInflation.isUnexpectedlyInLegacyMode()) return;
if (!TextUtils.isEmpty(summarization)) {
mConversationSenderName.setVisibility(GONE);
- titleText = null;
contentText = summarization;
mTextView.setSingleLine(false);
mTextView.setMaxLines(MAX_SUMMARIZATION_LINES);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index d97e25fdfa22..57ceafcd15c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -60,6 +60,7 @@ import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor;
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel;
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels;
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.shared.ImageModelProvider;
@@ -1003,7 +1004,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
row.mImageModelIndex = result.mRowImageInflater.getNewImageIndex();
if (PromotedNotificationContentModel.featureFlagEnabled()) {
- entry.setPromotedNotificationContentModel(result.mPromotedContent);
+ entry.setPromotedNotificationContentModels(result.mPromotedContent);
}
boolean setRepliesAndActions = true;
@@ -1387,9 +1388,9 @@ public class NotificationContentInflater implements NotificationRowContentBinder
mLogger.logAsyncTaskProgress(logKey, "extracting promoted notification content");
final ImageModelProvider imageModelProvider =
result.mRowImageInflater.useForContentModel();
- final PromotedNotificationContentModel promotedContent =
+ final PromotedNotificationContentModels promotedContent =
mPromotedNotificationContentExtractor.extractContent(mEntry,
- recoveredBuilder, imageModelProvider);
+ recoveredBuilder, mBindParams.redactionType, imageModelProvider);
mLogger.logAsyncTaskProgress(logKey, "extracted promoted notification content: "
+ promotedContent);
@@ -1503,7 +1504,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
static class InflationProgress {
RowImageInflater mRowImageInflater;
- PromotedNotificationContentModel mPromotedContent;
+ PromotedNotificationContentModels mPromotedContent;
private RemoteViews newContentView;
private RemoteViews newHeadsUpView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
index ae52db88358a..4f1b90544403 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
@@ -53,6 +53,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.logKey
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels
import com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED
import com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_EXPANDED
import com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP
@@ -595,7 +596,7 @@ constructor(
val rowImageInflater: RowImageInflater,
val remoteViews: NewRemoteViews,
val contentModel: NotificationContentModel,
- val promotedContent: PromotedNotificationContentModel?,
+ val promotedContent: PromotedNotificationContentModels?,
) {
var inflatedContentView: View? = null
@@ -700,7 +701,12 @@ constructor(
)
val imageModelProvider = rowImageInflater.useForContentModel()
promotedNotificationContentExtractor
- .extractContent(entry, builder, imageModelProvider)
+ .extractContent(
+ entry,
+ builder,
+ bindParams.redactionType,
+ imageModelProvider,
+ )
.also {
logger.logAsyncTaskProgress(
entry.logKey,
@@ -1519,7 +1525,7 @@ constructor(
entry.setContentModel(result.contentModel)
if (PromotedNotificationContentModel.featureFlagEnabled()) {
- entry.promotedNotificationContentModel = result.promotedContent
+ entry.promotedNotificationContentModels = result.promotedContent
}
result.inflatedSmartReplyState?.let { row.privateLayout.setInflatedSmartReplyState(it) }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt
index 487cbceb13a4..96527389b5fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt
@@ -20,6 +20,7 @@ import android.graphics.drawable.Icon
import android.util.Log
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels
import com.android.systemui.statusbar.notification.stack.PriorityBucket
/**
@@ -88,7 +89,7 @@ data class ActiveNotificationModel(
* The content needed to render this as a promoted notification on various surfaces, or null if
* this notification cannot be rendered as a promoted notification.
*/
- val promotedContent: PromotedNotificationContentModel?,
+ val promotedContent: PromotedNotificationContentModels?,
) : ActiveNotificationEntryModel() {
init {
if (!PromotedNotificationContentModel.featureFlagEnabled()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
index 000b3f643e9a..12b48eba7a96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
@@ -89,6 +89,17 @@ constructor(
source = shadeModeInteractor.shadeMode.map { getQuickSettingsShadeContentKey(it) },
)
+ /**
+ * Whether the current touch gesture is overscroll. If true, it means the NSSL has already
+ * consumed part of the gesture.
+ */
+ val isCurrentGestureOverscroll: Boolean by
+ hydrator.hydratedStateOf(
+ traceName = "isCurrentGestureOverscroll",
+ initialValue = false,
+ source = interactor.isCurrentGestureOverscroll
+ )
+
/** DEBUG: whether the placeholder should be made slightly visible for positional debugging. */
val isVisualDebuggingEnabled: Boolean = featureFlags.isEnabled(Flags.NSSL_DEBUG_LINES)
@@ -157,13 +168,6 @@ constructor(
val syntheticScroll: Flow<Float> =
interactor.syntheticScroll.dumpWhileCollecting("syntheticScroll")
- /**
- * Whether the current touch gesture is overscroll. If true, it means the NSSL has already
- * consumed part of the gesture.
- */
- val isCurrentGestureOverscroll: Flow<Boolean> =
- interactor.isCurrentGestureOverscroll.dumpWhileCollecting("isCurrentGestureOverScroll")
-
/** Whether remote input is currently active for any notification. */
val isRemoteInputActive = remoteInputInteractor.isRemoteInputActive
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 e617254fa288..fe367a3927ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -90,7 +90,6 @@ import androidx.annotation.NonNull;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleRegistry;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.logging.MetricsLogger;
@@ -233,6 +232,7 @@ import com.android.systemui.util.WallpaperController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.MessageRouter;
import com.android.systemui.util.kotlin.JavaAdapter;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import com.android.systemui.volume.VolumeComponent;
import com.android.systemui.wallet.controller.QuickAccessWalletController;
import com.android.wm.shell.bubbles.Bubbles;
@@ -597,7 +597,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
private final EmergencyGestureIntentFactory mEmergencyGestureIntentFactory;
- private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
private final QuickAccessWalletController mWalletController;
/**
@@ -713,8 +712,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
BrightnessMirrorShowingRepository brightnessMirrorShowingRepository,
GlanceableHubContainerController glanceableHubContainerController,
EmergencyGestureIntentFactory emergencyGestureIntentFactory,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
- QuickAccessWalletController walletController
+ QuickAccessWalletController walletController,
+ WindowManager windowManager,
+ WindowManagerProvider windowManagerProvider
) {
mContext = context;
mNotificationsController = notificationsController;
@@ -852,7 +852,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mLightRevealScrim = lightRevealScrim;
- mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager;
+ mWindowManager = windowManager;
+ mWindowManagerProvider = windowManagerProvider;
}
private void initBubbles(Bubbles bubbles) {
@@ -880,8 +881,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mColorExtractor.addOnColorsChangedListener(mOnColorsChangedListener);
- mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
-
mDisplay = mContext.getDisplay();
mDisplayId = mDisplay.getDisplayId();
updateDisplaySize();
@@ -1716,7 +1715,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mNotificationShadeWindowController.setRequestTopUi(false, TAG);
}
}, /* isDozing= */ false, RippleShape.CIRCLE,
- sUiEventLogger, mViewCaptureAwareWindowManager).show(animationDelay);
+ sUiEventLogger, mWindowManager, mWindowManagerProvider).show(animationDelay);
}
@Override
@@ -2876,6 +2875,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
protected WindowManager mWindowManager;
protected IWindowManager mWindowManagerService;
private final IDreamManager mDreamManager;
+ private final WindowManagerProvider mWindowManagerProvider;
protected Display mDisplay;
private int mDisplayId;
@@ -2912,9 +2912,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
protected void toggleKeyboardShortcuts(int deviceId) {
if (shouldUseTabletKeyboardShortcuts()) {
- KeyboardShortcutListSearch.toggle(mContext, deviceId);
+ KeyboardShortcutListSearch.toggle(mContext, deviceId, mWindowManagerProvider);
} else {
- KeyboardShortcuts.toggle(mContext, deviceId);
+ KeyboardShortcuts.toggle(mContext, deviceId, mWindowManagerProvider);
}
}
@@ -2928,7 +2928,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
private boolean shouldUseTabletKeyboardShortcuts() {
return mFeatureFlags.isEnabled(SHORTCUT_LIST_SEARCH_LAYOUT)
- && Utilities.isLargeScreen(mContext);
+ && Utilities.isLargeScreen(mWindowManager, mContext.getResources());
}
private void clearNotificationEffects() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index caf8a43b2aaf..67a7eee2977a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -104,6 +104,7 @@ private constructor(
// intercepted. See [View.OnTouchEvent]
if (event.source == InputDevice.SOURCE_MOUSE) {
if (event.action == MotionEvent.ACTION_UP) {
+ dispatchEventToShadeDisplayPolicy(event)
v.performClick()
shadeController.animateExpandShade()
}
@@ -113,6 +114,15 @@ private constructor(
}
}
+ private fun dispatchEventToShadeDisplayPolicy(event: MotionEvent) {
+ if (ShadeWindowGoesAround.isEnabled) {
+ // Notify the shade display policy that the status bar was touched. This may cause
+ // the shade to change display if the touch was in a display different than the shade
+ // one.
+ lazyStatusBarShadeDisplayPolicy.get().onStatusBarTouched(event, mView.width)
+ }
+ }
+
private val configurationListener =
object : ConfigurationController.ConfigurationListener {
override fun onDensityOrFontScaleChanged() {
@@ -232,9 +242,6 @@ private constructor(
!upOrCancel || shadeController.isExpandedVisible,
)
}
- if (ShadeWindowGoesAround.isEnabled && event.action == MotionEvent.ACTION_DOWN) {
- lazyStatusBarShadeDisplayPolicy.get().onStatusBarTouched(event, mView.width)
- }
}
private fun addDarkReceivers() {
@@ -249,6 +256,9 @@ private constructor(
inner class PhoneStatusBarViewTouchHandler : Gefingerpoken {
override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
+ if (event.action == MotionEvent.ACTION_DOWN) {
+ dispatchEventToShadeDisplayPolicy(event)
+ }
return if (Flags.statusBarSwipeOverChip()) {
shadeViewController.handleExternalInterceptTouch(event)
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index 61b7d80a8c85..b7eada1c6804 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -38,7 +38,7 @@ import com.android.systemui.statusbar.chips.ui.view.ChipChronometer
import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
import com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureHandler
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
-import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
import com.android.systemui.statusbar.notification.shared.CallType
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository
@@ -347,7 +347,7 @@ constructor(
* If the call notification also meets promoted notification criteria, this field is filled
* in with the content related to promotion. Otherwise null.
*/
- val promotedContent: PromotedNotificationContentModel?,
+ val promotedContent: PromotedNotificationContentModels?,
/** True if the call is currently ongoing (as opposed to incoming, screening, etc.). */
val isOngoing: Boolean,
/** True if the user has swiped away the status bar while in this phone call. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt
index 322dfff8f144..9546d374b595 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt
@@ -18,7 +18,7 @@ package com.android.systemui.statusbar.phone.ongoingcall.shared.model
import android.app.PendingIntent
import com.android.systemui.statusbar.StatusBarIconView
-import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels
/** Represents the state of any ongoing calls. */
sealed interface OngoingCallModel {
@@ -47,7 +47,7 @@ sealed interface OngoingCallModel {
val intent: PendingIntent?,
val notificationKey: String,
val appName: String,
- val promotedContent: PromotedNotificationContentModel?,
+ val promotedContent: PromotedNotificationContentModels?,
val isAppVisible: Boolean,
) : OngoingCallModel
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
index fa022b4768fc..0f8d534df659 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
@@ -28,7 +28,6 @@ import android.util.IndentingPrintWriter;
import androidx.annotation.NonNull;
import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager;
-import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
@@ -49,7 +48,7 @@ public final class DeviceStateRotationLockSettingController
private final RotationPolicyWrapper mRotationPolicyWrapper;
private final DeviceStateManager mDeviceStateManager;
private final Executor mMainExecutor;
- private final DeviceStateRotationLockSettingsManager mDeviceStateRotationLockSettingsManager;
+ private final DeviceStateAutoRotateSettingManager mDeviceStateAutoRotateSettingManager;
private final DeviceStateRotationLockSettingControllerLogger mLogger;
// On registration for DeviceStateCallback, we will receive a callback with the current state
@@ -65,13 +64,13 @@ public final class DeviceStateRotationLockSettingController
RotationPolicyWrapper rotationPolicyWrapper,
DeviceStateManager deviceStateManager,
@Main Executor executor,
- DeviceStateRotationLockSettingsManager deviceStateRotationLockSettingsManager,
+ DeviceStateAutoRotateSettingManager deviceStateAutoRotateSettingManager,
DeviceStateRotationLockSettingControllerLogger logger,
DumpManager dumpManager) {
mRotationPolicyWrapper = rotationPolicyWrapper;
mDeviceStateManager = deviceStateManager;
mMainExecutor = executor;
- mDeviceStateRotationLockSettingsManager = deviceStateRotationLockSettingsManager;
+ mDeviceStateAutoRotateSettingManager = deviceStateAutoRotateSettingManager;
mLogger = logger;
dumpManager.registerDumpable(this);
}
@@ -86,14 +85,14 @@ public final class DeviceStateRotationLockSettingController
mDeviceStateManager.registerCallback(mMainExecutor, mDeviceStateCallback);
mDeviceStateAutoRotateSettingListener = () ->
readPersistedSetting("deviceStateRotationLockChange", mDeviceState);
- mDeviceStateRotationLockSettingsManager.registerListener(
+ mDeviceStateAutoRotateSettingManager.registerListener(
mDeviceStateAutoRotateSettingListener);
} else {
if (mDeviceStateCallback != null) {
mDeviceStateManager.unregisterCallback(mDeviceStateCallback);
}
if (mDeviceStateAutoRotateSettingListener != null) {
- mDeviceStateRotationLockSettingsManager.unregisterListener(
+ mDeviceStateAutoRotateSettingManager.unregisterListener(
mDeviceStateAutoRotateSettingListener);
}
}
@@ -102,7 +101,7 @@ public final class DeviceStateRotationLockSettingController
@Override
public void onRotationLockStateChanged(boolean newRotationLocked, boolean affordanceVisible) {
int deviceState = mDeviceState;
- boolean currentRotationLocked = mDeviceStateRotationLockSettingsManager
+ boolean currentRotationLocked = mDeviceStateAutoRotateSettingManager
.isRotationLocked(deviceState);
mLogger.logRotationLockStateChanged(deviceState, newRotationLocked, currentRotationLocked);
if (deviceState == -1) {
@@ -117,7 +116,7 @@ public final class DeviceStateRotationLockSettingController
private void saveNewRotationLockSetting(boolean isRotationLocked) {
int deviceState = mDeviceState;
mLogger.logSaveNewRotationLockSetting(isRotationLocked, deviceState);
- mDeviceStateRotationLockSettingsManager.updateSetting(deviceState, isRotationLocked);
+ mDeviceStateAutoRotateSettingManager.updateSetting(deviceState, isRotationLocked);
}
private void updateDeviceState(@NonNull DeviceState state) {
@@ -139,7 +138,7 @@ public final class DeviceStateRotationLockSettingController
private void readPersistedSetting(String caller, int state) {
int rotationLockSetting =
- mDeviceStateRotationLockSettingsManager.getRotationLockSetting(state);
+ mDeviceStateAutoRotateSettingManager.getRotationLockSetting(state);
boolean shouldBeLocked = rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_LOCKED;
boolean isLocked = mRotationPolicyWrapper.isRotationLocked();
@@ -167,7 +166,7 @@ public final class DeviceStateRotationLockSettingController
@Override
public void dump(@NonNull PrintWriter printWriter, @NonNull String[] args) {
IndentingPrintWriter pw = new IndentingPrintWriter(printWriter);
- mDeviceStateRotationLockSettingsManager.dump(pw);
+ mDeviceStateAutoRotateSettingManager.dump(printWriter, null);
pw.println("DeviceStateRotationLockSettingController");
pw.increaseIndent();
pw.println("mDeviceState: " + mDeviceState);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
index d1e807f18196..e2a6c195af38 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
@@ -18,13 +18,20 @@ package com.android.systemui.statusbar.policy.dagger;
import android.content.Context;
import android.content.res.Resources;
+import android.hardware.devicestate.DeviceStateManager;
+import android.os.Handler;
import android.os.UserManager;
import com.android.internal.R;
-import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
+import com.android.settingslib.devicestate.AndroidSecureSettings;
+import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager;
+import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManagerProvider;
+import com.android.settingslib.devicestate.PosturesHelper;
+import com.android.settingslib.devicestate.SecureSettings;
import com.android.settingslib.notification.modes.ZenIconLoader;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Application;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.log.LogBuffer;
@@ -222,12 +229,34 @@ public interface StatusBarPolicyModule {
return controller;
}
- /** Returns a singleton instance of DeviceStateRotationLockSettingsManager */
+ /** */
+ @SysUISingleton
+ @Provides
+ static SecureSettings provideAndroidSecureSettings(Context context) {
+ return new AndroidSecureSettings(context.getContentResolver());
+ }
+
+ /** */
@SysUISingleton
@Provides
- static DeviceStateRotationLockSettingsManager provideAutoRotateSettingsManager(
- Context context) {
- return DeviceStateRotationLockSettingsManager.getInstance(context);
+ static PosturesHelper providePosturesHelper(Context context,
+ DeviceStateManager deviceStateManager) {
+ return new PosturesHelper(context, deviceStateManager);
+ }
+
+ /** Returns a singleton instance of DeviceStateAutoRotateSettingManager based on auto-rotate
+ * refactor flag. */
+ @SysUISingleton
+ @Provides
+ static DeviceStateAutoRotateSettingManager provideAutoRotateSettingsManager(
+ Context context,
+ @Background Executor bgExecutor,
+ SecureSettings secureSettings,
+ @Main Handler mainHandler,
+ PosturesHelper posturesHelper
+ ) {
+ return DeviceStateAutoRotateSettingManagerProvider.createInstance(context, bgExecutor,
+ secureSettings, mainHandler, posturesHelper);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.kt
index ecfcb29a9944..eae310ed169b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.kt
@@ -19,7 +19,7 @@ package com.android.systemui.statusbar.window
import android.content.Context
import android.view.View
import android.view.ViewGroup
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
+import android.view.WindowManager
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.fragments.FragmentHostManager
import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController
@@ -84,7 +84,7 @@ interface StatusBarWindowController {
fun interface Factory {
fun create(
context: Context,
- viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
+ windowManager: WindowManager,
statusBarConfigurationController: StatusBarConfigurationController,
contentInsetsProvider: StatusBarContentInsetsProvider,
): StatusBarWindowController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java
index 25972ac2bedf..77cd58111b94 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java
@@ -47,7 +47,6 @@ import android.view.WindowManager;
import androidx.annotation.NonNull;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.policy.SystemBarUtils;
import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.animation.DelegateTransitionAnimatorController;
@@ -78,7 +77,7 @@ public class StatusBarWindowControllerImpl implements StatusBarWindowController
private static final boolean DEBUG = false;
private final Context mContext;
- private final ViewCaptureAwareWindowManager mWindowManager;
+ private final WindowManager mWindowManager;
private final StatusBarConfigurationController mStatusBarConfigurationController;
private final IWindowManager mIWindowManager;
private final StatusBarContentInsetsProvider mContentInsetsProvider;
@@ -100,7 +99,7 @@ public class StatusBarWindowControllerImpl implements StatusBarWindowController
public StatusBarWindowControllerImpl(
@Assisted Context context,
@InternalWindowViewInflater StatusBarWindowViewInflater statusBarWindowViewInflater,
- @Assisted ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
+ @Assisted WindowManager windowManager,
@Assisted StatusBarConfigurationController statusBarConfigurationController,
IWindowManager iWindowManager,
@Assisted StatusBarContentInsetsProvider contentInsetsProvider,
@@ -108,7 +107,7 @@ public class StatusBarWindowControllerImpl implements StatusBarWindowController
Optional<UnfoldTransitionProgressProvider> unfoldTransitionProgressProvider,
@Main Executor mainExecutor) {
mContext = context;
- mWindowManager = viewCaptureAwareWindowManager;
+ mWindowManager = windowManager;
mStatusBarConfigurationController = statusBarConfigurationController;
mIWindowManager = iWindowManager;
mContentInsetsProvider = contentInsetsProvider;
@@ -406,7 +405,7 @@ public class StatusBarWindowControllerImpl implements StatusBarWindowController
@Override
StatusBarWindowControllerImpl create(
@NonNull Context context,
- @NonNull ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
+ @NonNull WindowManager windowManager,
@NonNull StatusBarConfigurationController statusBarConfigurationController,
@NonNull StatusBarContentInsetsProvider contentInsetsProvider);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt
index 39afc38dad11..74c028a8c7ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt
@@ -18,7 +18,6 @@ package com.android.systemui.statusbar.window
import android.content.Context
import android.view.WindowManager
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.display.data.repository.DisplayRepository
@@ -42,7 +41,6 @@ constructor(
@Background backgroundApplicationScope: CoroutineScope,
private val controllerFactory: StatusBarWindowController.Factory,
private val displayWindowPropertiesRepository: DisplayWindowPropertiesRepository,
- private val viewCaptureAwareWindowManagerFactory: ViewCaptureAwareWindowManager.Factory,
private val statusBarConfigurationControllerStore: StatusBarConfigurationControllerStore,
private val statusBarContentInsetsProviderStore: StatusBarContentInsetsProviderStore,
displayRepository: DisplayRepository,
@@ -67,11 +65,9 @@ constructor(
statusBarConfigurationControllerStore.forDisplay(displayId) ?: return null
val contentInsetsProvider =
statusBarContentInsetsProviderStore.forDisplay(displayId) ?: return null
- val viewCaptureAwareWindowManager =
- viewCaptureAwareWindowManagerFactory.create(statusBarDisplayContext.windowManager)
return controllerFactory.create(
statusBarDisplayContext.context,
- viewCaptureAwareWindowManager,
+ statusBarDisplayContext.windowManager,
statusBarConfigurationController,
contentInsetsProvider,
)
@@ -89,7 +85,7 @@ class SingleDisplayStatusBarWindowControllerStore
@Inject
constructor(
context: Context,
- viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
+ windowManager: WindowManager,
factory: StatusBarWindowControllerImpl.Factory,
statusBarConfigurationControllerStore: StatusBarConfigurationControllerStore,
statusBarContentInsetsProviderStore: StatusBarContentInsetsProviderStore,
@@ -98,7 +94,7 @@ constructor(
PerDisplayStore<StatusBarWindowController> by SingleDisplayStore(
factory.create(
context,
- viewCaptureAwareWindowManager,
+ windowManager,
statusBarConfigurationControllerStore.defaultDisplay,
statusBarContentInsetsProviderStore.defaultDisplay,
)
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
index 635576743462..ea5f422f977d 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
@@ -32,7 +32,6 @@ import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS
import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_TEXT
import androidx.annotation.CallSuper
import androidx.annotation.VisibleForTesting
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
@@ -70,7 +69,7 @@ import java.io.PrintWriter
abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : TemporaryViewLogger<T>>(
internal val context: Context,
internal val logger: U,
- internal val windowManager: ViewCaptureAwareWindowManager,
+ internal val windowManager: WindowManager,
@Main private val mainExecutor: DelayableExecutor,
private val accessibilityManager: AccessibilityManager,
private val configurationController: ConfigurationController,
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
index 9b9cba9186f4..b6f54331b29f 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
@@ -29,6 +29,7 @@ import android.view.View
import android.view.View.ACCESSIBILITY_LIVE_REGION_ASSERTIVE
import android.view.View.ACCESSIBILITY_LIVE_REGION_NONE
import android.view.ViewGroup
+import android.view.WindowManager
import android.view.accessibility.AccessibilityManager
import android.view.accessibility.AccessibilityNodeInfo
import android.widget.ImageView
@@ -37,7 +38,6 @@ import androidx.annotation.DimenRes
import androidx.annotation.IdRes
import androidx.annotation.VisibleForTesting
import com.android.app.animation.Interpolators
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.internal.widget.CachingIconView
import com.android.systemui.Gefingerpoken
import com.android.systemui.classifier.FalsingCollector
@@ -81,7 +81,7 @@ open class ChipbarCoordinator
constructor(
context: Context,
logger: ChipbarLogger,
- viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
+ windowManager: WindowManager,
@Main mainExecutor: DelayableExecutor,
accessibilityManager: AccessibilityManager,
configurationController: ConfigurationController,
@@ -100,7 +100,7 @@ constructor(
TemporaryViewDisplayController<ChipbarInfo, ChipbarLogger>(
context,
logger,
- viewCaptureAwareWindowManager,
+ windowManager,
mainExecutor,
accessibilityManager,
configurationController,
diff --git a/packages/SystemUI/src/com/android/systemui/topwindoweffects/TopLevelWindowEffects.kt b/packages/SystemUI/src/com/android/systemui/topwindoweffects/TopLevelWindowEffects.kt
index c11e4c507914..00c6ffd861da 100644
--- a/packages/SystemUI/src/com/android/systemui/topwindoweffects/TopLevelWindowEffects.kt
+++ b/packages/SystemUI/src/com/android/systemui/topwindoweffects/TopLevelWindowEffects.kt
@@ -21,7 +21,6 @@ import android.graphics.PixelFormat
import android.view.Gravity
import android.view.WindowInsets
import android.view.WindowManager
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -38,7 +37,7 @@ import javax.inject.Inject
class TopLevelWindowEffects @Inject constructor(
@Application private val context: Context,
@Application private val applicationScope: CoroutineScope,
- private val windowManager: ViewCaptureAwareWindowManager,
+ private val windowManager: WindowManager,
private val squeezeEffectInteractor: SqueezeEffectInteractor,
private val keyEventInteractor: KeyEventInteractor,
private val viewModelFactory: SqueezeEffectViewModel.Factory
diff --git a/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt b/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt
index 70fd5ab767d0..f1a556353273 100644
--- a/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt
@@ -20,7 +20,6 @@ import android.graphics.Rect
import android.util.IndentingPrintWriter
import android.view.View
import android.view.ViewGroup
-import dagger.Lazy
import java.io.PrintWriter
/** [Sequence] that yields all of the direct children of this [ViewGroup] */
@@ -56,11 +55,6 @@ val View.boundsOnScreen: Rect
return bounds
}
-/** Extension method to convert [dagger.Lazy] to [kotlin.Lazy] for object of any class [T]. */
-fun <T> Lazy<T>.toKotlinLazy(): kotlin.Lazy<T> {
- return lazy { this.get() }
-}
-
/**
* Returns whether this [Collection] contains exactly all [elements].
*
diff --git a/packages/SystemUI/src/com/android/systemui/util/display/DisplayHelper.java b/packages/SystemUI/src/com/android/systemui/util/display/DisplayHelper.java
index 8acd6535e751..757b2d973312 100644
--- a/packages/SystemUI/src/com/android/systemui/util/display/DisplayHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/util/display/DisplayHelper.java
@@ -21,6 +21,8 @@ import android.hardware.display.DisplayManager;
import android.view.Display;
import android.view.WindowManager;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
+
import javax.inject.Inject;
/**
@@ -29,14 +31,17 @@ import javax.inject.Inject;
public class DisplayHelper {
private final Context mContext;
private final DisplayManager mDisplayManager;
+ private final WindowManagerProvider mWindowManagerProvider;
/**
* Default constructor.
*/
@Inject
- public DisplayHelper(Context context, DisplayManager displayManager) {
+ public DisplayHelper(Context context, DisplayManager displayManager,
+ WindowManagerProvider windowManagerProvider) {
mContext = context;
mDisplayManager = displayManager;
+ mWindowManagerProvider = windowManagerProvider;
}
@@ -45,9 +50,8 @@ public class DisplayHelper {
*/
public Rect getMaxBounds(int displayId, int windowContextType) {
final Display display = mDisplayManager.getDisplay(displayId);
- WindowManager windowManager = mContext.createDisplayContext(display)
- .createWindowContext(windowContextType, null)
- .getSystemService(WindowManager.class);
+ WindowManager windowManager = mWindowManagerProvider.getWindowManager(mContext
+ .createDisplayContext(display).createWindowContext(windowContextType, null));
return windowManager.getMaximumWindowMetrics().getBounds();
}
}
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 3ac6c7bc0c6b..457b6ac42f8b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt
@@ -18,6 +18,7 @@ package com.android.systemui.volume.dialog
import android.content.Context
import android.os.Bundle
+import android.view.Gravity
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
@@ -57,8 +58,10 @@ constructor(
attributes.apply {
title = "VolumeDialog" // Not the same as Window#setTitle
}
- setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
+ setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT)
+ setGravity(Gravity.END)
}
+ setCancelable(false)
setCanceledOnTouchOutside(true)
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt
index 938e313771ad..b06a3b7784f8 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt
@@ -68,6 +68,7 @@ constructor(
viewModel.isShowingSafetyWarning
.mapLatest { isShowingSafetyWarning ->
if (isShowingSafetyWarning) {
+ viewModel.onSafetyWarningDialogShown()
showSafetyWarningVisibility { viewModel.onSafetyWarningDismissed() }
}
}
@@ -76,6 +77,7 @@ constructor(
viewModel.csdWarning
.mapLatest { csdWarning ->
if (csdWarning != null) {
+ viewModel.onCsdWarningDialogShown()
showCsdWarningDialog(csdWarning, viewModel.csdWarningConfigModel.actions) {
viewModel.onCsdWarningDismissed()
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt
index bd23e8cce82d..4ed25859fb86 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt
@@ -108,6 +108,7 @@ constructor(
/** Resets current dialog timeout. */
fun resetDismissTimeout() {
+ controller.userActivity()
mutableDismissDialogEvents.tryEmit(Unit)
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/util/RingerDrawerConstraintsUtils.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/util/RingerDrawerConstraintsUtils.kt
index fb9884cf4341..ad24f08a7214 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/util/RingerDrawerConstraintsUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/util/RingerDrawerConstraintsUtils.kt
@@ -111,7 +111,7 @@ private fun ConstraintSet.adjustOpenConstraintsForDrawer(
view.id,
ConstraintSet.START,
motionLayout.context.resources.getDimensionPixelSize(
- R.dimen.volume_dialog_ringer_drawer_margin
+ R.dimen.volume_dialog_background_margin
),
)
}
@@ -121,7 +121,7 @@ private fun ConstraintSet.adjustOpenConstraintsForDrawer(
view.id,
ConstraintSet.END,
motionLayout.context.resources.getDimensionPixelSize(
- R.dimen.volume_dialog_components_spacing
+ R.dimen.volume_dialog_ringer_drawer_buttons_spacing
),
)
} else {
@@ -140,7 +140,7 @@ private fun ConstraintSet.adjustOpenConstraintsForDrawer(
view.id,
ConstraintSet.BOTTOM,
motionLayout.context.resources.getDimensionPixelSize(
- R.dimen.volume_dialog_components_spacing
+ R.dimen.volume_dialog_ringer_drawer_buttons_spacing
),
)
} else {
@@ -158,10 +158,10 @@ private fun ConstraintSet.adjustOpenConstraintsForDrawer(
R.dimen.volume_dialog_ringer_drawer_button_size
) * (motionLayout.childCount - 1)) +
(motionLayout.context.resources.getDimensionPixelSize(
- R.dimen.volume_dialog_ringer_drawer_margin
+ R.dimen.volume_dialog_background_margin
) * 2) +
(motionLayout.context.resources.getDimensionPixelSize(
- R.dimen.volume_dialog_components_spacing
+ R.dimen.volume_dialog_ringer_drawer_buttons_spacing
) * (motionLayout.childCount - 2))
ORIENTATION_PORTRAIT ->
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
index db4b8ef5aef7..dc41e90faeaf 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
@@ -92,6 +92,8 @@ private fun VolumeDialogSlider(
SliderDefaults.colors(
activeTickColor = MaterialTheme.colorScheme.surfaceContainerHighest,
inactiveTrackColor = MaterialTheme.colorScheme.surfaceContainerHighest,
+ disabledActiveTickColor = MaterialTheme.colorScheme.surfaceContainerHighest,
+ disabledInactiveTrackColor = MaterialTheme.colorScheme.surfaceContainerHighest,
)
val collectedSliderStateModel by viewModel.state.collectAsStateWithLifecycle(null)
val sliderStateModel = collectedSliderStateModel ?: return
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt
index 6d16300bf56e..87cc1830af28 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt
@@ -70,10 +70,18 @@ constructor(
val isShowingSafetyWarning: Flow<Boolean> = dialogSafetyWarningInteractor.isShowingSafetyWarning
val csdWarning: Flow<Int?> = dialogCsdWarningInteractor.csdWarning
+ fun onSafetyWarningDialogShown() {
+ dialogVisibilityInteractor.resetDismissTimeout()
+ }
+
fun onSafetyWarningDismissed() {
dialogSafetyWarningInteractor.onSafetyWarningDismissed()
}
+ fun onCsdWarningDialogShown() {
+ dialogVisibilityInteractor.resetDismissTimeout()
+ }
+
fun onCsdWarningDismissed() {
dialogCsdWarningInteractor.onCsdWarningDismissed()
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
index b1c6455a6e57..107f670eae8d 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
@@ -42,7 +42,6 @@ import android.service.wallpaper.WallpaperService;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
-import android.view.WindowManager;
import androidx.annotation.NonNull;
@@ -50,6 +49,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dagger.qualifiers.LongRunning;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -71,6 +71,7 @@ public class ImageWallpaper extends WallpaperService {
private boolean mPagesComputed = false;
private final UserTracker mUserTracker;
+ private final WindowManagerProvider mWindowManagerProvider;
// used to handle WallpaperService messages (e.g. DO_ATTACH, MSG_UPDATE_SURFACE)
// and to receive WallpaperService callbacks (e.g. onCreateEngine, onSurfaceRedrawNeeded)
@@ -84,10 +85,12 @@ public class ImageWallpaper extends WallpaperService {
private static final int DELAY_UNLOAD_BITMAP = 2000;
@Inject
- public ImageWallpaper(@LongRunning DelayableExecutor longExecutor, UserTracker userTracker) {
+ public ImageWallpaper(@LongRunning DelayableExecutor longExecutor, UserTracker userTracker,
+ WindowManagerProvider windowManagerProvider) {
super();
mLongExecutor = longExecutor;
mUserTracker = userTracker;
+ mWindowManagerProvider = windowManagerProvider;
}
@Override
@@ -552,8 +555,7 @@ public class ImageWallpaper extends WallpaperService {
}
private void getDisplaySizeAndUpdateColorExtractor() {
- Rect window = getDisplayContext()
- .getSystemService(WindowManager.class)
+ Rect window = mWindowManagerProvider.getWindowManager(getDisplayContext())
.getCurrentWindowMetrics()
.getBounds();
mWallpaperLocalColorExtractor.setDisplayDimensions(window.width(), window.height());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 0769ada805a2..645e306eed07 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -80,8 +80,6 @@ import androidx.annotation.Nullable;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.app.viewcapture.ViewCapture;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository;
@@ -104,7 +102,6 @@ import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.commandline.CommandRegistry;
import com.android.systemui.statusbar.events.PrivacyDotViewController;
-import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.concurrency.FakeThreadFactory;
import com.android.systemui.util.kotlin.JavaAdapter;
@@ -112,8 +109,6 @@ import com.android.systemui.util.settings.FakeSettings;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.time.FakeSystemClock;
-import kotlin.Lazy;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -132,7 +127,6 @@ public class ScreenDecorationsTest extends SysuiTestCase {
private ScreenDecorations mScreenDecorations;
private WindowManager mWindowManager;
- private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
private DisplayManager mDisplayManager;
private SecureSettings mSecureSettings;
private FakeExecutor mExecutor;
@@ -179,8 +173,6 @@ public class ScreenDecorationsTest extends SysuiTestCase {
private CutoutDecorProviderFactory mCutoutFactory;
@Mock
private JavaAdapter mJavaAdapter;
- @Mock
- private Lazy<ViewCapture> mLazyViewCapture;
private FakeFacePropertyRepository mFakeFacePropertyRepository =
new FakeFacePropertyRepository();
@@ -254,14 +246,12 @@ public class ScreenDecorationsTest extends SysuiTestCase {
new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")),
mFakeFacePropertyRepository));
- mViewCaptureAwareWindowManager = new ViewCaptureAwareWindowManager(mWindowManager,
- mLazyViewCapture, false);
mScreenDecorations = spy(new ScreenDecorations(mContext, mSecureSettings,
mCommandRegistry, mUserTracker, mDisplayTracker, mDotViewController,
mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory,
new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")),
mFakeFacePropertyRepository, mJavaAdapter, mCameraProtectionLoader,
- mViewCaptureAwareWindowManager, mMainHandler, mExecutor) {
+ mWindowManager, mMainHandler, mExecutor) {
@Override
public void start() {
super.start();
@@ -1276,7 +1266,7 @@ public class ScreenDecorationsTest extends SysuiTestCase {
mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory,
new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")),
mFakeFacePropertyRepository, mJavaAdapter, mCameraProtectionLoader,
- mViewCaptureAwareWindowManager, mMainHandler, mExecutor);
+ mWindowManager, mMainHandler, mExecutor);
screenDecorations.start();
when(mContext.getDisplay()).thenReturn(mDisplay);
when(mDisplay.getDisplayInfo(any())).thenAnswer(new Answer<Boolean>() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
index 6d75c4ca3a38..d3d4e24001cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
@@ -43,13 +43,13 @@ import android.view.accessibility.IRemoteMagnificationAnimationCallback;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.model.SysUiState;
import com.android.systemui.recents.LauncherProxyService;
import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.util.settings.SecureSettings;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import org.junit.Before;
import org.junit.Test;
@@ -94,7 +94,7 @@ public class IMagnificationConnectionTest extends SysuiTestCase {
@Mock
private IWindowManager mIWindowManager;
@Mock
- private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
+ private WindowManagerProvider mWindowManagerProvider;
private IMagnificationConnection mIMagnificationConnection;
private MagnificationImpl mMagnification;
@@ -116,8 +116,7 @@ public class IMagnificationConnectionTest extends SysuiTestCase {
mTestableLooper.getLooper(), mContext.getMainExecutor(), mCommandQueue,
mModeSwitchesController, mSysUiState, mLauncherProxyService, mSecureSettings,
mDisplayTracker, getContext().getSystemService(DisplayManager.class),
- mA11yLogger, mIWindowManager, mAccessibilityManager,
- mViewCaptureAwareWindowManager);
+ mA11yLogger, mIWindowManager, mAccessibilityManager, mWindowManagerProvider);
mMagnification.mWindowMagnificationControllerSupplier =
new FakeWindowMagnificationControllerSupplier(
mContext.getSystemService(DisplayManager.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
index 8bfd2545ff2b..ae96e8fe7b8b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
@@ -50,13 +50,13 @@ import android.view.accessibility.IMagnificationConnectionCallback;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.model.SysUiState;
import com.android.systemui.recents.LauncherProxyService;
import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.util.settings.SecureSettings;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import org.junit.Before;
import org.junit.Test;
@@ -98,7 +98,7 @@ public class MagnificationTest extends SysuiTestCase {
@Mock
private IWindowManager mIWindowManager;
@Mock
- private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
+ private WindowManagerProvider mWindowManagerProvider;
@Before
public void setUp() throws Exception {
@@ -132,8 +132,7 @@ public class MagnificationTest extends SysuiTestCase {
mCommandQueue, mModeSwitchesController,
mSysUiState, mLauncherProxyService, mSecureSettings, mDisplayTracker,
getContext().getSystemService(DisplayManager.class), mA11yLogger, mIWindowManager,
- getContext().getSystemService(AccessibilityManager.class),
- mViewCaptureAwareWindowManager);
+ getContext().getSystemService(AccessibilityManager.class), mWindowManagerProvider);
mMagnification.mWindowMagnificationControllerSupplier = new FakeControllerSupplier(
mContext.getSystemService(DisplayManager.class), mWindowMagnificationController);
mMagnification.mMagnificationSettingsSupplier = new FakeSettingsSupplier(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
index a0f5b2214f80..ac0378b093c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
@@ -51,8 +51,6 @@ import android.view.animation.AccelerateInterpolator;
import android.window.InputTransferToken;
import androidx.test.filters.LargeTest;
-
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
@@ -112,8 +110,6 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
SysUiState mSysUiState;
@Mock
SecureSettings mSecureSettings;
- @Mock
- ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
private SpyWindowMagnificationController mController;
private WindowMagnificationController mSpyController;
private WindowMagnificationAnimationController mWindowMagnificationAnimationController;
@@ -167,7 +163,7 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
mSecureSettings,
scvhSupplier,
mSfVsyncFrameProvider,
- mViewCaptureAwareWindowManager);
+ mWindowManager);
mSpyController = mController.getSpyController();
}
@@ -1023,7 +1019,7 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
SecureSettings secureSettings,
Supplier<SurfaceControlViewHost> scvhSupplier,
SfVsyncFrameCallbackProvider sfVsyncFrameProvider,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
+ WindowManager windowManager) {
super(
context,
handler,
@@ -1033,7 +1029,8 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
callback,
sysUiState,
secureSettings,
- scvhSupplier);
+ scvhSupplier,
+ windowManager);
mSpyController = Mockito.mock(WindowMagnificationController.class);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index 5b32b922d377..c8e0b14152fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -238,7 +238,8 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnifierCallback,
mSysUiState,
mSecureSettings,
- scvhSupplier);
+ scvhSupplier,
+ mWindowManager);
verify(mMirrorWindowControl).setWindowDelegate(
any(MirrorWindowControl.MirrorWindowDelegate.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
index 45b9f4ad2322..dfc3ff2791b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
@@ -65,8 +65,6 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.app.viewcapture.ViewCapture;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView;
@@ -74,8 +72,6 @@ import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView.OnSeekBarW
import com.android.systemui.res.R;
import com.android.systemui.util.settings.SecureSettings;
-import kotlin.Lazy;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -104,8 +100,6 @@ public class WindowMagnificationSettingsTest extends SysuiTestCase {
private SecureSettings mSecureSettings;
@Mock
private WindowMagnificationSettingsCallback mWindowMagnificationSettingsCallback;
- @Mock
- private Lazy<ViewCapture> mLazyViewCapture;
private TestableWindowManager mWindowManager;
private WindowMagnificationSettings mWindowMagnificationSettings;
private MotionEventHelper mMotionEventHelper = new MotionEventHelper();
@@ -123,6 +117,7 @@ public class WindowMagnificationSettingsTest extends SysuiTestCase {
final WindowManager wm = mContext.getSystemService(WindowManager.class);
mWindowManager = spy(new TestableWindowManager(wm));
mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
+
mContext.addMockSystemService(Context.ACCESSIBILITY_SERVICE, mAccessibilityManager);
when(mSecureSettings.getIntForUser(anyString(), anyInt(), anyInt())).then(
@@ -130,11 +125,9 @@ public class WindowMagnificationSettingsTest extends SysuiTestCase {
when(mSecureSettings.getFloatForUser(anyString(), anyFloat(), anyInt())).then(
returnsSecondArg());
- ViewCaptureAwareWindowManager vwm = new ViewCaptureAwareWindowManager(mWindowManager,
- mLazyViewCapture, /* isViewCaptureEnabled= */ false);
mWindowMagnificationSettings = new WindowMagnificationSettings(mContext,
mWindowMagnificationSettingsCallback, mSfVsyncFrameProvider,
- mSecureSettings, vwm);
+ mSecureSettings, mWindowManager);
mSettingView = mWindowMagnificationSettings.getSettingView();
mZoomSeekbar = mSettingView.findViewById(R.id.magnifier_zoom_slider);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
index bc9d4c7fa0e6..b81cbe48b4ff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
@@ -38,8 +38,6 @@ import android.view.accessibility.AccessibilityManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.app.viewcapture.ViewCapture;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.settingslib.bluetooth.HearingAidDeviceManager;
@@ -51,8 +49,6 @@ import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.util.settings.SecureSettings;
-import kotlin.Lazy;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -78,7 +74,6 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
private Context mContextWrapper;
private WindowManager mWindowManager;
- private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
private AccessibilityManager mAccessibilityManager;
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private AccessibilityFloatingMenuController mController;
@@ -93,8 +88,6 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
@Mock
private SecureSettings mSecureSettings;
@Mock
- private Lazy<ViewCapture> mLazyViewCapture;
- @Mock
private NavigationModeController mNavigationModeController;
@Mock
private HearingAidDeviceManager mHearingAidDeviceManager;
@@ -110,8 +103,6 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
};
mWindowManager = mContext.getSystemService(WindowManager.class);
- mViewCaptureAwareWindowManager = new ViewCaptureAwareWindowManager(mWindowManager,
- mLazyViewCapture, /* isViewCaptureEnabled= */ false);
mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
mTestableLooper = TestableLooper.get(this);
@@ -172,8 +163,8 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
enableAccessibilityFloatingMenuConfig();
mController = setUpController();
mController.mFloatingMenu = new MenuViewLayerController(mContextWrapper, mWindowManager,
- mViewCaptureAwareWindowManager, mAccessibilityManager, mSecureSettings,
- mNavigationModeController, mHearingAidDeviceManager);
+ mAccessibilityManager, mSecureSettings, mNavigationModeController,
+ mHearingAidDeviceManager);
captureKeyguardUpdateMonitorCallback();
mKeyguardCallback.onUserUnlocked();
@@ -200,8 +191,8 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
enableAccessibilityFloatingMenuConfig();
mController = setUpController();
mController.mFloatingMenu = new MenuViewLayerController(mContextWrapper, mWindowManager,
- mViewCaptureAwareWindowManager, mAccessibilityManager, mSecureSettings,
- mNavigationModeController, mHearingAidDeviceManager);
+ mAccessibilityManager, mSecureSettings, mNavigationModeController,
+ mHearingAidDeviceManager);
captureKeyguardUpdateMonitorCallback();
mKeyguardCallback.onUserSwitching(fakeUserId);
@@ -215,8 +206,8 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
enableAccessibilityFloatingMenuConfig();
mController = setUpController();
mController.mFloatingMenu = new MenuViewLayerController(mContextWrapper, mWindowManager,
- mViewCaptureAwareWindowManager, mAccessibilityManager, mSecureSettings,
- mNavigationModeController, mHearingAidDeviceManager);
+ mAccessibilityManager, mSecureSettings, mNavigationModeController,
+ mHearingAidDeviceManager);
captureKeyguardUpdateMonitorCallback();
mKeyguardCallback.onUserUnlocked();
mKeyguardCallback.onKeyguardVisibilityChanged(true);
@@ -362,18 +353,15 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
private AccessibilityFloatingMenuController setUpController() {
final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
- final ViewCaptureAwareWindowManager viewCaptureAwareWindowManager =
- new ViewCaptureAwareWindowManager(windowManager, mLazyViewCapture,
- /* isViewCaptureEnabled= */ false);
final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
final FakeDisplayTracker displayTracker = new FakeDisplayTracker(mContext);
mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
final AccessibilityFloatingMenuController controller =
new AccessibilityFloatingMenuController(mContextWrapper, windowManager,
- viewCaptureAwareWindowManager, displayManager, mAccessibilityManager,
- mTargetsObserver, mModeObserver, mHearingAidDeviceManager,
- mKeyguardUpdateMonitor, mSecureSettings, displayTracker,
- mNavigationModeController, new Handler(mTestableLooper.getLooper()));
+ displayManager, mAccessibilityManager, mTargetsObserver, mModeObserver,
+ mHearingAidDeviceManager, mKeyguardUpdateMonitor, mSecureSettings,
+ displayTracker, mNavigationModeController,
+ new Handler(mTestableLooper.getLooper()));
controller.init();
return controller;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
index 6cc8238f2d09..dfb6afe8bd12 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
@@ -352,6 +352,7 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
val controller = createController()
val runner = underTest.createEphemeralRunner(controller)
runner.onAnimationCancelled()
+ waitForIdleSync()
runner.onAnimationStart(
TRANSIT_NONE,
emptyArray(),
@@ -359,12 +360,13 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
emptyArray(),
iCallback,
)
-
waitForIdleSync()
+
verify(controller).onTransitionAnimationCancelled()
verify(controller, never()).onTransitionAnimationStart(anyBoolean())
verify(listener).onTransitionAnimationCancelled()
verify(listener, never()).onTransitionAnimationStart()
+ verify(iCallback).onAnimationFinished()
assertNull(runner.delegate)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index a1d038ad8554..a9e6a3ebdb30 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -28,8 +28,6 @@ import android.view.WindowManager
import android.view.accessibility.AccessibilityManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.app.viewcapture.ViewCapture
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityTransitionAnimator
@@ -62,7 +60,6 @@ import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.testKosmos
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.google.common.truth.Truth.assertThat
-import dagger.Lazy
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -98,7 +95,6 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
@Mock private lateinit var inflater: LayoutInflater
@Mock private lateinit var windowManager: WindowManager
- @Mock private lateinit var lazyViewCapture: kotlin.Lazy<ViewCapture>
@Mock private lateinit var accessibilityManager: AccessibilityManager
@Mock private lateinit var statusBarStateController: StatusBarStateController
@Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
@@ -160,11 +156,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
UdfpsControllerOverlay(
context,
inflater,
- ViewCaptureAwareWindowManager(
- windowManager,
- lazyViewCapture,
- isViewCaptureEnabled = false,
- ),
+ windowManager,
accessibilityManager,
statusBarStateController,
statusBarKeyguardViewManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index 2c70249bcb06..bf79d11b2fb8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -1472,7 +1472,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
whenever(kosmos.udfpsUtils.onTouchOutsideOfSensorArea(any(), any(), any(), any(), any()))
.thenReturn("Direction")
- kosmos.promptViewModel.onAnnounceAccessibilityHint(
+ kosmos.promptViewModel.onUpdateAccessibilityHint(
obtainMotionEvent(MotionEvent.ACTION_HOVER_ENTER),
true,
)
@@ -1497,7 +1497,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
whenever(kosmos.udfpsUtils.onTouchOutsideOfSensorArea(any(), any(), any(), any(), any()))
.thenReturn("Direction")
- kosmos.promptViewModel.onAnnounceAccessibilityHint(
+ kosmos.promptViewModel.onUpdateAccessibilityHint(
obtainMotionEvent(MotionEvent.ACTION_HOVER_ENTER),
true,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
index 57b397cfca7e..034bab855faf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
@@ -23,13 +23,11 @@ import android.view.WindowManager
import android.view.WindowMetrics
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.app.viewcapture.ViewCapture
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.internal.logging.UiEventLogger
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.res.R
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -42,12 +40,12 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers
import org.mockito.Mock
+import org.mockito.Mockito.`when`
import org.mockito.Mockito.any
import org.mockito.Mockito.eq
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
@@ -62,7 +60,6 @@ class WiredChargingRippleControllerTest : SysuiTestCase() {
@Mock private lateinit var windowManager: WindowManager
@Mock private lateinit var uiEventLogger: UiEventLogger
@Mock private lateinit var windowMetrics: WindowMetrics
- @Mock private lateinit var lazyViewCapture: Lazy<ViewCapture>
private val systemClock = FakeSystemClock()
@Before
@@ -71,9 +68,7 @@ class WiredChargingRippleControllerTest : SysuiTestCase() {
`when`(featureFlags.isEnabled(Flags.CHARGING_RIPPLE)).thenReturn(true)
controller = WiredChargingRippleController(
commandRegistry, batteryController, configurationController,
- featureFlags, context, windowManager,
- ViewCaptureAwareWindowManager(windowManager,
- lazyViewCapture, isViewCaptureEnabled = false), systemClock, uiEventLogger)
+ featureFlags, context, windowManager, systemClock, uiEventLogger)
rippleView.setupShader()
controller.rippleView = rippleView // Replace the real ripple view with a mock instance
controller.registerCallbacks()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 9b314f25e02b..41fdaa74e57b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -73,10 +73,10 @@ import android.view.IRemoteAnimationFinishedCallback;
import android.view.RemoteAnimationTarget;
import android.view.View;
import android.view.ViewRootImpl;
+import android.view.WindowManager;
import androidx.test.filters.SmallTest;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.foldables.FoldGracePeriodProvider;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.UiEventLogger;
@@ -175,7 +175,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
private @Mock BroadcastDispatcher mBroadcastDispatcher;
private @Mock DismissCallbackRegistry mDismissCallbackRegistry;
private @Mock DumpManager mDumpManager;
- private @Mock ViewCaptureAwareWindowManager mWindowManager;
+ private @Mock WindowManager mWindowManager;
private @Mock IActivityManager mActivityManager;
private @Mock ConfigurationController mConfigurationController;
private @Mock PowerManager mPowerManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
index c7beb158c2de..add87686bc9f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
@@ -20,8 +20,8 @@ import android.content.Context
import android.os.Handler
import android.os.PowerManager
import android.view.ViewGroup
+import android.view.WindowManager
import android.view.accessibility.AccessibilityManager
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -35,7 +35,7 @@ class FakeMediaTttChipControllerReceiver(
commandQueue: CommandQueue,
context: Context,
logger: MediaTttReceiverLogger,
- viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
+ windowManager: WindowManager,
mainExecutor: DelayableExecutor,
accessibilityManager: AccessibilityManager,
configurationController: ConfigurationController,
@@ -53,7 +53,7 @@ class FakeMediaTttChipControllerReceiver(
commandQueue,
context,
logger,
- viewCaptureAwareWindowManager,
+ windowManager,
mainExecutor,
accessibilityManager,
configurationController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index 378dd452d030..1aa6ac67ec27 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -31,8 +31,6 @@ import android.view.accessibility.AccessibilityManager
import android.widget.ImageView
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.app.viewcapture.ViewCapture
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.internal.logging.InstanceId
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.SysuiTestCase
@@ -75,8 +73,6 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
@Mock private lateinit var windowManager: WindowManager
@Mock private lateinit var commandQueue: CommandQueue
@Mock private lateinit var rippleController: MediaTttReceiverRippleController
- @Mock private lateinit var lazyViewCapture: Lazy<ViewCapture>
- private lateinit var viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager
private lateinit var commandQueueCallback: CommandQueue.Callbacks
private lateinit var fakeAppIconDrawable: Drawable
private lateinit var uiEventLoggerFake: UiEventLoggerFake
@@ -114,18 +110,12 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
fakeWakeLockBuilder = WakeLockFake.Builder(context)
fakeWakeLockBuilder.setWakeLock(fakeWakeLock)
- viewCaptureAwareWindowManager =
- ViewCaptureAwareWindowManager(
- windowManager,
- lazyViewCapture,
- isViewCaptureEnabled = false,
- )
controllerReceiver =
FakeMediaTttChipControllerReceiver(
commandQueue,
context,
logger,
- viewCaptureAwareWindowManager,
+ windowManager,
fakeExecutor,
accessibilityManager,
configurationController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
index c90ac5993c31..18de32e80da6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
@@ -34,7 +34,6 @@ import android.widget.TextView
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.app.viewcapture.ViewCapture
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.statusbar.IUndoMediaTransferCallback
import com.android.systemui.SysuiTestCase
@@ -100,7 +99,6 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
@Mock private lateinit var windowManager: WindowManager
@Mock private lateinit var vibratorHelper: VibratorHelper
@Mock private lateinit var swipeHandler: SwipeChipbarAwayGestureHandler
- @Mock private lateinit var lazyViewCapture: Lazy<ViewCapture>
private lateinit var fakeWakeLockBuilder: WakeLockFake.Builder
private lateinit var fakeWakeLock: WakeLockFake
private lateinit var chipbarCoordinator: ChipbarCoordinator
@@ -145,11 +143,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
ChipbarCoordinator(
context,
chipbarLogger,
- ViewCaptureAwareWindowManager(
- windowManager,
- lazyViewCapture,
- isViewCaptureEnabled = false,
- ),
+ windowManager,
fakeExecutor,
accessibilityManager,
configurationController,
@@ -1476,7 +1470,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
private fun ViewGroup.getUndoButton(): View = this.requireViewById(R.id.end_button)
private fun ChipStateSender.getExpectedStateText(
- otherDeviceName: String = OTHER_DEVICE_NAME
+ otherDeviceName: String = OTHER_DEVICE_NAME,
): String? {
return this.getChipTextString(context, otherDeviceName).loadText(context)
}
@@ -1487,7 +1481,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED,
routeInfo,
- null,
+ null
)
}
@@ -1497,7 +1491,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
routeInfo,
- null,
+ null
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt
index b4db6da2000a..b169cc12f08a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt
@@ -27,7 +27,6 @@ import android.view.ViewConfiguration
import android.view.WindowManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.internal.jank.Cuj
import com.android.internal.util.LatencyTracker
import com.android.systemui.SysuiTestCase
@@ -64,7 +63,7 @@ class BackPanelControllerTest : SysuiTestCase() {
private var triggerThreshold: Float = 0.0f
private val touchSlop = ViewConfiguration.get(context).scaledEdgeSlop
@Mock private lateinit var vibratorHelper: VibratorHelper
- @Mock private lateinit var viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager
+ @Mock private lateinit var windowManager: WindowManager
@Mock private lateinit var configurationController: ConfigurationController
@Mock private lateinit var latencyTracker: LatencyTracker
private val interactionJankMonitor by lazy { kosmos.interactionJankMonitor }
@@ -79,7 +78,7 @@ class BackPanelControllerTest : SysuiTestCase() {
mBackPanelController =
BackPanelController(
context,
- viewCaptureAwareWindowManager,
+ windowManager,
ViewConfiguration.get(context),
Handler.createAsync(testableLooper.looper),
systemClock,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentControllerTest.java
index e4a4953063bb..e949c8a10c9c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentControllerTest.java
@@ -62,7 +62,6 @@ import android.view.WindowManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.logging.UiEventLogger;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.settingslib.wifi.WifiUtils;
@@ -162,7 +161,7 @@ public class InternetDetailsContentControllerTest extends SysuiTestCase {
@Mock
InternetDetailsContentController.InternetDialogCallback mInternetDialogCallback;
@Mock
- private ViewCaptureAwareWindowManager mWindowManager;
+ private WindowManager mWindowManager;
@Mock
private ToastFactory mToastFactory;
@Mock
@@ -234,9 +233,8 @@ public class InternetDetailsContentControllerTest extends SysuiTestCase {
mSubscriptionManager, mTelephonyManager, mWifiManager,
mConnectivityManager, mHandler, mExecutor, mBroadcastDispatcher,
mock(KeyguardUpdateMonitor.class), mGlobalSettings, mKeyguardStateController,
- mWindowManager, mToastFactory, mWorkerHandler,
- mCarrierConfigTracker, mLocationController, mDialogTransitionAnimator,
- mWifiStateWorker, mFlags);
+ mWindowManager, mToastFactory, mWorkerHandler, mCarrierConfigTracker,
+ mLocationController, mDialogTransitionAnimator, mWifiStateWorker, mFlags);
mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor,
mInternetDetailsContentController.mOnSubscriptionsChangedListener);
mInternetDetailsContentController.onStart(mInternetDialogCallback, true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java
index 8045a13ff9be..07204ee814d2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java
@@ -34,6 +34,7 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import com.google.android.material.bottomsheet.BottomSheetDialog;
@@ -61,6 +62,7 @@ public class KeyboardShortcutListSearchTest extends SysuiTestCase {
@Mock private BottomSheetDialog mBottomSheetDialog;
@Mock WindowManager mWindowManager;
@Mock Handler mHandler;
+ @Mock WindowManagerProvider mWindowManagerProvider;
@Before
public void setUp() {
@@ -77,7 +79,7 @@ public class KeyboardShortcutListSearchTest extends SysuiTestCase {
public void toggle_isShowingTrue_instanceShouldBeNull() {
when(mBottomSheetDialog.isShowing()).thenReturn(true);
- mKeyboardShortcutListSearch.toggle(mContext, DEVICE_ID);
+ mKeyboardShortcutListSearch.toggle(mContext, DEVICE_ID, mWindowManagerProvider);
assertThat(mKeyboardShortcutListSearch.sInstance).isNull();
}
@@ -86,7 +88,7 @@ public class KeyboardShortcutListSearchTest extends SysuiTestCase {
public void toggle_isShowingFalse_showKeyboardShortcuts() {
when(mBottomSheetDialog.isShowing()).thenReturn(false);
- mKeyboardShortcutListSearch.toggle(mContext, DEVICE_ID);
+ mKeyboardShortcutListSearch.toggle(mContext, DEVICE_ID, mWindowManagerProvider);
verify(mWindowManager).requestAppKeyboardShortcuts(any(), anyInt());
verify(mWindowManager).requestImeKeyboardShortcuts(any(), anyInt());
@@ -96,7 +98,7 @@ public class KeyboardShortcutListSearchTest extends SysuiTestCase {
public void requestAppKeyboardShortcuts_callback_sanitisesIcons() {
KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests();
- mKeyboardShortcutListSearch.toggle(mContext, DEVICE_ID);
+ mKeyboardShortcutListSearch.toggle(mContext, DEVICE_ID, mWindowManagerProvider);
ArgumentCaptor<WindowManager.KeyboardShortcutsReceiver> callbackCaptor =
ArgumentCaptor.forClass(WindowManager.KeyboardShortcutsReceiver.class);
@@ -114,7 +116,7 @@ public class KeyboardShortcutListSearchTest extends SysuiTestCase {
public void requestImeKeyboardShortcuts_callback_sanitisesIcons() {
KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests();
- mKeyboardShortcutListSearch.toggle(mContext, DEVICE_ID);
+ mKeyboardShortcutListSearch.toggle(mContext, DEVICE_ID, mWindowManagerProvider);
ArgumentCaptor<WindowManager.KeyboardShortcutsReceiver> callbackCaptor =
ArgumentCaptor.forClass(WindowManager.KeyboardShortcutsReceiver.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsReceiverTest.java
index 2cb9791cc159..0bd9b29a1a37 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsReceiverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsReceiverTest.java
@@ -36,6 +36,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.shared.recents.utilities.Utilities;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import org.junit.After;
import org.junit.Before;
@@ -59,6 +60,7 @@ public class KeyboardShortcutsReceiverTest extends SysuiTestCase {
@Mock private KeyboardShortcuts mKeyboardShortcuts;
@Mock private KeyboardShortcutListSearch mKeyboardShortcutListSearch;
+ @Mock private WindowManagerProvider mWindowManagerProvider;
@Before
public void setUp() {
@@ -69,7 +71,8 @@ public class KeyboardShortcutsReceiverTest extends SysuiTestCase {
KeyboardShortcuts.sInstance = mKeyboardShortcuts;
KeyboardShortcutListSearch.sInstance = mKeyboardShortcutListSearch;
- mKeyboardShortcutsReceiver = spy(new KeyboardShortcutsReceiver(mFeatureFlags));
+ mKeyboardShortcutsReceiver = spy(new KeyboardShortcutsReceiver(mFeatureFlags,
+ mWindowManagerProvider));
}
@Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java
index 20ecaf75c625..939f2b899dbe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java
@@ -39,6 +39,7 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import org.junit.Before;
import org.junit.Rule;
@@ -67,6 +68,7 @@ public class KeyboardShortcutsTest extends SysuiTestCase {
@Mock private Dialog mDialog;
@Mock WindowManager mWindowManager;
@Mock Handler mHandler;
+ @Mock WindowManagerProvider mWindowManagerProvider;
@Before
public void setUp() {
@@ -92,7 +94,7 @@ public class KeyboardShortcutsTest extends SysuiTestCase {
public void toggle_isShowingTrue_instanceShouldBeNull() {
when(mDialog.isShowing()).thenReturn(true);
- KeyboardShortcuts.toggle(mContext, DEVICE_ID);
+ KeyboardShortcuts.toggle(mContext, DEVICE_ID, mWindowManagerProvider);
assertThat(KeyboardShortcuts.sInstance).isNull();
}
@@ -101,7 +103,7 @@ public class KeyboardShortcutsTest extends SysuiTestCase {
public void toggle_isShowingFalse_showKeyboardShortcuts() {
when(mDialog.isShowing()).thenReturn(false);
- KeyboardShortcuts.toggle(mContext, DEVICE_ID);
+ KeyboardShortcuts.toggle(mContext, DEVICE_ID, mWindowManagerProvider);
verify(mWindowManager).requestAppKeyboardShortcuts(any(), anyInt());
verify(mWindowManager).requestImeKeyboardShortcuts(any(), anyInt());
@@ -131,7 +133,7 @@ public class KeyboardShortcutsTest extends SysuiTestCase {
@Test
public void requestAppKeyboardShortcuts_callback_sanitisesIcons() {
KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests();
- KeyboardShortcuts.toggle(mContext, DEVICE_ID);
+ KeyboardShortcuts.toggle(mContext, DEVICE_ID, mWindowManagerProvider);
emitAppShortcuts(singletonList(group), DEVICE_ID);
@@ -142,7 +144,7 @@ public class KeyboardShortcutsTest extends SysuiTestCase {
@Test
public void requestImeKeyboardShortcuts_callback_sanitisesIcons() {
KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests();
- KeyboardShortcuts.toggle(mContext, DEVICE_ID);
+ KeyboardShortcuts.toggle(mContext, DEVICE_ID, mWindowManagerProvider);
emitImeShortcuts(singletonList(group), DEVICE_ID);
@@ -153,7 +155,7 @@ public class KeyboardShortcutsTest extends SysuiTestCase {
@Test
public void onImeAndAppShortcutsReceived_appShortcutsNull_doesNotCrash() {
KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests();
- KeyboardShortcuts.toggle(mContext, DEVICE_ID);
+ KeyboardShortcuts.toggle(mContext, DEVICE_ID, mWindowManagerProvider);
emitImeShortcuts(singletonList(group), DEVICE_ID);
emitAppShortcuts(/* groups= */ null, DEVICE_ID);
@@ -162,7 +164,7 @@ public class KeyboardShortcutsTest extends SysuiTestCase {
@Test
public void onImeAndAppShortcutsReceived_imeShortcutsNull_doesNotCrash() {
KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests();
- KeyboardShortcuts.toggle(mContext, DEVICE_ID);
+ KeyboardShortcuts.toggle(mContext, DEVICE_ID, mWindowManagerProvider);
emitAppShortcuts(singletonList(group), DEVICE_ID);
emitImeShortcuts(/* groups= */ null, DEVICE_ID);
@@ -170,7 +172,7 @@ public class KeyboardShortcutsTest extends SysuiTestCase {
@Test
public void onImeAndAppShortcutsReceived_bothNull_doesNotCrash() {
- KeyboardShortcuts.toggle(mContext, DEVICE_ID);
+ KeyboardShortcuts.toggle(mContext, DEVICE_ID, mWindowManagerProvider);
emitImeShortcuts(/* groups= */ null, DEVICE_ID);
emitAppShortcuts(/* groups= */ null, DEVICE_ID);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index a7f3fdcb517e..0c5cbc299aee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -93,7 +93,6 @@ import android.view.WindowMetrics;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.compose.animation.scene.ObservableTransitionState;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.logging.UiEventLogger;
@@ -214,6 +213,7 @@ import com.android.systemui.util.settings.FakeGlobalSettings;
import com.android.systemui.util.settings.FakeSettings;
import com.android.systemui.util.settings.SystemSettings;
import com.android.systemui.util.time.FakeSystemClock;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import com.android.systemui.volume.VolumeComponent;
import com.android.systemui.wallet.controller.QuickAccessWalletController;
import com.android.wm.shell.bubbles.Bubbles;
@@ -372,9 +372,10 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Mock private GlanceableHubContainerController mGlanceableHubContainerController;
@Mock private EmergencyGestureIntentFactory mEmergencyGestureIntentFactory;
@Mock private NotificationSettingsInteractor mNotificationSettingsInteractor;
- @Mock private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
@Mock private StatusBarLongPressGestureDetector mStatusBarLongPressGestureDetector;
@Mock private QuickAccessWalletController mQuickAccessWalletController;
+ @Mock private WindowManager mWindowManager;
+ @Mock private WindowManagerProvider mWindowManagerProvider;
private ShadeController mShadeController;
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
private final FakeGlobalSettings mFakeGlobalSettings = new FakeGlobalSettings();
@@ -642,8 +643,9 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mBrightnessMirrorShowingRepository,
mGlanceableHubContainerController,
mEmergencyGestureIntentFactory,
- mViewCaptureAwareWindowManager,
- mQuickAccessWalletController
+ mQuickAccessWalletController,
+ mWindowManager,
+ mWindowManagerProvider
);
mScreenLifecycle.addObserver(mCentralSurfaces.mScreenObserver);
mCentralSurfaces.initShadeVisibilityListener();
@@ -1363,15 +1365,13 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
private void switchToScreenSize(int widthDp, int heightDp) {
WindowMetrics windowMetrics = Mockito.mock(WindowMetrics.class);
- WindowManager windowManager = Mockito.mock(WindowManager.class);
Configuration configuration = new Configuration();
configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT;
mContext.getOrCreateTestableResources().overrideConfiguration(configuration);
when(windowMetrics.getBounds()).thenReturn(new Rect(0, 0, widthDp, heightDp));
- when(windowManager.getCurrentWindowMetrics()).thenReturn(windowMetrics);
- mContext.addMockSystemService(WindowManager.class, windowManager);
+ when(mWindowManager.getCurrentWindowMetrics()).thenReturn(windowMetrics);
}
/**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index 68f66611c981..574b2c010a37 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -471,6 +471,51 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(ShadeWindowGoesAround.FLAG_NAME)
+ fun onTouch_withMouseOnEndSideIcons_flagOn_propagatedToShadeDisplayPolicy() {
+ val view = createViewMock()
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ controller = createAndInitController(view)
+ }
+ val event = getActionUpEventFromSource(InputDevice.SOURCE_MOUSE)
+
+ val statusContainer = view.requireViewById<View>(R.id.system_icons)
+ statusContainer.dispatchTouchEvent(event)
+
+ verify(statusBarTouchShadeDisplayPolicy).onStatusBarTouched(eq(event), any())
+ }
+
+ @Test
+ @EnableFlags(ShadeWindowGoesAround.FLAG_NAME)
+ fun onTouch_withMouseOnStartSideIcons_flagOn_propagatedToShadeDisplayPolicy() {
+ val view = createViewMock()
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ controller = createAndInitController(view)
+ }
+ val event = getActionUpEventFromSource(InputDevice.SOURCE_MOUSE)
+
+ val statusContainer = view.requireViewById<View>(R.id.status_bar_start_side_content)
+ statusContainer.dispatchTouchEvent(event)
+
+ verify(statusBarTouchShadeDisplayPolicy).onStatusBarTouched(eq(event), any())
+ }
+
+ @Test
+ @DisableFlags(ShadeWindowGoesAround.FLAG_NAME)
+ fun onTouch_withMouseOnSystemIcons_flagOff_notPropagatedToShadeDisplayPolicy() {
+ val view = createViewMock()
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ controller = createAndInitController(view)
+ }
+ val event = getActionUpEventFromSource(InputDevice.SOURCE_MOUSE)
+
+ val statusContainer = view.requireViewById<View>(R.id.system_icons)
+ statusContainer.dispatchTouchEvent(event)
+
+ verify(statusBarTouchShadeDisplayPolicy, never()).onStatusBarTouched(eq(event), any())
+ }
+
+ @Test
fun shadeIsExpandedOnStatusIconMouseClick() {
val view = createViewMock()
InstrumentationRegistry.getInstrumentation().runOnMainSync {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionKairosAdapterTelephonySmokeTests.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionKairosAdapterTelephonySmokeTests.kt
new file mode 100644
index 000000000000..5695df5c307d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionKairosAdapterTelephonySmokeTests.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.pipeline.mobile.data.repository.prod
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.activated
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.launchKairosNetwork
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.runCurrent
+import org.mockito.Mockito
+
+@OptIn(ExperimentalKairosApi::class, ExperimentalCoroutinesApi::class)
+@SmallTest
+class MobileConnectionKairosAdapterTelephonySmokeTests : MobileConnectionTelephonySmokeTestsBase() {
+
+ var job: Job? = null
+ val kairosNetwork = testScope.backgroundScope.launchKairosNetwork()
+
+ override fun recreateRepo(): MobileConnectionRepository {
+ lateinit var adapter: MobileConnectionRepositoryKairosAdapter
+ job?.cancel()
+ Mockito.clearInvocations(telephonyManager)
+ job =
+ testScope.backgroundScope.launch {
+ kairosNetwork.activateSpec {
+ val repo = activated {
+ MobileConnectionRepositoryKairosImpl(
+ MobileConnectionRepositoryTest.SUB_1_ID,
+ context,
+ subscriptionModel.toState(),
+ MobileConnectionRepositoryTest.DEFAULT_NAME_MODEL,
+ MobileConnectionRepositoryTest.SEP,
+ connectivityManager,
+ telephonyManager,
+ systemUiCarrierConfig,
+ fakeBroadcastDispatcher,
+ mobileMappings,
+ testDispatcher,
+ logger,
+ tableLogger,
+ flags,
+ )
+ }
+ adapter = MobileConnectionRepositoryKairosAdapter(repo, systemUiCarrierConfig)
+ Unit
+ }
+ }
+ testScope.runCurrent()
+ return adapter
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryKairosAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryKairosAdapterTest.kt
new file mode 100644
index 000000000000..0cb7c1eea268
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryKairosAdapterTest.kt
@@ -0,0 +1,72 @@
+/*
+ * 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.pipeline.mobile.data.repository.prod
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.activated
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.launchKairosNetwork
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.runCurrent
+import org.junit.runner.RunWith
+import org.mockito.Mockito
+
+@OptIn(ExperimentalKairosApi::class, ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class MobileConnectionRepositoryKairosAdapterTest : MobileConnectionRepositoryTest() {
+
+ var job: Job? = null
+ val kairosNetwork = testScope.backgroundScope.launchKairosNetwork()
+
+ override fun recreateRepo(): MobileConnectionRepository {
+ lateinit var adapter: MobileConnectionRepositoryKairosAdapter
+ job?.cancel()
+ Mockito.clearInvocations(telephonyManager)
+ job =
+ testScope.backgroundScope.launch {
+ kairosNetwork.activateSpec {
+ val repo = activated {
+ MobileConnectionRepositoryKairosImpl(
+ SUB_1_ID,
+ context,
+ subscriptionModel.toState(),
+ DEFAULT_NAME_MODEL,
+ SEP,
+ connectivityManager,
+ telephonyManager,
+ systemUiCarrierConfig,
+ fakeBroadcastDispatcher,
+ mobileMappings,
+ testDispatcher,
+ logger,
+ tableLogger,
+ flags,
+ )
+ }
+ adapter = MobileConnectionRepositoryKairosAdapter(repo, systemUiCarrierConfig)
+ Unit
+ }
+ }
+ testScope.runCurrent() // ensure the lateinit is set
+ return adapter
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
index ed8be9b253ab..2636195c0021 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
@@ -88,6 +88,7 @@ import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionMod
import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig
import com.android.systemui.statusbar.pipeline.mobile.data.model.testCarrierConfig
import com.android.systemui.statusbar.pipeline.mobile.data.model.testCarrierConfigWithOverride
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.signalStrength
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.telephonyDisplayInfo
@@ -116,25 +117,49 @@ import org.mockito.kotlin.argumentCaptor
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
@SmallTest
@RunWith(AndroidJUnit4::class)
-class MobileConnectionRepositoryTest : SysuiTestCase() {
- private lateinit var underTest: MobileConnectionRepositoryImpl
+class MobileConnectionRepositoryImplTest : MobileConnectionRepositoryTest() {
+ override fun recreateRepo(): MobileConnectionRepository =
+ MobileConnectionRepositoryImpl(
+ SUB_1_ID,
+ context,
+ subscriptionModel,
+ DEFAULT_NAME_MODEL,
+ SEP,
+ connectivityManager,
+ telephonyManager,
+ systemUiCarrierConfig,
+ fakeBroadcastDispatcher,
+ mobileMappings,
+ testDispatcher,
+ logger,
+ tableLogger,
+ flags,
+ testScope.backgroundScope,
+ )
+}
+
+abstract class MobileConnectionRepositoryTest : SysuiTestCase() {
+
+ abstract fun recreateRepo(): MobileConnectionRepository
+
+ lateinit var underTest: MobileConnectionRepository
- private val flags =
+ protected val flags =
FakeFeatureFlagsClassic().also { it.set(ROAMING_INDICATOR_VIA_DISPLAY_INFO, true) }
- @Mock private lateinit var connectivityManager: ConnectivityManager
- @Mock private lateinit var telephonyManager: TelephonyManager
- @Mock private lateinit var logger: MobileInputLogger
- @Mock private lateinit var tableLogger: TableLogBuffer
- @Mock private lateinit var context: Context
+ @Mock protected lateinit var connectivityManager: ConnectivityManager
+ @Mock protected lateinit var telephonyManager: TelephonyManager
+ @Mock protected lateinit var logger: MobileInputLogger
+ @Mock protected lateinit var tableLogger: TableLogBuffer
+ @Mock protected lateinit var context: Context
- private val mobileMappings = FakeMobileMappingsProxy()
- private val systemUiCarrierConfig = SystemUiCarrierConfig(SUB_1_ID, testCarrierConfig())
+ protected val mobileMappings = FakeMobileMappingsProxy()
+ protected val systemUiCarrierConfig = SystemUiCarrierConfig(SUB_1_ID, testCarrierConfig())
- private val testDispatcher = UnconfinedTestDispatcher()
- private val testScope = TestScope(testDispatcher)
+ protected val testDispatcher = UnconfinedTestDispatcher()
+ protected val testScope = TestScope(testDispatcher)
- private val subscriptionModel: MutableStateFlow<SubscriptionModel?> =
+ protected val subscriptionModel: MutableStateFlow<SubscriptionModel?> =
MutableStateFlow(
SubscriptionModel(
subscriptionId = SUB_1_ID,
@@ -144,28 +169,11 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
)
@Before
- fun setUp() {
+ fun setUpBase() {
MockitoAnnotations.initMocks(this)
whenever(telephonyManager.subscriptionId).thenReturn(SUB_1_ID)
- underTest =
- MobileConnectionRepositoryImpl(
- SUB_1_ID,
- context,
- subscriptionModel,
- DEFAULT_NAME_MODEL,
- SEP,
- connectivityManager,
- telephonyManager,
- systemUiCarrierConfig,
- fakeBroadcastDispatcher,
- mobileMappings,
- testDispatcher,
- logger,
- tableLogger,
- flags,
- testScope.backgroundScope,
- )
+ underTest = recreateRepo()
}
@Test
@@ -400,6 +408,7 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
fun carrierId_initialValueCaptured() =
testScope.runTest {
whenever(telephonyManager.simCarrierId).thenReturn(1234)
+ underTest = recreateRepo()
var latest: Int? = null
val job = underTest.carrierId.onEach { latest = it }.launchIn(this)
@@ -430,6 +439,8 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
@Test
fun carrierNetworkChange() =
testScope.runTest {
+ underTest = recreateRepo()
+
var latest: Boolean? = null
val job = underTest.carrierNetworkChangeActive.onEach { latest = it }.launchIn(this)
@@ -622,24 +633,7 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
flags.set(ROAMING_INDICATOR_VIA_DISPLAY_INFO, true)
// Re-create the repository, because the flag is read at init
- underTest =
- MobileConnectionRepositoryImpl(
- SUB_1_ID,
- context,
- subscriptionModel,
- DEFAULT_NAME_MODEL,
- SEP,
- connectivityManager,
- telephonyManager,
- systemUiCarrierConfig,
- fakeBroadcastDispatcher,
- mobileMappings,
- testDispatcher,
- logger,
- tableLogger,
- flags,
- testScope.backgroundScope,
- )
+ underTest = recreateRepo()
var latest: Boolean? = null
val job = underTest.isRoaming.onEach { latest = it }.launchIn(this)
@@ -671,24 +665,7 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
flags.set(ROAMING_INDICATOR_VIA_DISPLAY_INFO, false)
// Re-create the repository, because the flag is read at init
- underTest =
- MobileConnectionRepositoryImpl(
- SUB_1_ID,
- context,
- subscriptionModel,
- DEFAULT_NAME_MODEL,
- SEP,
- connectivityManager,
- telephonyManager,
- systemUiCarrierConfig,
- fakeBroadcastDispatcher,
- mobileMappings,
- testDispatcher,
- logger,
- tableLogger,
- flags,
- testScope.backgroundScope,
- )
+ underTest = recreateRepo()
var latest: Boolean? = null
val job = underTest.isRoaming.onEach { latest = it }.launchIn(this)
@@ -1441,14 +1418,14 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
}
companion object {
- private const val SUB_1_ID = 1
+ const val SUB_1_ID = 1
- private const val DEFAULT_NAME = "Fake Mobile Network"
- private val DEFAULT_NAME_MODEL = NetworkNameModel.Default(DEFAULT_NAME)
- private const val SEP = "-"
+ const val DEFAULT_NAME = "Fake Mobile Network"
+ val DEFAULT_NAME_MODEL = NetworkNameModel.Default(DEFAULT_NAME)
+ const val SEP = "-"
- private const val SPN = "testSpn"
- private const val DATA_SPN = "testDataSpn"
- private const val PLMN = "testPlmn"
+ const val SPN = "testSpn"
+ const val DATA_SPN = "testDataSpn"
+ const val PLMN = "testPlmn"
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
index 6f21e795532b..caa6e21fd883 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
@@ -42,6 +42,7 @@ import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetwork
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig
import com.android.systemui.statusbar.pipeline.mobile.data.model.testCarrierConfig
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.getTelephonyCallbackForType
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.signalStrength
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
@@ -92,47 +93,53 @@ import org.mockito.MockitoAnnotations
*/
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
@SmallTest
-class MobileConnectionTelephonySmokeTests : SysuiTestCase() {
- private lateinit var underTest: MobileConnectionRepositoryImpl
+class MobileConnectionTelephonySmokeTests : MobileConnectionTelephonySmokeTestsBase() {
+ override fun recreateRepo(): MobileConnectionRepository =
+ MobileConnectionRepositoryImpl(
+ SUB_1_ID,
+ context,
+ subscriptionModel,
+ DEFAULT_NAME,
+ SEP,
+ connectivityManager,
+ telephonyManager,
+ systemUiCarrierConfig,
+ fakeBroadcastDispatcher,
+ mobileMappings,
+ testDispatcher,
+ logger,
+ tableLogger,
+ flags,
+ testScope.backgroundScope,
+ )
+}
+
+abstract class MobileConnectionTelephonySmokeTestsBase : SysuiTestCase() {
+ protected lateinit var underTest: MobileConnectionRepository
- private val flags =
+ protected val flags =
FakeFeatureFlagsClassic().also { it.set(Flags.ROAMING_INDICATOR_VIA_DISPLAY_INFO, true) }
- @Mock private lateinit var connectivityManager: ConnectivityManager
- @Mock private lateinit var telephonyManager: TelephonyManager
- @Mock private lateinit var logger: MobileInputLogger
- @Mock private lateinit var tableLogger: TableLogBuffer
- @Mock private lateinit var subscriptionModel: StateFlow<SubscriptionModel?>
+ @Mock protected lateinit var connectivityManager: ConnectivityManager
+ @Mock protected lateinit var telephonyManager: TelephonyManager
+ @Mock protected lateinit var logger: MobileInputLogger
+ @Mock protected lateinit var tableLogger: TableLogBuffer
+ @Mock protected lateinit var subscriptionModel: StateFlow<SubscriptionModel?>
- private val mobileMappings = FakeMobileMappingsProxy()
- private val systemUiCarrierConfig = SystemUiCarrierConfig(SUB_1_ID, testCarrierConfig())
+ protected val mobileMappings = FakeMobileMappingsProxy()
+ protected val systemUiCarrierConfig = SystemUiCarrierConfig(SUB_1_ID, testCarrierConfig())
- private val testDispatcher = UnconfinedTestDispatcher()
- private val testScope = TestScope(testDispatcher)
+ protected val testDispatcher = UnconfinedTestDispatcher()
+ protected val testScope = TestScope(testDispatcher)
+
+ abstract fun recreateRepo(): MobileConnectionRepository
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
whenever(telephonyManager.subscriptionId).thenReturn(SUB_1_ID)
- underTest =
- MobileConnectionRepositoryImpl(
- SUB_1_ID,
- context,
- subscriptionModel,
- DEFAULT_NAME,
- SEP,
- connectivityManager,
- telephonyManager,
- systemUiCarrierConfig,
- fakeBroadcastDispatcher,
- mobileMappings,
- testDispatcher,
- logger,
- tableLogger,
- flags,
- testScope.backgroundScope,
- )
+ underTest = recreateRepo()
}
@Test
@@ -329,9 +336,9 @@ class MobileConnectionTelephonySmokeTests : SysuiTestCase() {
}
companion object {
- private const val SUB_1_ID = 1
+ const val SUB_1_ID = 1
- private val DEFAULT_NAME = NetworkNameModel.Default("default name")
- private const val SEP = "-"
+ val DEFAULT_NAME = NetworkNameModel.Default("default name")
+ const val SEP = "-"
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryKairosAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryKairosAdapterTest.kt
new file mode 100644
index 000000000000..65849ae103c4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryKairosAdapterTest.kt
@@ -0,0 +1,126 @@
+/*
+ * 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.pipeline.mobile.data.repository.prod
+
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.launchKairosNetwork
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeCarrierConfigRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepositoryKairosAdapter
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.runCurrent
+import org.mockito.Mockito
+import org.mockito.kotlin.mock
+
+@OptIn(ExperimentalKairosApi::class, ExperimentalCoroutinesApi::class)
+@SmallTest
+// This is required because our [SubscriptionManager.OnSubscriptionsChangedListener] uses a looper
+// to run the callback and this makes the looper place nicely with TestScope etc.
+@TestableLooper.RunWithLooper
+class MobileConnectionsRepositoryKairosAdapterTest :
+ MobileConnectionsRepositoryTest<MobileConnectionsRepositoryKairosAdapter>() {
+
+ var job: Job? = null
+ val kairosNetwork = testScope.backgroundScope.launchKairosNetwork()
+
+ override fun recreateRepo(): MobileConnectionsRepositoryKairosAdapter {
+ val carrierConfigRepo = FakeCarrierConfigRepository()
+ lateinit var connectionsRepo: MobileConnectionsRepositoryKairosImpl
+ connectionsRepo =
+ MobileConnectionsRepositoryKairosImpl(
+ connectivityRepository = connectivityRepository,
+ subscriptionManager = subscriptionManager,
+ subscriptionManagerProxy = subscriptionManagerProxy,
+ telephonyManager = telephonyManager,
+ logger = logger,
+ tableLogger = summaryLogger,
+ mobileMappingsProxy = mobileMappings,
+ broadcastDispatcher = fakeBroadcastDispatcher,
+ context = context,
+ bgDispatcher = testDispatcher,
+ mainDispatcher = testDispatcher,
+ airplaneModeRepository = airplaneModeRepository,
+ wifiRepository = wifiRepository,
+ keyguardUpdateMonitor = updateMonitor,
+ dumpManager = mock(),
+ mobileRepoFactory = {
+ MobileConnectionRepositoryKairosFactoryImpl(
+ context = context,
+ connectionsRepo = connectionsRepo,
+ logFactory = logBufferFactory,
+ carrierConfigRepo = carrierConfigRepo,
+ telephonyManager = telephonyManager,
+ mobileRepoFactory = {
+ subId,
+ mobileLogger,
+ subscriptionModel,
+ defaultNetworkName,
+ networkNameSeparator,
+ systemUiCarrierConfig,
+ telephonyManager ->
+ MobileConnectionRepositoryKairosImpl(
+ subId = subId,
+ context = context,
+ subscriptionModel = subscriptionModel,
+ defaultNetworkName = defaultNetworkName,
+ networkNameSeparator = networkNameSeparator,
+ connectivityManager = connectivityManager,
+ telephonyManager = telephonyManager,
+ systemUiCarrierConfig = systemUiCarrierConfig,
+ broadcastDispatcher = fakeBroadcastDispatcher,
+ mobileMappingsProxy = mobileMappings,
+ bgDispatcher = testDispatcher,
+ logger = logger,
+ tableLogBuffer = mobileLogger,
+ flags = flags,
+ )
+ },
+ mergedRepoFactory =
+ CarrierMergedConnectionRepositoryKairos.Factory(
+ telephonyManager,
+ wifiRepository,
+ ),
+ )
+ },
+ )
+
+ val adapter =
+ MobileConnectionsRepositoryKairosAdapter(
+ kairosRepo = connectionsRepo,
+ kairosNetwork = kairosNetwork,
+ scope = testScope.backgroundScope,
+ connectivityRepository = connectivityRepository,
+ context = context,
+ carrierConfigRepo = carrierConfigRepo,
+ )
+
+ job?.cancel()
+ Mockito.clearInvocations(telephonyManager)
+ job =
+ testScope.backgroundScope.launch {
+ kairosNetwork.activateSpec {
+ connectionsRepo.run { activate() }
+ adapter.run { activate() }
+ }
+ }
+ testScope.runCurrent() // ensure everything is activated
+ return adapter
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index d1d6e27332b0..c3662880c5cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -37,6 +37,7 @@ import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET
import android.telephony.TelephonyCallback
import android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener
+import android.telephony.TelephonyCallback.EmergencyCallbackModeListener
import android.telephony.TelephonyManager
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
@@ -58,6 +59,7 @@ import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.CarrierConfigRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.carrierConfigRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Factory.Companion.tableBufferLogName
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
@@ -78,6 +80,7 @@ import com.android.wifitrackerlib.MergedCarrierEntry
import com.android.wifitrackerlib.WifiEntry
import com.android.wifitrackerlib.WifiPickerTracker
import com.google.common.truth.Truth.assertThat
+import java.time.Duration
import java.util.UUID
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.launchIn
@@ -93,6 +96,7 @@ import org.junit.Test
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mock
+import org.mockito.Mockito.atLeast
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.any
@@ -104,34 +108,311 @@ import org.mockito.kotlin.whenever
// This is required because our [SubscriptionManager.OnSubscriptionsChangedListener] uses a looper
// to run the callback and this makes the looper place nicely with TestScope etc.
@TestableLooper.RunWithLooper
-class MobileConnectionsRepositoryTest : SysuiTestCase() {
+class MobileConnectionsRepositoryImplTest :
+ MobileConnectionsRepositoryTest<MobileConnectionsRepositoryImpl>() {
+ override fun recreateRepo() =
+ MobileConnectionsRepositoryImpl(
+ connectivityRepository = connectivityRepository,
+ subscriptionManager = subscriptionManager,
+ subscriptionManagerProxy = subscriptionManagerProxy,
+ telephonyManager = telephonyManager,
+ logger = logger,
+ tableLogger = summaryLogger,
+ mobileMappingsProxy = mobileMappings,
+ broadcastDispatcher = fakeBroadcastDispatcher,
+ context = context,
+ bgDispatcher = testDispatcher,
+ scope = testScope.backgroundScope,
+ mainDispatcher = testDispatcher,
+ airplaneModeRepository = airplaneModeRepository,
+ wifiRepository = wifiRepository,
+ fullMobileRepoFactory = fullConnectionFactory,
+ keyguardUpdateMonitor = updateMonitor,
+ dumpManager = mock(),
+ )
+
+ @Test
+ fun activeDataSentBeforeSubscriptionList_subscriptionReusesActiveDataRepo() =
+ testScope.runTest {
+ val activeRepo by collectLastValue(underTest.activeMobileDataRepository)
+ collectLastValue(underTest.subscriptions)
+
+ // GIVEN active repo is updated before the subscription list updates
+ getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
+ .onActiveDataSubscriptionIdChanged(SUB_2_ID)
+
+ assertThat(activeRepo).isNotNull()
+
+ // GIVEN the subscription list is then updated which includes the active data sub id
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_2))
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ // WHEN requesting a connection repository for the subscription
+ val newRepo = underTest.getRepoForSubId(SUB_2_ID)
+
+ // THEN the newly request repo has been cached and reused
+ assertThat(activeRepo).isSameInstanceAs(newRepo)
+ }
+
+ @Test
+ fun testConnectionRepository_invalidSubId_doesNotThrow() =
+ testScope.runTest {
+ underTest.getRepoForSubId(SUB_1_ID)
+ // No exception
+ }
+
+ @Test
+ fun testConnectionRepository_carrierMergedAndMobileSubs_usesCorrectRepos() =
+ testScope.runTest {
+ collectLastValue(underTest.subscriptions)
+
+ getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_CM)
+ setWifiState(isCarrierMerged = true)
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_1, SUB_CM))
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ val carrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID)
+ val mobileRepo = underTest.getRepoForSubId(SUB_1_ID)
+ assertThat(carrierMergedRepo.getIsCarrierMerged()).isTrue()
+ assertThat(mobileRepo.getIsCarrierMerged()).isFalse()
+ }
+
+ @Test
+ fun testSubscriptions_subNoLongerCarrierMerged_repoUpdates() =
+ testScope.runTest {
+ collectLastValue(underTest.subscriptions)
+
+ getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_CM)
+ setWifiState(isCarrierMerged = true)
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_1, SUB_CM))
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ val carrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID)
+ var mobileRepo = underTest.getRepoForSubId(SUB_1_ID)
+ assertThat(carrierMergedRepo.getIsCarrierMerged()).isTrue()
+ assertThat(mobileRepo.getIsCarrierMerged()).isFalse()
+
+ // WHEN the wifi network updates to be not carrier merged
+ getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_ACTIVE)
+ setWifiState(isCarrierMerged = false)
+ runCurrent()
+
+ // THEN the repos update
+ val noLongerCarrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID)
+ mobileRepo = underTest.getRepoForSubId(SUB_1_ID)
+ assertThat(noLongerCarrierMergedRepo.getIsCarrierMerged()).isFalse()
+ assertThat(mobileRepo.getIsCarrierMerged()).isFalse()
+ }
+
+ @Test
+ fun testSubscriptions_subBecomesCarrierMerged_repoUpdates() =
+ testScope.runTest {
+ collectLastValue(underTest.subscriptions)
+
+ getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_ACTIVE)
+ setWifiState(isCarrierMerged = false)
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_1, SUB_CM))
+ getSubscriptionCallback().onSubscriptionsChanged()
+ runCurrent()
+
+ val notYetCarrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID)
+ var mobileRepo = underTest.getRepoForSubId(SUB_1_ID)
+ assertThat(notYetCarrierMergedRepo.getIsCarrierMerged()).isFalse()
+ assertThat(mobileRepo.getIsCarrierMerged()).isFalse()
+
+ // WHEN the wifi network updates to be carrier merged
+ getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_CM)
+ setWifiState(isCarrierMerged = true)
+ runCurrent()
+
+ // THEN the repos update
+ val carrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID)
+ mobileRepo = underTest.getRepoForSubId(SUB_1_ID)
+ assertThat(carrierMergedRepo.getIsCarrierMerged()).isTrue()
+ assertThat(mobileRepo.getIsCarrierMerged()).isFalse()
+ }
+
+ @Test
+ @Ignore("b/333912012")
+ fun testConnectionCache_clearsInvalidSubscriptions() =
+ testScope.runTest {
+ collectLastValue(underTest.subscriptions)
+
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_1, SUB_2))
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ // Get repos to trigger caching
+ val repo1 = underTest.getRepoForSubId(SUB_1_ID)
+ val repo2 = underTest.getRepoForSubId(SUB_2_ID)
+
+ assertThat(underTest.getSubIdRepoCache())
+ .containsExactly(SUB_1_ID, repo1, SUB_2_ID, repo2)
+
+ // SUB_2 disappears
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_1))
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ assertThat(underTest.getSubIdRepoCache()).containsExactly(SUB_1_ID, repo1)
+ }
+
+ @Test
+ @Ignore("b/333912012")
+ fun testConnectionCache_clearsInvalidSubscriptions_includingCarrierMerged() =
+ testScope.runTest {
+ collectLastValue(underTest.subscriptions)
+
+ getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_CM)
+ setWifiState(isCarrierMerged = true)
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_1, SUB_2, SUB_CM))
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ // Get repos to trigger caching
+ val repo1 = underTest.getRepoForSubId(SUB_1_ID)
+ val repo2 = underTest.getRepoForSubId(SUB_2_ID)
+ val repoCarrierMerged = underTest.getRepoForSubId(SUB_CM_ID)
+
+ assertThat(underTest.getSubIdRepoCache())
+ .containsExactly(SUB_1_ID, repo1, SUB_2_ID, repo2, SUB_CM_ID, repoCarrierMerged)
+
+ // SUB_2 and SUB_CM disappear
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_1))
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ assertThat(underTest.getSubIdRepoCache()).containsExactly(SUB_1_ID, repo1)
+ }
+
+ /** Regression test for b/261706421 */
+ @Test
+ @Ignore("b/333912012")
+ fun testConnectionsCache_clearMultipleSubscriptionsAtOnce_doesNotThrow() =
+ testScope.runTest {
+ collectLastValue(underTest.subscriptions)
+
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_1, SUB_2))
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ // Get repos to trigger caching
+ val repo1 = underTest.getRepoForSubId(SUB_1_ID)
+ val repo2 = underTest.getRepoForSubId(SUB_2_ID)
+
+ assertThat(underTest.getSubIdRepoCache())
+ .containsExactly(SUB_1_ID, repo1, SUB_2_ID, repo2)
+
+ // All subscriptions disappear
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList).thenReturn(listOf())
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ assertThat(underTest.getSubIdRepoCache()).isEmpty()
+ }
+
+ @Test
+ fun getRepoForSubId_activeDataSubIdIsRequestedBeforeSubscriptionsUpdate() =
+ testScope.runTest {
+ var latestActiveRepo: MobileConnectionRepository? = null
+ collectLastValue(
+ underTest.activeMobileDataSubscriptionId.filterNotNull().onEach {
+ latestActiveRepo = underTest.getRepoForSubId(it)
+ }
+ )
+
+ val latestSubscriptions by collectLastValue(underTest.subscriptions)
+
+ // Active data subscription id is sent, but no subscription change has been posted yet
+ getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
+ .onActiveDataSubscriptionIdChanged(SUB_2_ID)
+
+ // Subscriptions list is empty
+ assertThat(latestSubscriptions).isEmpty()
+ // getRepoForSubId does not throw
+ assertThat(latestActiveRepo).isNotNull()
+ }
+
+ @Test
+ fun testConnectionsCache_keepsReposCached() =
+ testScope.runTest {
+ // Collect subscriptions to start the job
+ collectLastValue(underTest.subscriptions)
+
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_1))
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ val repo1_1 = underTest.getRepoForSubId(SUB_1_ID)
+
+ // All subscriptions disappear
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList).thenReturn(listOf())
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ // Sub1 comes back
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_1))
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ val repo1_2 = underTest.getRepoForSubId(SUB_1_ID)
+
+ assertThat(repo1_1).isSameInstanceAs(repo1_2)
+ }
+
+ @Test
+ fun testConnectionsCache_doesNotDropReferencesThatHaveBeenRealized() =
+ testScope.runTest {
+ // Collect subscriptions to start the job
+ collectLastValue(underTest.subscriptions)
+
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_1))
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ // Client grabs a reference to a repository, but doesn't keep it around
+ underTest.getRepoForSubId(SUB_1_ID)
+
+ // All subscriptions disappear
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList).thenReturn(listOf())
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ val repo1 = underTest.getRepoForSubId(SUB_1_ID)
+
+ assertThat(repo1).isNotNull()
+ }
+}
+
+abstract class MobileConnectionsRepositoryTest<T : MobileConnectionsRepository> : SysuiTestCase() {
private val kosmos = testKosmos()
- private val flags =
+ protected val flags =
FakeFeatureFlagsClassic().also { it.set(Flags.ROAMING_INDICATOR_VIA_DISPLAY_INFO, true) }
private lateinit var connectionFactory: MobileConnectionRepositoryImpl.Factory
private lateinit var carrierMergedFactory: CarrierMergedConnectionRepository.Factory
- private lateinit var fullConnectionFactory: FullMobileConnectionRepository.Factory
- private lateinit var connectivityRepository: ConnectivityRepository
- private lateinit var airplaneModeRepository: FakeAirplaneModeRepository
- private lateinit var wifiRepository: WifiRepository
+ protected lateinit var fullConnectionFactory: FullMobileConnectionRepository.Factory
+ protected lateinit var connectivityRepository: ConnectivityRepository
+ protected lateinit var airplaneModeRepository: FakeAirplaneModeRepository
+ protected lateinit var wifiRepository: WifiRepository
private lateinit var carrierConfigRepository: CarrierConfigRepository
- @Mock private lateinit var connectivityManager: ConnectivityManager
- @Mock private lateinit var subscriptionManager: SubscriptionManager
- @Mock private lateinit var telephonyManager: TelephonyManager
- @Mock private lateinit var logger: MobileInputLogger
- private val summaryLogger = logcatTableLogBuffer(kosmos, "summaryLogger")
- @Mock private lateinit var logBufferFactory: TableLogBufferFactory
- @Mock private lateinit var updateMonitor: KeyguardUpdateMonitor
+ @Mock protected lateinit var connectivityManager: ConnectivityManager
+ @Mock protected lateinit var subscriptionManager: SubscriptionManager
+ @Mock protected lateinit var telephonyManager: TelephonyManager
+ @Mock protected lateinit var logger: MobileInputLogger
+ protected val summaryLogger = logcatTableLogBuffer(kosmos, "summaryLogger")
+ @Mock protected lateinit var logBufferFactory: TableLogBufferFactory
+ @Mock protected lateinit var updateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var wifiManager: WifiManager
@Mock private lateinit var wifiPickerTrackerFactory: WifiPickerTrackerFactory
@Mock private lateinit var wifiPickerTracker: WifiPickerTracker
private val wifiTableLogBuffer = logcatTableLogBuffer(kosmos, "wifiTableLog")
- private val mobileMappings = FakeMobileMappingsProxy()
- private val subscriptionManagerProxy = FakeSubscriptionManagerProxy()
+ protected val mobileMappings = FakeMobileMappingsProxy()
+ protected val subscriptionManagerProxy = FakeSubscriptionManagerProxy()
private val mainExecutor = FakeExecutor(FakeSystemClock())
private val wifiLogBuffer = LogBuffer("wifi", maxSize = 100, logcatEchoTracker = mock())
private val wifiPickerTrackerCallback =
@@ -139,10 +420,10 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
private val vcnTransportInfo = VcnTransportInfo.Builder().build()
private val userRepository = kosmos.fakeUserRepository
- private val testDispatcher = StandardTestDispatcher()
- private val testScope = TestScope(testDispatcher)
+ protected val testDispatcher = StandardTestDispatcher()
+ protected val testScope = TestScope(testDispatcher)
- private lateinit var underTest: MobileConnectionsRepositoryImpl
+ protected lateinit var underTest: T
@Before
fun setUp() {
@@ -237,30 +518,13 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
carrierMergedRepoFactory = carrierMergedFactory,
)
- underTest =
- MobileConnectionsRepositoryImpl(
- connectivityRepository,
- subscriptionManager,
- subscriptionManagerProxy,
- telephonyManager,
- logger,
- summaryLogger,
- mobileMappings,
- fakeBroadcastDispatcher,
- context,
- /* bgDispatcher = */ testDispatcher,
- testScope.backgroundScope,
- /* mainDispatcher = */ testDispatcher,
- airplaneModeRepository,
- wifiRepository,
- fullConnectionFactory,
- updateMonitor,
- mock(),
- )
+ underTest = recreateRepo()
testScope.runCurrent()
}
+ abstract fun recreateRepo(): T
+
@Test
fun testSubscriptions_initiallyEmpty() =
testScope.runTest {
@@ -410,9 +674,17 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
fun activeRepo_updatesWithActiveDataId() =
testScope.runTest {
val latest by collectLastValue(underTest.activeMobileDataRepository)
+ runCurrent()
- getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
- .onActiveDataSubscriptionIdChanged(SUB_2_ID)
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_2))
+ getSubscriptionCallbacks().forEach { it.onSubscriptionsChanged() }
+ runCurrent()
+
+ getTelephonyCallbacksForType<ActiveDataSubscriptionIdListener>().forEach {
+ it.onActiveDataSubscriptionIdChanged(SUB_2_ID)
+ }
+ runCurrent()
assertThat(latest?.subId).isEqualTo(SUB_2_ID)
}
@@ -422,6 +694,10 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
testScope.runTest {
val latest by collectLastValue(underTest.activeMobileDataRepository)
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_2))
+ getSubscriptionCallbacks().forEach { it.onSubscriptionsChanged() }
+
getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
.onActiveDataSubscriptionIdChanged(SUB_2_ID)
@@ -437,60 +713,15 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
/** Regression test for b/268146648. */
fun activeSubIdIsSetBeforeSubscriptionsAreUpdated_doesNotThrow() =
testScope.runTest {
- val activeRepo by collectLastValue(underTest.activeMobileDataRepository)
+ val activeRepo = collectLastValue(underTest.activeMobileDataRepository)
val subscriptions by collectLastValue(underTest.subscriptions)
- getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
- .onActiveDataSubscriptionIdChanged(SUB_2_ID)
+ getTelephonyCallbacksForType<ActiveDataSubscriptionIdListener>().forEach {
+ it.onActiveDataSubscriptionIdChanged(SUB_2_ID)
+ }
assertThat(subscriptions).isEmpty()
- assertThat(activeRepo).isNotNull()
- }
-
- @Test
- fun getRepoForSubId_activeDataSubIdIsRequestedBeforeSubscriptionsUpdate() =
- testScope.runTest {
- var latestActiveRepo: MobileConnectionRepository? = null
- collectLastValue(
- underTest.activeMobileDataSubscriptionId.filterNotNull().onEach {
- latestActiveRepo = underTest.getRepoForSubId(it)
- }
- )
-
- val latestSubscriptions by collectLastValue(underTest.subscriptions)
-
- // Active data subscription id is sent, but no subscription change has been posted yet
- getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
- .onActiveDataSubscriptionIdChanged(SUB_2_ID)
-
- // Subscriptions list is empty
- assertThat(latestSubscriptions).isEmpty()
- // getRepoForSubId does not throw
- assertThat(latestActiveRepo).isNotNull()
- }
-
- @Test
- fun activeDataSentBeforeSubscriptionList_subscriptionReusesActiveDataRepo() =
- testScope.runTest {
- val activeRepo by collectLastValue(underTest.activeMobileDataRepository)
- collectLastValue(underTest.subscriptions)
-
- // GIVEN active repo is updated before the subscription list updates
- getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
- .onActiveDataSubscriptionIdChanged(SUB_2_ID)
-
- assertThat(activeRepo).isNotNull()
-
- // GIVEN the subscription list is then updated which includes the active data sub id
- whenever(subscriptionManager.completeActiveSubscriptionInfoList)
- .thenReturn(listOf(SUB_2))
- getSubscriptionCallback().onSubscriptionsChanged()
-
- // WHEN requesting a connection repository for the subscription
- val newRepo = underTest.getRepoForSubId(SUB_2_ID)
-
- // THEN the newly request repo has been cached and reused
- assertThat(activeRepo).isSameInstanceAs(newRepo)
+ activeRepo.invoke() // does not throw
}
@Test
@@ -501,6 +732,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
whenever(subscriptionManager.completeActiveSubscriptionInfoList)
.thenReturn(listOf(SUB_1))
getSubscriptionCallback().onSubscriptionsChanged()
+ runCurrent()
val repo1 = underTest.getRepoForSubId(SUB_1_ID)
val repo2 = underTest.getRepoForSubId(SUB_1_ID)
@@ -525,80 +757,6 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
assertThat(repo1).isSameInstanceAs(repo2)
}
- @Test
- fun testConnectionRepository_carrierMergedAndMobileSubs_usesCorrectRepos() =
- testScope.runTest {
- collectLastValue(underTest.subscriptions)
-
- getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_CM)
- setWifiState(isCarrierMerged = true)
- whenever(subscriptionManager.completeActiveSubscriptionInfoList)
- .thenReturn(listOf(SUB_1, SUB_CM))
- getSubscriptionCallback().onSubscriptionsChanged()
-
- val carrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID)
- val mobileRepo = underTest.getRepoForSubId(SUB_1_ID)
- assertThat(carrierMergedRepo.getIsCarrierMerged()).isTrue()
- assertThat(mobileRepo.getIsCarrierMerged()).isFalse()
- }
-
- @Test
- fun testSubscriptions_subNoLongerCarrierMerged_repoUpdates() =
- testScope.runTest {
- collectLastValue(underTest.subscriptions)
-
- getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_CM)
- setWifiState(isCarrierMerged = true)
- whenever(subscriptionManager.completeActiveSubscriptionInfoList)
- .thenReturn(listOf(SUB_1, SUB_CM))
- getSubscriptionCallback().onSubscriptionsChanged()
-
- val carrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID)
- var mobileRepo = underTest.getRepoForSubId(SUB_1_ID)
- assertThat(carrierMergedRepo.getIsCarrierMerged()).isTrue()
- assertThat(mobileRepo.getIsCarrierMerged()).isFalse()
-
- // WHEN the wifi network updates to be not carrier merged
- getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_ACTIVE)
- setWifiState(isCarrierMerged = false)
- runCurrent()
-
- // THEN the repos update
- val noLongerCarrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID)
- mobileRepo = underTest.getRepoForSubId(SUB_1_ID)
- assertThat(noLongerCarrierMergedRepo.getIsCarrierMerged()).isFalse()
- assertThat(mobileRepo.getIsCarrierMerged()).isFalse()
- }
-
- @Test
- fun testSubscriptions_subBecomesCarrierMerged_repoUpdates() =
- testScope.runTest {
- collectLastValue(underTest.subscriptions)
-
- getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_ACTIVE)
- setWifiState(isCarrierMerged = false)
- whenever(subscriptionManager.completeActiveSubscriptionInfoList)
- .thenReturn(listOf(SUB_1, SUB_CM))
- getSubscriptionCallback().onSubscriptionsChanged()
- runCurrent()
-
- val notYetCarrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID)
- var mobileRepo = underTest.getRepoForSubId(SUB_1_ID)
- assertThat(notYetCarrierMergedRepo.getIsCarrierMerged()).isFalse()
- assertThat(mobileRepo.getIsCarrierMerged()).isFalse()
-
- // WHEN the wifi network updates to be carrier merged
- getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_CM)
- setWifiState(isCarrierMerged = true)
- runCurrent()
-
- // THEN the repos update
- val carrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID)
- mobileRepo = underTest.getRepoForSubId(SUB_1_ID)
- assertThat(carrierMergedRepo.getIsCarrierMerged()).isTrue()
- assertThat(mobileRepo.getIsCarrierMerged()).isFalse()
- }
-
@SuppressLint("UnspecifiedRegisterReceiverFlag")
@Test
fun testDeviceEmergencyCallState_eagerlyChecksState() =
@@ -674,139 +832,6 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
}
@Test
- @Ignore("b/333912012")
- fun testConnectionCache_clearsInvalidSubscriptions() =
- testScope.runTest {
- collectLastValue(underTest.subscriptions)
-
- whenever(subscriptionManager.completeActiveSubscriptionInfoList)
- .thenReturn(listOf(SUB_1, SUB_2))
- getSubscriptionCallback().onSubscriptionsChanged()
-
- // Get repos to trigger caching
- val repo1 = underTest.getRepoForSubId(SUB_1_ID)
- val repo2 = underTest.getRepoForSubId(SUB_2_ID)
-
- assertThat(underTest.getSubIdRepoCache())
- .containsExactly(SUB_1_ID, repo1, SUB_2_ID, repo2)
-
- // SUB_2 disappears
- whenever(subscriptionManager.completeActiveSubscriptionInfoList)
- .thenReturn(listOf(SUB_1))
- getSubscriptionCallback().onSubscriptionsChanged()
-
- assertThat(underTest.getSubIdRepoCache()).containsExactly(SUB_1_ID, repo1)
- }
-
- @Test
- @Ignore("b/333912012")
- fun testConnectionCache_clearsInvalidSubscriptions_includingCarrierMerged() =
- testScope.runTest {
- collectLastValue(underTest.subscriptions)
-
- getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_CM)
- setWifiState(isCarrierMerged = true)
- whenever(subscriptionManager.completeActiveSubscriptionInfoList)
- .thenReturn(listOf(SUB_1, SUB_2, SUB_CM))
- getSubscriptionCallback().onSubscriptionsChanged()
-
- // Get repos to trigger caching
- val repo1 = underTest.getRepoForSubId(SUB_1_ID)
- val repo2 = underTest.getRepoForSubId(SUB_2_ID)
- val repoCarrierMerged = underTest.getRepoForSubId(SUB_CM_ID)
-
- assertThat(underTest.getSubIdRepoCache())
- .containsExactly(SUB_1_ID, repo1, SUB_2_ID, repo2, SUB_CM_ID, repoCarrierMerged)
-
- // SUB_2 and SUB_CM disappear
- whenever(subscriptionManager.completeActiveSubscriptionInfoList)
- .thenReturn(listOf(SUB_1))
- getSubscriptionCallback().onSubscriptionsChanged()
-
- assertThat(underTest.getSubIdRepoCache()).containsExactly(SUB_1_ID, repo1)
- }
-
- /** Regression test for b/261706421 */
- @Test
- @Ignore("b/333912012")
- fun testConnectionsCache_clearMultipleSubscriptionsAtOnce_doesNotThrow() =
- testScope.runTest {
- collectLastValue(underTest.subscriptions)
-
- whenever(subscriptionManager.completeActiveSubscriptionInfoList)
- .thenReturn(listOf(SUB_1, SUB_2))
- getSubscriptionCallback().onSubscriptionsChanged()
-
- // Get repos to trigger caching
- val repo1 = underTest.getRepoForSubId(SUB_1_ID)
- val repo2 = underTest.getRepoForSubId(SUB_2_ID)
-
- assertThat(underTest.getSubIdRepoCache())
- .containsExactly(SUB_1_ID, repo1, SUB_2_ID, repo2)
-
- // All subscriptions disappear
- whenever(subscriptionManager.completeActiveSubscriptionInfoList).thenReturn(listOf())
- getSubscriptionCallback().onSubscriptionsChanged()
-
- assertThat(underTest.getSubIdRepoCache()).isEmpty()
- }
-
- @Test
- fun testConnectionsCache_keepsReposCached() =
- testScope.runTest {
- // Collect subscriptions to start the job
- collectLastValue(underTest.subscriptions)
-
- whenever(subscriptionManager.completeActiveSubscriptionInfoList)
- .thenReturn(listOf(SUB_1))
- getSubscriptionCallback().onSubscriptionsChanged()
-
- val repo1_1 = underTest.getRepoForSubId(SUB_1_ID)
-
- // All subscriptions disappear
- whenever(subscriptionManager.completeActiveSubscriptionInfoList).thenReturn(listOf())
- getSubscriptionCallback().onSubscriptionsChanged()
-
- // Sub1 comes back
- whenever(subscriptionManager.completeActiveSubscriptionInfoList)
- .thenReturn(listOf(SUB_1))
- getSubscriptionCallback().onSubscriptionsChanged()
-
- val repo1_2 = underTest.getRepoForSubId(SUB_1_ID)
-
- assertThat(repo1_1).isSameInstanceAs(repo1_2)
- }
-
- @Test
- fun testConnectionsCache_doesNotDropReferencesThatHaveBeenRealized() =
- testScope.runTest {
- // Collect subscriptions to start the job
- collectLastValue(underTest.subscriptions)
-
- whenever(subscriptionManager.completeActiveSubscriptionInfoList)
- .thenReturn(listOf(SUB_1))
- getSubscriptionCallback().onSubscriptionsChanged()
-
- // Client grabs a reference to a repository, but doesn't keep it around
- underTest.getRepoForSubId(SUB_1_ID)
-
- // All subscriptions disappear
- whenever(subscriptionManager.completeActiveSubscriptionInfoList).thenReturn(listOf())
- getSubscriptionCallback().onSubscriptionsChanged()
-
- val repo1 = underTest.getRepoForSubId(SUB_1_ID)
-
- assertThat(repo1).isNotNull()
- }
-
- @Test
- fun testConnectionRepository_invalidSubId_doesNotThrow() =
- testScope.runTest {
- underTest.getRepoForSubId(SUB_1_ID)
- // No exception
- }
-
- @Test
fun connectionRepository_logBufferContainsSubIdInItsName() =
testScope.runTest {
collectLastValue(underTest.subscriptions)
@@ -814,6 +839,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
whenever(subscriptionManager.completeActiveSubscriptionInfoList)
.thenReturn(listOf(SUB_1, SUB_2))
getSubscriptionCallback().onSubscriptionsChanged()
+ runCurrent()
// Get repos to trigger creation
underTest.getRepoForSubId(SUB_1_ID)
@@ -848,15 +874,27 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
fun defaultDataSubId_fetchesInitialValueOnStart() =
testScope.runTest {
subscriptionManagerProxy.defaultDataSubId = 2
+ underTest = recreateRepo()
+
val latest by collectLastValue(underTest.defaultDataSubId)
assertThat(latest).isEqualTo(2)
}
+ private fun setDefaultDataSubId(subId: Int) {
+ subscriptionManagerProxy.defaultDataSubId = subId
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED).apply {
+ putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId)
+ },
+ )
+ }
+
@Test
fun defaultDataSubId_filtersOutInvalidSubIds() =
testScope.runTest {
- subscriptionManagerProxy.defaultDataSubId = INVALID_SUBSCRIPTION_ID
+ setDefaultDataSubId(INVALID_SUBSCRIPTION_ID)
val latest by collectLastValue(underTest.defaultDataSubId)
assertThat(latest).isNull()
@@ -865,15 +903,12 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
@Test
fun defaultDataSubId_filtersOutInvalidSubIds_fromValidToInvalid() =
testScope.runTest {
- subscriptionManagerProxy.defaultDataSubId = 2
+ setDefaultDataSubId(2)
val latest by collectLastValue(underTest.defaultDataSubId)
assertThat(latest).isEqualTo(2)
- val intent =
- Intent(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)
- .putExtra(PhoneConstants.SUBSCRIPTION_KEY, INVALID_SUBSCRIPTION_ID)
- fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, intent)
+ setDefaultDataSubId(INVALID_SUBSCRIPTION_ID)
assertThat(latest).isNull()
}
@@ -881,7 +916,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
@Test
fun defaultDataSubId_fetchesCurrentOnRestart() =
testScope.runTest {
- subscriptionManagerProxy.defaultDataSubId = 2
+ setDefaultDataSubId(2)
var latest: Int? = null
var job = underTest.defaultDataSubId.onEach { latest = it }.launchIn(this)
runCurrent()
@@ -894,7 +929,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
latest = null
- subscriptionManagerProxy.defaultDataSubId = 1
+ setDefaultDataSubId(1)
job = underTest.defaultDataSubId.onEach { latest = it }.launchIn(this)
runCurrent()
@@ -1281,26 +1316,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
// The initial value will be fetched when the repo is created, so we need to override
// the resources and then re-create the repo.
- underTest =
- MobileConnectionsRepositoryImpl(
- connectivityRepository,
- subscriptionManager,
- subscriptionManagerProxy,
- telephonyManager,
- logger,
- summaryLogger,
- mobileMappings,
- fakeBroadcastDispatcher,
- context,
- testDispatcher,
- testScope.backgroundScope,
- testDispatcher,
- airplaneModeRepository,
- wifiRepository,
- fullConnectionFactory,
- updateMonitor,
- mock(),
- )
+ underTest = recreateRepo()
val latest by collectLastValue(underTest.defaultDataSubRatConfig)
@@ -1428,6 +1444,12 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
assertThat(underTest.getIsAnySimSecure()).isFalse()
whenever(updateMonitor.isSimPinSecure).thenReturn(true)
+ org.mockito.kotlin
+ .argumentCaptor<KeyguardUpdateMonitorCallback>()
+ .apply { verify(updateMonitor, atLeast(0)).registerCallback(capture()) }
+ .allValues
+ .forEach { it.onSimStateChanged(0, 0, 0) }
+ runCurrent()
assertThat(underTest.getIsAnySimSecure()).isTrue()
}
@@ -1447,19 +1469,26 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
testScope.runTest {
whenever(telephonyManager.emergencyCallbackMode).thenReturn(true)
+ getTelephonyCallbacksForType<EmergencyCallbackModeListener>().forEach {
+ it.onCallbackModeStarted(
+ TelephonyManager.EMERGENCY_CALLBACK_MODE_SMS,
+ Duration.ZERO,
+ 0,
+ )
+ }
runCurrent()
assertThat(underTest.isInEcmMode()).isTrue()
}
- private fun TestScope.getDefaultNetworkCallback(): ConnectivityManager.NetworkCallback {
+ protected fun TestScope.getDefaultNetworkCallback(): ConnectivityManager.NetworkCallback {
runCurrent()
val callbackCaptor = argumentCaptor<ConnectivityManager.NetworkCallback>()
verify(connectivityManager).registerDefaultNetworkCallback(callbackCaptor.capture())
return callbackCaptor.value!!
}
- private fun setWifiState(isCarrierMerged: Boolean) {
+ protected fun setWifiState(isCarrierMerged: Boolean) {
if (isCarrierMerged) {
val mergedEntry =
mock<MergedCarrierEntry>().apply {
@@ -1481,7 +1510,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
wifiPickerTrackerCallback.value.onWifiEntriesChanged()
}
- private fun TestScope.getSubscriptionCallback():
+ protected fun TestScope.getSubscriptionCallback():
SubscriptionManager.OnSubscriptionsChangedListener {
runCurrent()
val callbackCaptor = argumentCaptor<SubscriptionManager.OnSubscriptionsChangedListener>()
@@ -1490,25 +1519,39 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
return callbackCaptor.value!!
}
- private fun TestScope.getTelephonyCallbacks(): List<TelephonyCallback> {
+ protected fun TestScope.getSubscriptionCallbacks():
+ List<SubscriptionManager.OnSubscriptionsChangedListener> {
+ runCurrent()
+ val callbackCaptor = argumentCaptor<SubscriptionManager.OnSubscriptionsChangedListener>()
+ verify(subscriptionManager, atLeast(0))
+ .addOnSubscriptionsChangedListener(any(), callbackCaptor.capture())
+ return callbackCaptor.allValues
+ }
+
+ fun TestScope.getTelephonyCallbacks(): List<TelephonyCallback> {
runCurrent()
val callbackCaptor = argumentCaptor<TelephonyCallback>()
- verify(telephonyManager).registerTelephonyCallback(any(), callbackCaptor.capture())
+ verify(telephonyManager, atLeast(0))
+ .registerTelephonyCallback(any(), callbackCaptor.capture())
return callbackCaptor.allValues
}
- private inline fun <reified T> TestScope.getTelephonyCallbackForType(): T {
- val cbs = this.getTelephonyCallbacks().filterIsInstance<T>()
+ inline fun <reified T> TestScope.getTelephonyCallbackForType(): T {
+ val cbs = getTelephonyCallbacksForType<T>()
assertThat(cbs.size).isEqualTo(1)
return cbs[0]
}
+ inline fun <reified T> TestScope.getTelephonyCallbacksForType(): List<T> {
+ return getTelephonyCallbacks().filterIsInstance<T>()
+ }
+
companion object {
// Subscription 1
- private const val SUB_1_ID = 1
+ const val SUB_1_ID = 1
private const val SUB_1_NAME = "Carrier $SUB_1_ID"
private val GROUP_1 = ParcelUuid(UUID.randomUUID())
- private val SUB_1 =
+ val SUB_1 =
mock<SubscriptionInfo>().also {
whenever(it.subscriptionId).thenReturn(SUB_1_ID)
whenever(it.groupUuid).thenReturn(GROUP_1)
@@ -1524,10 +1567,10 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
)
// Subscription 2
- private const val SUB_2_ID = 2
+ const val SUB_2_ID = 2
private const val SUB_2_NAME = "Carrier $SUB_2_ID"
private val GROUP_2 = ParcelUuid(UUID.randomUUID())
- private val SUB_2 =
+ val SUB_2 =
mock<SubscriptionInfo>().also {
whenever(it.subscriptionId).thenReturn(SUB_2_ID)
whenever(it.groupUuid).thenReturn(GROUP_2)
@@ -1552,6 +1595,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
whenever(it.subscriptionId).thenReturn(SUB_3_ID_GROUPED)
whenever(it.groupUuid).thenReturn(GROUP_ID_3_4)
whenever(it.profileClass).thenReturn(PROFILE_CLASS_UNSET)
+ whenever(it.carrierName).thenReturn("")
}
// Subscription 4
@@ -1561,17 +1605,18 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
whenever(it.subscriptionId).thenReturn(SUB_4_ID_GROUPED)
whenever(it.groupUuid).thenReturn(GROUP_ID_3_4)
whenever(it.profileClass).thenReturn(PROFILE_CLASS_UNSET)
+ whenever(it.carrierName).thenReturn("")
}
// Subs 3 and 4 are considered to be in the same group ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
private const val NET_ID = 123
- private val NETWORK = mock<Network>().apply { whenever(getNetId()).thenReturn(NET_ID) }
+ val NETWORK = mock<Network>().apply { whenever(getNetId()).thenReturn(NET_ID) }
// Carrier merged subscription
- private const val SUB_CM_ID = 5
+ const val SUB_CM_ID = 5
private const val SUB_CM_NAME = "Carrier $SUB_CM_ID"
- private val SUB_CM =
+ val SUB_CM =
mock<SubscriptionInfo>().also {
whenever(it.subscriptionId).thenReturn(SUB_CM_ID)
whenever(it.carrierName).thenReturn(SUB_CM_NAME)
@@ -1590,7 +1635,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
whenever(this.isCarrierMerged).thenReturn(true)
whenever(this.subscriptionId).thenReturn(SUB_CM_ID)
}
- private val WIFI_NETWORK_CAPS_CM =
+ val WIFI_NETWORK_CAPS_CM =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
whenever(it.transportInfo).thenReturn(WIFI_INFO_CM)
@@ -1602,7 +1647,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
whenever(this.isPrimary).thenReturn(true)
whenever(this.isCarrierMerged).thenReturn(false)
}
- private val WIFI_NETWORK_CAPS_ACTIVE =
+ val WIFI_NETWORK_CAPS_ACTIVE =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
whenever(it.transportInfo).thenReturn(WIFI_INFO_ACTIVE)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
index 3247a1ab6eb0..8ff8fe6cc3d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
@@ -44,6 +44,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.R;
import com.android.internal.view.RotationPolicy;
+import com.android.settingslib.devicestate.AndroidSecureSettings;
import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
@@ -117,7 +118,8 @@ public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase
ArgumentCaptor.forClass(DeviceStateManager.DeviceStateCallback.class);
mContentResolver = mContext.getContentResolver();
- mSettingsManager = DeviceStateRotationLockSettingsManager.getInstance(mContext);
+ mSettingsManager = new DeviceStateRotationLockSettingsManager(mContext,
+ new AndroidSecureSettings(mContentResolver));
mDeviceStateRotationLockSettingController =
new DeviceStateRotationLockSettingController(
mFakeRotationPolicy,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
index 54df9e99baa5..bb6ba46f1a0b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
@@ -25,13 +25,12 @@ import android.view.WindowManager
import android.view.accessibility.AccessibilityManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.internal.logging.InstanceId
import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
-import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
import com.android.systemui.util.concurrency.DelayableExecutor
@@ -78,7 +77,7 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
@Mock
private lateinit var dumpManager: DumpManager
@Mock
- private lateinit var windowManager: ViewCaptureAwareWindowManager
+ private lateinit var windowManager: WindowManager
@Mock
private lateinit var powerManager: PowerManager
@@ -1143,7 +1142,7 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
inner class TestController(
context: Context,
logger: TemporaryViewLogger<ViewInfo>,
- viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
+ windowManager: WindowManager,
@Main mainExecutor: DelayableExecutor,
accessibilityManager: AccessibilityManager,
configurationController: ConfigurationController,
@@ -1155,7 +1154,7 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
) : TemporaryViewDisplayController<ViewInfo, TemporaryViewLogger<ViewInfo>>(
context,
logger,
- viewCaptureAwareWindowManager,
+ windowManager,
mainExecutor,
accessibilityManager,
configurationController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
index 4260b6558950..664f2df62782 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
@@ -30,8 +30,6 @@ import android.widget.TextView
import androidx.core.animation.doOnCancel
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.app.viewcapture.ViewCapture
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.internal.logging.InstanceId
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.SysuiTestCase
@@ -86,7 +84,6 @@ class ChipbarCoordinatorTest : SysuiTestCase() {
@Mock private lateinit var viewUtil: ViewUtil
@Mock private lateinit var vibratorHelper: VibratorHelper
@Mock private lateinit var swipeGestureHandler: SwipeChipbarAwayGestureHandler
- @Mock private lateinit var lazyViewCapture: Lazy<ViewCapture>
private lateinit var chipbarAnimator: TestChipbarAnimator
private lateinit var fakeWakeLockBuilder: WakeLockFake.Builder
private lateinit var fakeWakeLock: WakeLockFake
@@ -115,8 +112,7 @@ class ChipbarCoordinatorTest : SysuiTestCase() {
ChipbarCoordinator(
context,
logger,
- ViewCaptureAwareWindowManager(windowManager, lazyViewCapture,
- isViewCaptureEnabled = false),
+ windowManager,
fakeExecutor,
accessibilityManager,
configurationController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/WindowManagerProviderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/utils/WindowManagerProviderImplTest.kt
new file mode 100644
index 000000000000..7b52237f0a01
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/WindowManagerProviderImplTest.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.utils
+
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.view.WindowManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.utils.windowmanager.WindowManagerProviderImpl
+import com.google.common.truth.Truth.assertThat
+import org.junit.runner.RunWith
+import org.junit.Test
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class WindowManagerProviderImplTest : SysuiTestCase() {
+
+ private val windowManagerProvider = WindowManagerProviderImpl()
+ private val windowManagerFromSystemService = mContext.getSystemService(WindowManager::class.java)
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_VIEW_CAPTURE_TRACING)
+ fun viewCaptureTracingEnabled_verifyWMInstanceDoesNotMatchContextOne() {
+ val windowManagerFromProvider = windowManagerProvider.getWindowManager(mContext)
+ assertThat(windowManagerFromProvider).isNotEqualTo(windowManagerFromSystemService)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_VIEW_CAPTURE_TRACING)
+ fun viewCaptureTracingDisabled_verifyWMInstanceMatchesContextOne() {
+ mContext.addMockSystemService(WindowManager::class.java, windowManagerFromSystemService)
+
+ val windowManagerFromProvider = windowManagerProvider.getWindowManager(mContext)
+ assertThat(windowManagerFromProvider).isEqualTo(windowManagerFromSystemService)
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 0d7ce5353cd4..f89571f2db2d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -101,8 +101,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
-import com.android.app.viewcapture.ViewCapture;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.protolog.ProtoLog;
@@ -358,8 +356,6 @@ public class BubblesTest extends SysuiTestCase {
@Mock
private Display mDefaultDisplay;
@Mock
- private Lazy<ViewCapture> mLazyViewCapture;
- @Mock
private SyncTransactionQueue mSyncQueue;
private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
@@ -429,8 +425,7 @@ public class BubblesTest extends SysuiTestCase {
mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(
mContext,
new FakeWindowRootViewComponent.Factory(mNotificationShadeWindowView),
- new ViewCaptureAwareWindowManager(mWindowManager, mLazyViewCapture,
- /* isViewCaptureEnabled= */ false),
+ mWindowManager,
mActivityManager,
mDozeParameters,
mStatusBarStateController,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/data/repository/FakePosturingRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/data/repository/FakePosturingRepository.kt
index 8a597a61ee78..47b1bf52d8e2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/data/repository/FakePosturingRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/data/repository/FakePosturingRepository.kt
@@ -16,18 +16,18 @@
package com.android.systemui.communal.posturing.data.repository
-import com.android.systemui.communal.posturing.shared.model.PosturedState
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
+import com.android.systemui.communal.posturing.data.model.PositionState
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.asSharedFlow
class FakePosturingRepository : PosturingRepository {
- private val _postured = MutableStateFlow<PosturedState>(PosturedState.Unknown)
+ private val _postured = MutableSharedFlow<PositionState>()
- override val posturedState: StateFlow<PosturedState> = _postured.asStateFlow()
+ override val positionState: Flow<PositionState> = _postured.asSharedFlow()
- fun setPosturedState(state: PosturedState) {
- _postured.value = state
+ suspend fun emitPositionState(state: PositionState) {
+ _postured.emit(state)
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorKosmos.kt
index 53c9c6440c69..792346ebce59 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorKosmos.kt
@@ -18,6 +18,27 @@ package com.android.systemui.communal.posturing.domain.interactor
import com.android.systemui.communal.posturing.data.repository.posturingRepository
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.advanceTimeBy
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.log.logcatLogBuffer
+import com.android.systemui.util.sensors.asyncSensorManager
+import com.android.systemui.util.time.systemClock
val Kosmos.posturingInteractor by
- Kosmos.Fixture<PosturingInteractor> { PosturingInteractor(repository = posturingRepository) }
+ Kosmos.Fixture<PosturingInteractor> {
+ PosturingInteractor(
+ repository = posturingRepository,
+ asyncSensorManager = asyncSensorManager,
+ applicationScope = applicationCoroutineScope,
+ bgDispatcher = testDispatcher,
+ logBuffer = logcatLogBuffer("PosturingInteractor"),
+ clock = systemClock,
+ )
+ }
+
+fun Kosmos.advanceTimeBySlidingWindowAndRun() {
+ advanceTimeBy(PosturingInteractor.SLIDING_WINDOW_DURATION)
+ runCurrent()
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
index 044332981bf8..ae28022e096e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
@@ -25,6 +25,7 @@ import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.kosmos.Kosmos.Fixture
import kotlin.coroutines.CoroutineContext
+import kotlin.time.Duration
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
@@ -32,6 +33,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runCurrent
import org.mockito.kotlin.verify
@@ -72,6 +74,8 @@ fun Kosmos.runTest(testBody: suspend Kosmos.() -> Unit) = let { kosmos ->
fun Kosmos.runCurrent() = testScope.runCurrent()
+fun Kosmos.advanceTimeBy(duration: Duration) = testScope.advanceTimeBy(duration)
+
fun <T> Kosmos.collectLastValue(flow: Flow<T>) = testScope.collectLastValue(flow)
fun <T> Kosmos.collectValues(flow: Flow<T>): FlowValue<List<T>> = testScope.collectValues(flow)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelKosmos.kt
index 2f3d3c3e0489..ba1c598a79d7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelKosmos.kt
@@ -17,6 +17,7 @@
package com.android.systemui.media.controls.ui.viewmodel
import android.content.applicationContext
+import com.android.internal.logging.InstanceId
import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testDispatcher
@@ -31,5 +32,9 @@ val Kosmos.mediaControlViewModel by
backgroundExecutor = fakeExecutor,
interactor = mediaControlInteractor,
logger = mediaUiEventLogger,
+ instanceId = InstanceId.fakeInstanceId(-1),
+ onAdded = {},
+ onRemoved = {},
+ onUpdated = {},
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStoreKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStoreKosmos.kt
index aae32cfaafa6..f83fcb32aafe 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStoreKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStoreKosmos.kt
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar.data.repository
-import android.view.WindowManager
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.systemui.display.data.repository.displayRepository
import com.android.systemui.display.data.repository.displayWindowPropertiesRepository
import com.android.systemui.kosmos.Kosmos
@@ -35,14 +33,6 @@ val Kosmos.privacyDotWindowControllerStoreImpl by
windowControllerFactory = { _, _, _, _ -> mock() },
displayWindowPropertiesRepository = displayWindowPropertiesRepository,
privacyDotViewControllerStore = privacyDotViewControllerStore,
- viewCaptureAwareWindowManagerFactory =
- object : ViewCaptureAwareWindowManager.Factory {
- override fun create(
- windowManager: WindowManager
- ): ViewCaptureAwareWindowManager {
- return mock()
- }
- },
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/PrivacyDotWindowControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/PrivacyDotWindowControllerKosmos.kt
index c73838708a7a..da4027e46783 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/PrivacyDotWindowControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/PrivacyDotWindowControllerKosmos.kt
@@ -17,8 +17,8 @@
package com.android.systemui.statusbar.events
import android.content.testableContext
+import android.view.fakeWindowManager
import android.view.layoutInflater
-import com.android.app.viewcapture.realCaptureAwareWindowManager
import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.decor.privacyDotDecorProviderFactory
import com.android.systemui.kosmos.Kosmos
@@ -28,7 +28,7 @@ var Kosmos.privacyDotWindowController by
PrivacyDotWindowController(
testableContext.displayId,
privacyDotViewController,
- realCaptureAwareWindowManager,
+ fakeWindowManager,
layoutInflater,
fakeExecutor,
privacyDotDecorProviderFactory,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt
index 165f85d2cbba..4af4e804ff10 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt
@@ -19,7 +19,7 @@ package com.android.systemui.statusbar.notification.data.model
import android.app.PendingIntent
import android.graphics.drawable.Icon
import com.android.systemui.statusbar.StatusBarIconView
-import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
import com.android.systemui.statusbar.notification.shared.CallType
import com.android.systemui.statusbar.notification.stack.BUCKET_UNKNOWN
@@ -49,7 +49,7 @@ fun activeNotificationModel(
contentIntent: PendingIntent? = null,
bucket: Int = BUCKET_UNKNOWN,
callType: CallType = CallType.None,
- promotedContent: PromotedNotificationContentModel? = null,
+ promotedContent: PromotedNotificationContentModels? = null,
) =
ActiveNotificationModel(
key = key,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelKosmos.kt
index 99323dbd7cce..ebe20af51c19 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelKosmos.kt
@@ -22,6 +22,7 @@ import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shared.notifications.domain.interactor.notificationSettingsInteractor
import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
import com.android.systemui.statusbar.notification.domain.interactor.seenNotificationsInteractor
+import com.android.systemui.window.domain.interactor.windowRootViewBlurInteractor
val Kosmos.footerViewModel by Fixture {
FooterViewModel(
@@ -29,6 +30,7 @@ val Kosmos.footerViewModel by Fixture {
notificationSettingsInteractor = notificationSettingsInteractor,
seenNotificationsInteractor = seenNotificationsInteractor,
shadeInteractor = shadeInteractor,
+ windowRootViewBlurInteractor = windowRootViewBlurInteractor,
)
}
val Kosmos.footerViewModelFactory: FooterViewModel.Factory by Fixture {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationContentExtractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationContentExtractor.kt
index 8fdf5dbf2aeb..aaa86aaaedc6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationContentExtractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationContentExtractor.kt
@@ -17,21 +17,23 @@
package com.android.systemui.statusbar.notification.promoted
import android.app.Notification
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.RedactionType
import com.android.systemui.statusbar.notification.collection.NotificationEntry
-import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels
import com.android.systemui.statusbar.notification.row.shared.ImageModelProvider
import org.junit.Assert
class FakePromotedNotificationContentExtractor : PromotedNotificationContentExtractor {
@JvmField
- val contentForEntry = mutableMapOf<NotificationEntry, PromotedNotificationContentModel?>()
+ val contentForEntry = mutableMapOf<NotificationEntry, PromotedNotificationContentModels?>()
@JvmField val extractCalls = mutableListOf<Pair<NotificationEntry, Notification.Builder>>()
override fun extractContent(
entry: NotificationEntry,
recoveredBuilder: Notification.Builder,
+ @RedactionType redactionType: Int,
imageModelProvider: ImageModelProvider,
- ): PromotedNotificationContentModel? {
+ ): PromotedNotificationContentModels? {
extractCalls.add(entry to recoveredBuilder)
if (contentForEntry.isEmpty()) {
@@ -44,7 +46,7 @@ class FakePromotedNotificationContentExtractor : PromotedNotificationContentExtr
}
}
- fun resetForEntry(entry: NotificationEntry, content: PromotedNotificationContentModel?) {
+ fun resetForEntry(entry: NotificationEntry, content: PromotedNotificationContentModels?) {
contentForEntry.clear()
contentForEntry[entry] = content
extractCalls.clear()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorKosmos.kt
index 2b3158da38f9..c4542c4e709b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorKosmos.kt
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.promoted
import android.app.Notification
import android.content.applicationContext
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.row.RowImageInflater
import com.android.systemui.statusbar.notification.row.shared.skeletonImageTransform
@@ -39,9 +40,10 @@ fun Kosmos.setPromotedContent(entry: NotificationEntry) {
promotedNotificationContentExtractor.extractContent(
entry,
Notification.Builder.recoverBuilder(applicationContext, entry.sbn.notification),
+ REDACTION_TYPE_NONE,
RowImageInflater.newInstance(previousIndex = null, reinflating = false)
.useForContentModel(),
)
- entry.promotedNotificationContentModel =
+ entry.promotedNotificationContentModels =
requireNotNull(extractedContent) { "extractContent returned null" }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentBuilder.kt
new file mode 100644
index 000000000000..6916d560a7ad
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentBuilder.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.promoted.shared.model
+
+class PromotedNotificationContentBuilder(val key: String) {
+ private val sharedBuilder = PromotedNotificationContentModel.Builder(key)
+
+ fun applyToShared(
+ block: PromotedNotificationContentModel.Builder.() -> Unit
+ ): PromotedNotificationContentBuilder {
+ sharedBuilder.apply(block)
+ return this
+ }
+
+ fun build(): PromotedNotificationContentModels {
+ val sharedModel = sharedBuilder.build()
+ return PromotedNotificationContentModels(sharedModel, sharedModel)
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallTestHelper.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallTestHelper.kt
index 3e96fd7c729f..e5e1a830231e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallTestHelper.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallTestHelper.kt
@@ -26,7 +26,7 @@ import com.android.systemui.statusbar.notification.data.model.activeNotification
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.promoted.shared.model.PromotedNotificationContentModels
import com.android.systemui.statusbar.notification.shared.CallType
import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
@@ -39,7 +39,7 @@ fun inCallModel(
intent: PendingIntent? = null,
notificationKey: String = "test",
appName: String = "",
- promotedContent: PromotedNotificationContentModel? = null,
+ promotedContent: PromotedNotificationContentModels? = null,
isAppVisible: Boolean = false,
) =
OngoingCallModel.InCall(
@@ -77,7 +77,7 @@ object OngoingCallTestHelper {
key: String = "notif",
startTimeMs: Long = 1000L,
statusBarChipIconView: StatusBarIconView? = createStatusBarIconViewOrNull(),
- promotedContent: PromotedNotificationContentModel? = null,
+ promotedContent: PromotedNotificationContentModels? = null,
contentIntent: PendingIntent? = null,
uid: Int = DEFAULT_UID,
appName: String = "Fake name",
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerFactory.kt
index 3a19547f0713..f20fb3bf1779 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerFactory.kt
@@ -17,14 +17,14 @@
package com.android.systemui.statusbar.window
import android.content.Context
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
+import android.view.WindowManager
import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController
import com.android.systemui.statusbar.layout.StatusBarContentInsetsProvider
class FakeStatusBarWindowControllerFactory : StatusBarWindowController.Factory {
override fun create(
context: Context,
- viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
+ windowManager: WindowManager,
statusBarConfigurationController: StatusBarConfigurationController,
contentInsetsProvider: StatusBarContentInsetsProvider,
) = FakeStatusBarWindowController()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt
index f595aef41e2d..b0214769362f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt
@@ -17,8 +17,8 @@
package com.android.systemui.statusbar.window
import android.content.testableContext
+import android.view.fakeWindowManager
import android.view.windowManagerService
-import com.android.app.viewcapture.realCaptureAwareWindowManager
import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.fragments.fragmentService
import com.android.systemui.kosmos.Kosmos
@@ -33,7 +33,7 @@ val Kosmos.statusBarWindowControllerImpl by
StatusBarWindowControllerImpl(
testableContext,
statusBarWindowViewInflater,
- realCaptureAwareWindowManager,
+ fakeWindowManager,
statusBarConfigurationController,
windowManagerService,
statusBarContentInsetsProvider,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStoreKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStoreKosmos.kt
index 4941ceb7991d..0f9310376b2a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStoreKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStoreKosmos.kt
@@ -16,9 +16,6 @@
package com.android.systemui.statusbar.window
-import android.view.WindowManager
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
-import com.android.app.viewcapture.realCaptureAwareWindowManager
import com.android.systemui.display.data.repository.displayRepository
import com.android.systemui.display.data.repository.displayWindowPropertiesRepository
import com.android.systemui.kosmos.Kosmos
@@ -33,14 +30,6 @@ val Kosmos.multiDisplayStatusBarWindowControllerStore by
backgroundApplicationScope = applicationCoroutineScope,
controllerFactory = { _, _, _, _ -> mock() },
displayWindowPropertiesRepository = displayWindowPropertiesRepository,
- viewCaptureAwareWindowManagerFactory =
- object : ViewCaptureAwareWindowManager.Factory {
- override fun create(
- windowManager: WindowManager
- ): ViewCaptureAwareWindowManager {
- return realCaptureAwareWindowManager
- }
- },
statusBarConfigurationControllerStore = statusBarConfigurationControllerStore,
statusBarContentInsetsProviderStore = statusBarContentInsetsProviderStore,
displayRepository = displayRepository,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/windowmanager/FakeWindowManagerProvider.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/windowmanager/FakeWindowManagerProvider.kt
new file mode 100644
index 000000000000..5c8eae3183c7
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/windowmanager/FakeWindowManagerProvider.kt
@@ -0,0 +1,28 @@
+/*
+ * 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.utils.windowmanager
+
+import android.content.Context
+import android.view.WindowManager
+
+/** Fake implementation of [WindowManagerProvider], to be used in tests only. */
+class FakeWindowManagerProvider(private val windowManager: WindowManager) : WindowManagerProvider {
+
+ override fun getWindowManager(context: Context): WindowManager {
+ return windowManager
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/utils/src/com/android/app/viewcapture/ViewCaptureAwareWindowManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCsdWarningInteractorKosmos.kt
index 021c7bbb44cd..fc7fc0f4e5b4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/app/viewcapture/ViewCaptureAwareWindowManagerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCsdWarningInteractorKosmos.kt
@@ -14,23 +14,13 @@
* limitations under the License.
*/
-package com.android.app.viewcapture
+package com.android.systemui.volume.dialog.domain.interactor
-import android.view.fakeWindowManager
import com.android.systemui.kosmos.Kosmos
-import org.mockito.kotlin.mock
+import com.android.systemui.volume.dialog.shared.model.CsdWarningConfigModel
-val Kosmos.mockViewCaptureAwareWindowManager by
- Kosmos.Fixture { mock<ViewCaptureAwareWindowManager>() }
+val Kosmos.volumeDialogCsdWarningInteractor: VolumeDialogCsdWarningInteractor by
+ Kosmos.Fixture { VolumeDialogCsdWarningInteractor(volumeDialogStateInteractor) }
-val Kosmos.realCaptureAwareWindowManager by
- Kosmos.Fixture {
- ViewCaptureAwareWindowManager(
- fakeWindowManager,
- lazyViewCapture = lazy { mock<ViewCapture>() },
- isViewCaptureEnabled = false,
- )
- }
-
-var Kosmos.viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager by
- Kosmos.Fixture { mockViewCaptureAwareWindowManager }
+val Kosmos.csdWarningConfigModel: CsdWarningConfigModel by
+ Kosmos.Fixture { CsdWarningConfigModel(emptyList()) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModelKosmos.kt
new file mode 100644
index 000000000000..5a58599eaade
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModelKosmos.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.ui.viewmodel
+
+import com.android.internal.logging.uiEventLogger
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.backgroundScope
+import com.android.systemui.volume.dialog.domain.interactor.csdWarningConfigModel
+import com.android.systemui.volume.dialog.domain.interactor.volumeDialogCsdWarningInteractor
+import com.android.systemui.volume.dialog.domain.interactor.volumeDialogSafetyWarningInteractor
+import com.android.systemui.volume.dialog.domain.interactor.volumeDialogVisibilityInteractor
+import com.android.systemui.volume.dialog.shared.volumeDialogLogger
+import com.android.systemui.volume.dialog.volumeDialog
+
+val Kosmos.volumeDialogPluginViewModel: VolumeDialogPluginViewModel by
+ Kosmos.Fixture {
+ VolumeDialogPluginViewModel(
+ backgroundScope,
+ volumeDialogVisibilityInteractor,
+ volumeDialogSafetyWarningInteractor,
+ volumeDialogCsdWarningInteractor,
+ { volumeDialog },
+ volumeDialogLogger,
+ csdWarningConfigModel,
+ uiEventLogger,
+ )
+ }
diff --git a/packages/SystemUI/utils/Android.bp b/packages/SystemUI/utils/Android.bp
index 1efb11b436ff..8b63c07b270f 100644
--- a/packages/SystemUI/utils/Android.bp
+++ b/packages/SystemUI/utils/Android.bp
@@ -26,6 +26,8 @@ java_library {
"src/**/*.kt",
],
static_libs: [
+ "//frameworks/libs/systemui:view_capture",
+ "com_android_systemui_flags_lib",
"kotlin-stdlib",
"kotlinx_coroutines",
],
diff --git a/packages/SystemUI/utils/src/com/android/systemui/utils/windowmanager/WindowManagerProvider.kt b/packages/SystemUI/utils/src/com/android/systemui/utils/windowmanager/WindowManagerProvider.kt
new file mode 100644
index 000000000000..4e6eacbc8808
--- /dev/null
+++ b/packages/SystemUI/utils/src/com/android/systemui/utils/windowmanager/WindowManagerProvider.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.utils.windowmanager
+
+import android.content.Context
+import android.view.WindowManager
+
+/**
+ * Provider for [WindowManager] in SystemUI.
+ *
+ * Use this class over [WindowManagerUtils] in cases where
+ * a [WindowManager] is needed for a context created inside the class. [WindowManagerUtils] should
+ * only be used in a class where the [WindowManager] is needed for a custom context inside the
+ * class, and the class is not part of the dagger graph. Example usage:
+ * ```kotlin
+ * class Sample {
+ * private final WindowManager mWindowManager;
+ *
+ * @Inject
+ * public Sample(WindowManagerProvider windowManagerProvider) {
+ * Context context = getCustomContext();
+ * mWindowManager = windowManagerProvider.getWindowManager(context);
+ * }
+ * // use mWindowManager
+ * }
+ *
+ * class SampleTest {
+ *
+ * @Mock
+ * WindowManager mWindowManager;
+ *
+ * FakeWindowManagerProvider fakeProvider = new FakeWindowManagerProvider(mWindowManager);
+ *
+ * // define the behaviour of mWindowManager to get required WindowManager instance in tests.
+ * }
+ * ```
+ */
+interface WindowManagerProvider {
+
+ /** Method to return the required [WindowManager]. */
+ fun getWindowManager(context: Context): WindowManager
+}
diff --git a/packages/SystemUI/utils/src/com/android/systemui/utils/windowmanager/WindowManagerProviderImpl.kt b/packages/SystemUI/utils/src/com/android/systemui/utils/windowmanager/WindowManagerProviderImpl.kt
new file mode 100644
index 000000000000..5e965ed47403
--- /dev/null
+++ b/packages/SystemUI/utils/src/com/android/systemui/utils/windowmanager/WindowManagerProviderImpl.kt
@@ -0,0 +1,28 @@
+/*
+ * 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.utils.windowmanager
+
+import android.content.Context
+import android.view.WindowManager
+
+/** Implementation of [WindowManagerProvider]. */
+class WindowManagerProviderImpl : WindowManagerProvider {
+
+ override fun getWindowManager(context: Context): WindowManager {
+ return WindowManagerUtils.getWindowManager(context)
+ }
+}
diff --git a/packages/SystemUI/utils/src/com/android/systemui/utils/windowmanager/WindowManagerUtils.kt b/packages/SystemUI/utils/src/com/android/systemui/utils/windowmanager/WindowManagerUtils.kt
new file mode 100644
index 000000000000..643e93422294
--- /dev/null
+++ b/packages/SystemUI/utils/src/com/android/systemui/utils/windowmanager/WindowManagerUtils.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.utils.windowmanager
+
+import android.content.Context
+import android.view.WindowManager
+import com.android.app.viewcapture.ViewCaptureAwareWindowManagerFactory
+import com.android.systemui.Flags.enableViewCaptureTracing
+
+/**
+ * Provides [WindowManager] in SystemUI. Use [WindowManagerProvider] unless [WindowManager] instance
+ * needs to be created in a class that is not part of the dagger dependency graph.
+ */
+object WindowManagerUtils {
+
+ /** Method to return the required [WindowManager]. */
+ @JvmStatic
+ fun getWindowManager(context: Context): WindowManager {
+ return if (!enableViewCaptureTracing()) {
+ context.getSystemService(WindowManager::class.java)
+ } else {
+ /**
+ * We use this token to supply windowContextToken to [WindowManager] for
+ * [WindowContext].
+ */
+ val windowContextToken = context.windowContextToken
+
+ ViewCaptureAwareWindowManagerFactory.getInstance(
+ context,
+ parent = null,
+ windowContextToken,
+ )
+ }
+ }
+}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index decac40d20f8..cf85dd957b3f 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -258,7 +258,6 @@ java_library_static {
"dreams_flags_lib",
"aconfig_new_storage_flags_lib",
"powerstats_flags_lib",
- "locksettings_flags_lib",
"MmdProperties",
"mmd_flags_lib",
"profiling_flags_lib",
diff --git a/services/core/java/com/android/server/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java
index d12a0a2a1e00..c338a1ef15c9 100644
--- a/services/core/java/com/android/server/adb/AdbService.java
+++ b/services/core/java/com/android/server/adb/AdbService.java
@@ -32,6 +32,7 @@ import android.debug.IAdbTransport;
import android.debug.PairDevice;
import android.hardware.usb.UsbManager;
import android.net.Uri;
+import android.net.wifi.WifiManager;
import android.os.Binder;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
@@ -192,6 +193,7 @@ public class AdbService extends IAdbManager.Stub {
@Override
public void onChange(boolean selfChange, @NonNull Uri uri, @UserIdInt int userId) {
+ Slog.d("AdbSettingsObserver", "onChange " + uri.toString());
if (mAdbUsbUri.equals(uri)) {
boolean shouldEnable = (Settings.Global.getInt(mContentResolver,
Settings.Global.ADB_ENABLED, 0) > 0);
@@ -417,6 +419,28 @@ public class AdbService extends IAdbManager.Stub {
}
}
+ private WifiManager.MulticastLock mAdbMulticastLock = null;
+
+ private void acquireMulticastLock() {
+ if (mAdbMulticastLock == null) {
+ WifiManager wifiManager = (WifiManager)
+ mContext.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
+ mAdbMulticastLock = wifiManager.createMulticastLock("AdbMulticastLock");
+ }
+
+ if (!mAdbMulticastLock.isHeld()) {
+ mAdbMulticastLock.acquire();
+ Slog.d(TAG, "Acquired multicast lock");
+ }
+ }
+
+ private void releaseMulticastLock() {
+ if (mAdbMulticastLock != null && mAdbMulticastLock.isHeld()) {
+ mAdbMulticastLock.release();
+ Slog.d(TAG, "Released multicast lock");
+ }
+ }
+
private void setAdbEnabled(boolean enable, byte transportType) {
Slog.d(TAG, "setAdbEnabled(" + enable + "), mIsAdbUsbEnabled=" + mIsAdbUsbEnabled
+ ", mIsAdbWifiEnabled=" + mIsAdbWifiEnabled + ", transportType=" + transportType);
@@ -428,9 +452,11 @@ public class AdbService extends IAdbManager.Stub {
if (mIsAdbWifiEnabled) {
// Start adb over WiFi.
SystemProperties.set(WIFI_PERSISTENT_CONFIG_PROPERTY, "1");
+ acquireMulticastLock();
} else {
// Stop adb over WiFi.
SystemProperties.set(WIFI_PERSISTENT_CONFIG_PROPERTY, "0");
+ releaseMulticastLock();
}
} else {
// No change
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 6b3661a2a004..797ee002affb 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -186,6 +186,7 @@ import android.media.audiopolicy.AudioPolicyConfig;
import android.media.audiopolicy.AudioProductStrategy;
import android.media.audiopolicy.AudioVolumeGroup;
import android.media.audiopolicy.IAudioPolicyCallback;
+import android.media.audiopolicy.IAudioVolumeChangeDispatcher;
import android.media.permission.ClearCallingIdentityContext;
import android.media.permission.SafeCloseable;
import android.media.projection.IMediaProjection;
@@ -1388,6 +1389,7 @@ public class AudioService extends IAudioService.Stub
mUseVolumeGroupAliases = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_handleVolumeAliasesUsingVolumeGroups);
+ mAudioVolumeChangeHandler = new AudioVolumeChangeHandler(mAudioSystem);
// Initialize volume
// Priority 1 - Android Property
// Priority 2 - Audio Policy Service
@@ -4188,7 +4190,13 @@ public class AudioService extends IAudioService.Stub
Log.d(TAG, "adjustStreamVolume postSetHearingAidVolumeIndex index="
+ newIndex + " stream=" + streamType);
}
- mDeviceBroker.postSetHearingAidVolumeIndex(newIndex, streamType);
+ int haIndex;
+ final VolumeStreamState vss = getVssForStreamOrDefault(streamType);
+ synchronized (mVolumeStateLock) {
+ haIndex = (int) (vss.getMinIndex() + (newIndex - vss.getMinIndex())
+ / vss.getIndexStepFactor());
+ }
+ mDeviceBroker.postSetHearingAidVolumeIndex(haIndex, streamType);
}
}
@@ -4452,6 +4460,27 @@ public class AudioService extends IAudioService.Stub
}
}
+ //================================
+ // Audio Volume Change Dispatcher
+ //================================
+ private final AudioVolumeChangeHandler mAudioVolumeChangeHandler;
+
+ /** @see AudioManager#registerVolumeGroupCallback(executor, callback) */
+ @android.annotation.EnforcePermission(
+ android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+ public void registerAudioVolumeCallback(IAudioVolumeChangeDispatcher callback) {
+ super.registerAudioVolumeCallback_enforcePermission();
+ mAudioVolumeChangeHandler.registerListener(callback);
+ }
+
+ /** @see AudioManager#unregisterVolumeGroupCallback(callback) */
+ @android.annotation.EnforcePermission(
+ android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+ public void unregisterAudioVolumeCallback(IAudioVolumeChangeDispatcher callback) {
+ super.unregisterAudioVolumeCallback_enforcePermission();
+ mAudioVolumeChangeHandler.unregisterListener(callback);
+ }
+
@Override
@android.annotation.EnforcePermission(anyOf = {
MODIFY_AUDIO_SETTINGS_PRIVILEGED, MODIFY_AUDIO_ROUTING })
@@ -5126,7 +5155,13 @@ public class AudioService extends IAudioService.Stub
mDeviceBroker.postSetLeAudioVolumeIndex(index * 10,
getVssForStreamOrDefault(streamType).getMaxIndex(), streamType);
} else if (device == AudioSystem.DEVICE_OUT_HEARING_AID) {
- mDeviceBroker.postSetHearingAidVolumeIndex(index * 10, streamType);
+ int haIndex = index * 10;
+ final VolumeStreamState vss = getVssForStreamOrDefault(streamType);
+ synchronized (mVolumeStateLock) {
+ haIndex = (int) (vss.getMinIndex()
+ + (haIndex - vss.getMinIndex()) / vss.getIndexStepFactor());
+ }
+ mDeviceBroker.postSetHearingAidVolumeIndex(haIndex, streamType);
} else if (AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)) {
mDeviceBroker.postSetAvrcpAbsoluteVolumeIndex(index);
} else {
@@ -5257,7 +5292,13 @@ public class AudioService extends IAudioService.Stub
&& streamType == getBluetoothContextualVolumeStream()) {
Log.i(TAG, "setStreamVolume postSetHearingAidVolumeIndex index=" + index
+ " stream=" + streamType);
- mDeviceBroker.postSetHearingAidVolumeIndex(index, streamType);
+ int haIndex;
+ final VolumeStreamState vss = getVssForStreamOrDefault(streamType);
+ synchronized (mVolumeStateLock) {
+ haIndex = (int) (vss.getMinIndex()
+ + (index - vss.getMinIndex()) / vss.getIndexStepFactor());
+ }
+ mDeviceBroker.postSetHearingAidVolumeIndex(haIndex, streamType);
}
synchronized (mHdmiClientLock) {
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index a6267c156fb3..ced5faeeff27 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -23,6 +23,7 @@ import android.media.AudioDeviceAttributes;
import android.media.AudioMixerAttributes;
import android.media.AudioSystem;
import android.media.IDevicesForAttributesCallback;
+import android.media.INativeAudioVolumeGroupCallback;
import android.media.ISoundDose;
import android.media.ISoundDoseCallback;
import android.media.audiopolicy.AudioMix;
@@ -758,6 +759,29 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback,
}
/**
+ * Same as {@link AudioSystem#registerAudioVolumeGroupCallback(INativeAudioVolumeGroupCallback)}
+ * @param callback to register
+ * @return {@link #SUCCESS} if successfully registered.
+ *
+ * @hide
+ */
+ public int registerAudioVolumeGroupCallback(INativeAudioVolumeGroupCallback callback) {
+ return AudioSystem.registerAudioVolumeGroupCallback(callback);
+ }
+
+ /**
+ * Same as
+ * {@link AudioSystem#unregisterAudioVolumeGroupCallback(INativeAudioVolumeGroupCallback)}.
+ * @param callback to register
+ * @return {@link #SUCCESS} if successfully registered.
+ *
+ * @hide
+ */
+ public int unregisterAudioVolumeGroupCallback(INativeAudioVolumeGroupCallback callback) {
+ return AudioSystem.unregisterAudioVolumeGroupCallback(callback);
+ }
+
+ /**
* Part of AudioService dump
* @param pw
*/
diff --git a/services/core/java/com/android/server/audio/AudioVolumeChangeHandler.java b/services/core/java/com/android/server/audio/AudioVolumeChangeHandler.java
new file mode 100644
index 000000000000..2bb4301bb8fd
--- /dev/null
+++ b/services/core/java/com/android/server/audio/AudioVolumeChangeHandler.java
@@ -0,0 +1,103 @@
+/*
+ * 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.server.audio;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.media.INativeAudioVolumeGroupCallback;
+import android.media.audio.common.AudioVolumeGroupChangeEvent;
+import android.media.audiopolicy.IAudioVolumeChangeDispatcher;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
+
+/**
+ * The AudioVolumeChangeHandler handles AudioVolume callbacks invoked by native
+ * {@link INativeAudioVolumeGroupCallback} callback.
+ */
+/* private package */ class AudioVolumeChangeHandler {
+ private static final String TAG = "AudioVolumeChangeHandler";
+
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
+ private final RemoteCallbackList<IAudioVolumeChangeDispatcher> mListeners =
+ new RemoteCallbackList<>();
+ private final @NonNull AudioSystemAdapter mAudioSystem;
+ private @Nullable AudioVolumeGroupCallback mAudioVolumeGroupCallback;
+
+ AudioVolumeChangeHandler(@NonNull AudioSystemAdapter asa) {
+ mAudioSystem = asa;
+ }
+
+ @GuardedBy("mLock")
+ private void lazyInitLocked() {
+ mAudioVolumeGroupCallback = new AudioVolumeGroupCallback();
+ mAudioSystem.registerAudioVolumeGroupCallback(mAudioVolumeGroupCallback);
+ }
+
+ private void sendAudioVolumeGroupChangedToClients(int groupId, int index) {
+ RemoteCallbackList<IAudioVolumeChangeDispatcher> listeners;
+ int nbDispatchers;
+ synchronized (mLock) {
+ listeners = mListeners;
+ nbDispatchers = mListeners.beginBroadcast();
+ }
+ for (int i = 0; i < nbDispatchers; i++) {
+ try {
+ listeners.getBroadcastItem(i).onAudioVolumeGroupChanged(groupId, index);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to broadcast Volume Changed event");
+ }
+ }
+ synchronized (mLock) {
+ mListeners.finishBroadcast();
+ }
+ }
+
+ /**
+ * @param cb the {@link IAudioVolumeChangeDispatcher} to register
+ */
+ public void registerListener(@NonNull IAudioVolumeChangeDispatcher cb) {
+ Preconditions.checkNotNull(cb, "Volume group callback must not be null");
+ synchronized (mLock) {
+ if (mAudioVolumeGroupCallback == null) {
+ lazyInitLocked();
+ }
+ mListeners.register(cb);
+ }
+ }
+
+ /**
+ * @param cb the {@link IAudioVolumeChangeDispatcher} to unregister
+ */
+ public void unregisterListener(@NonNull IAudioVolumeChangeDispatcher cb) {
+ Preconditions.checkNotNull(cb, "Volume group callback must not be null");
+ synchronized (mLock) {
+ mListeners.unregister(cb);
+ }
+ }
+
+ private final class AudioVolumeGroupCallback extends INativeAudioVolumeGroupCallback.Stub {
+ public void onAudioVolumeGroupChanged(AudioVolumeGroupChangeEvent volumeEvent) {
+ Slog.v(TAG, "onAudioVolumeGroupChanged volumeEvent=" + volumeEvent);
+ sendAudioVolumeGroupChangedToClients(volumeEvent.groupId, volumeEvent.flags);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 29e04e744759..6af55300d0b3 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -25,6 +25,7 @@ import static android.view.KeyEvent.KEYCODE_UNKNOWN;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static com.android.hardware.input.Flags.enableCustomizableInputGestures;
+import static com.android.hardware.input.Flags.fixSearchModifierFallbacks;
import static com.android.hardware.input.Flags.keyEventActivityDetection;
import static com.android.hardware.input.Flags.touchpadVisualizer;
import static com.android.hardware.input.Flags.useKeyGestureEventHandler;
@@ -750,7 +751,8 @@ public class InputManagerService extends IInputManager.Stub
* @return True if the lookup was successful, false otherwise.
*/
@Override // Binder call
- public boolean hasKeys(int deviceId, int sourceMask, int[] keyCodes, boolean[] keyExists) {
+ public boolean hasKeys(int deviceId, int sourceMask, @NonNull int[] keyCodes,
+ @NonNull boolean[] keyExists) {
Objects.requireNonNull(keyCodes, "keyCodes must not be null");
Objects.requireNonNull(keyExists, "keyExists must not be null");
if (keyExists.length < keyCodes.length) {
@@ -791,7 +793,7 @@ public class InputManagerService extends IInputManager.Stub
* @deprecated Use {@link #transferTouchGesture(IBinder, IBinder)}
*/
@Deprecated
- public boolean transferTouch(IBinder destChannelToken, int displayId) {
+ public boolean transferTouch(@NonNull IBinder destChannelToken, int displayId) {
// TODO(b/162194035): Replace this with a SPY window
Objects.requireNonNull(destChannelToken, "destChannelToken must not be null");
return mNative.transferTouch(destChannelToken, displayId);
@@ -803,7 +805,7 @@ public class InputManagerService extends IInputManager.Stub
* @param displayId Target display id.
* @return The input channel.
*/
- public InputChannel monitorInput(String inputChannelName, int displayId) {
+ public InputChannel monitorInput(@NonNull String inputChannelName, int displayId) {
Objects.requireNonNull(inputChannelName, "inputChannelName not be null");
if (displayId < Display.DEFAULT_DISPLAY) {
@@ -835,7 +837,7 @@ public class InputManagerService extends IInputManager.Stub
return outInputChannel;
}
- private void removeSpyWindowGestureMonitor(IBinder inputChannelToken) {
+ private void removeSpyWindowGestureMonitor(@NonNull IBinder inputChannelToken) {
final GestureMonitorSpyWindow monitor;
synchronized (mInputMonitors) {
monitor = mInputMonitors.remove(inputChannelToken);
@@ -854,8 +856,8 @@ public class InputManagerService extends IInputManager.Stub
* @return The input channel.
*/
@Override // Binder call
- public InputMonitor monitorGestureInput(IBinder monitorToken, @NonNull String requestedName,
- int displayId) {
+ public InputMonitor monitorGestureInput(@NonNull IBinder monitorToken,
+ @NonNull String requestedName, int displayId) {
if (!checkCallingPermission(android.Manifest.permission.MONITOR_INPUT,
"monitorGestureInput()")) {
throw new SecurityException("Requires MONITOR_INPUT permission");
@@ -902,7 +904,7 @@ public class InputManagerService extends IInputManager.Stub
* Removes an input channel.
* @param connectionToken The input channel to unregister.
*/
- public void removeInputChannel(IBinder connectionToken) {
+ public void removeInputChannel(@NonNull IBinder connectionToken) {
Objects.requireNonNull(connectionToken, "connectionToken must not be null");
mNative.removeInputChannel(connectionToken);
}
@@ -977,12 +979,12 @@ public class InputManagerService extends IInputManager.Stub
}
@Override // Binder call
- public boolean injectInputEvent(InputEvent event, int mode) {
+ public boolean injectInputEvent(@NonNull InputEvent event, int mode) {
return injectInputEventToTarget(event, mode, Process.INVALID_UID);
}
@Override // Binder call
- public boolean injectInputEventToTarget(InputEvent event, int mode, int targetUid) {
+ public boolean injectInputEventToTarget(@NonNull InputEvent event, int mode, int targetUid) {
if (!checkCallingPermission(android.Manifest.permission.INJECT_EVENTS,
"injectInputEvent()", true /*checkInstrumentationSource*/)) {
throw new SecurityException(
@@ -1032,7 +1034,7 @@ public class InputManagerService extends IInputManager.Stub
}
@Override // Binder call
- public VerifiedInputEvent verifyInputEvent(InputEvent event) {
+ public VerifiedInputEvent verifyInputEvent(@NonNull InputEvent event) {
Objects.requireNonNull(event, "event must not be null");
return mNative.verifyInputEvent(event);
}
@@ -1106,7 +1108,8 @@ public class InputManagerService extends IInputManager.Stub
}
@Override // Binder call
- public void registerInputDevicesChangedListener(IInputDevicesChangedListener listener) {
+ public void registerInputDevicesChangedListener(
+ @NonNull IInputDevicesChangedListener listener) {
Objects.requireNonNull(listener, "listener must not be null");
synchronized (mInputDevicesLock) {
@@ -1176,7 +1179,7 @@ public class InputManagerService extends IInputManager.Stub
}
@Override // Binder call & native callback
- public TouchCalibration getTouchCalibrationForInputDevice(String inputDeviceDescriptor,
+ public TouchCalibration getTouchCalibrationForInputDevice(@NonNull String inputDeviceDescriptor,
int surfaceRotation) {
Objects.requireNonNull(inputDeviceDescriptor, "inputDeviceDescriptor must not be null");
@@ -1186,8 +1189,8 @@ public class InputManagerService extends IInputManager.Stub
}
@Override // Binder call
- public void setTouchCalibrationForInputDevice(String inputDeviceDescriptor, int surfaceRotation,
- TouchCalibration calibration) {
+ public void setTouchCalibrationForInputDevice(@NonNull String inputDeviceDescriptor,
+ int surfaceRotation, @NonNull TouchCalibration calibration) {
if (!checkCallingPermission(android.Manifest.permission.SET_INPUT_CALIBRATION,
"setTouchCalibrationForInputDevice()")) {
throw new SecurityException("Requires SET_INPUT_CALIBRATION permission");
@@ -1225,7 +1228,7 @@ public class InputManagerService extends IInputManager.Stub
}
@Override // Binder call
- public void registerTabletModeChangedListener(ITabletModeChangedListener listener) {
+ public void registerTabletModeChangedListener(@NonNull ITabletModeChangedListener listener) {
if (!checkCallingPermission(android.Manifest.permission.TABLET_MODE,
"registerTabletModeChangedListener()")) {
throw new SecurityException("Requires TABLET_MODE_LISTENER permission");
@@ -1341,7 +1344,7 @@ public class InputManagerService extends IInputManager.Stub
}
@Override
- public void requestPointerCapture(IBinder inputChannelToken, boolean enabled) {
+ public void requestPointerCapture(@NonNull IBinder inputChannelToken, boolean enabled) {
Objects.requireNonNull(inputChannelToken, "inputChannelToken must not be null");
mNative.requestPointerCapture(inputChannelToken, enabled);
@@ -1664,7 +1667,8 @@ public class InputManagerService extends IInputManager.Stub
}
@Override // Binder call
- public boolean registerVibratorStateListener(int deviceId, IVibratorStateListener listener) {
+ public boolean registerVibratorStateListener(int deviceId,
+ @NonNull IVibratorStateListener listener) {
Objects.requireNonNull(listener, "listener must not be null");
RemoteCallbackList<IVibratorStateListener> listeners;
@@ -1717,8 +1721,8 @@ public class InputManagerService extends IInputManager.Stub
// Binder call
@Override
- public boolean setPointerIcon(PointerIcon icon, int displayId, int deviceId, int pointerId,
- IBinder inputToken) {
+ public boolean setPointerIcon(@NonNull PointerIcon icon, int displayId, int deviceId,
+ int pointerId, IBinder inputToken) {
Objects.requireNonNull(icon);
return mNative.setPointerIcon(icon, displayId, deviceId, pointerId, inputToken);
}
@@ -1896,7 +1900,7 @@ public class InputManagerService extends IInputManager.Stub
}
@Override // Binder call
- public boolean registerSensorListener(IInputSensorEventListener listener) {
+ public boolean registerSensorListener(@NonNull IInputSensorEventListener listener) {
if (DEBUG) {
Slog.d(TAG, "registerSensorListener: listener=" + listener + " callingPid="
+ Binder.getCallingPid());
@@ -1927,7 +1931,7 @@ public class InputManagerService extends IInputManager.Stub
}
@Override // Binder call
- public void unregisterSensorListener(IInputSensorEventListener listener) {
+ public void unregisterSensorListener(@NonNull IInputSensorEventListener listener) {
if (DEBUG) {
Slog.d(TAG, "unregisterSensorListener: listener=" + listener + " callingPid="
+ Binder.getCallingPid());
@@ -2016,7 +2020,8 @@ public class InputManagerService extends IInputManager.Stub
/**
* Set specified light state with for a specific input device.
*/
- private void setLightStateInternal(int deviceId, Light light, LightState lightState) {
+ private void setLightStateInternal(int deviceId, @NonNull Light light,
+ @NonNull LightState lightState) {
Objects.requireNonNull(light, "light does not exist");
if (DEBUG) {
Slog.d(TAG, "setLightStateInternal device " + deviceId + " light " + light
@@ -2079,7 +2084,7 @@ public class InputManagerService extends IInputManager.Stub
}
@Override
- public void openLightSession(int deviceId, String opPkg, IBinder token) {
+ public void openLightSession(int deviceId, String opPkg, @NonNull IBinder token) {
Objects.requireNonNull(token);
synchronized (mLightLock) {
Preconditions.checkState(mLightSessions.get(token) == null, "already registered");
@@ -2098,7 +2103,7 @@ public class InputManagerService extends IInputManager.Stub
}
@Override
- public void closeLightSession(int deviceId, IBinder token) {
+ public void closeLightSession(int deviceId, @NonNull IBinder token) {
Objects.requireNonNull(token);
synchronized (mLightLock) {
LightSession lightSession = mLightSessions.get(token);
@@ -2128,13 +2133,15 @@ public class InputManagerService extends IInputManager.Stub
}
@Override
- public void registerBatteryListener(int deviceId, IInputDeviceBatteryListener listener) {
+ public void registerBatteryListener(int deviceId,
+ @NonNull IInputDeviceBatteryListener listener) {
Objects.requireNonNull(listener);
mBatteryController.registerBatteryListener(deviceId, listener, Binder.getCallingPid());
}
@Override
- public void unregisterBatteryListener(int deviceId, IInputDeviceBatteryListener listener) {
+ public void unregisterBatteryListener(int deviceId,
+ @NonNull IInputDeviceBatteryListener listener) {
Objects.requireNonNull(listener);
mBatteryController.unregisterBatteryListener(deviceId, listener, Binder.getCallingPid());
}
@@ -2155,7 +2162,7 @@ public class InputManagerService extends IInputManager.Stub
@EnforcePermission(Manifest.permission.MONITOR_INPUT)
@Override
- public void pilferPointers(IBinder inputChannelToken) {
+ public void pilferPointers(@NonNull IBinder inputChannelToken) {
super.pilferPointers_enforcePermission();
Objects.requireNonNull(inputChannelToken);
@@ -2164,7 +2171,7 @@ public class InputManagerService extends IInputManager.Stub
@Override
@EnforcePermission(Manifest.permission.MONITOR_KEYBOARD_BACKLIGHT)
- public void registerKeyboardBacklightListener(IKeyboardBacklightListener listener) {
+ public void registerKeyboardBacklightListener(@NonNull IKeyboardBacklightListener listener) {
super.registerKeyboardBacklightListener_enforcePermission();
Objects.requireNonNull(listener);
mKeyboardBacklightController.registerKeyboardBacklightListener(listener,
@@ -2173,7 +2180,7 @@ public class InputManagerService extends IInputManager.Stub
@Override
@EnforcePermission(Manifest.permission.MONITOR_KEYBOARD_BACKLIGHT)
- public void unregisterKeyboardBacklightListener(IKeyboardBacklightListener listener) {
+ public void unregisterKeyboardBacklightListener(@NonNull IKeyboardBacklightListener listener) {
super.unregisterKeyboardBacklightListener_enforcePermission();
Objects.requireNonNull(listener);
mKeyboardBacklightController.unregisterKeyboardBacklightListener(listener,
@@ -2653,6 +2660,8 @@ public class InputManagerService extends IInputManager.Stub
@SuppressWarnings("unused")
@VisibleForTesting
long interceptKeyBeforeDispatching(IBinder focus, KeyEvent event, int policyFlags) {
+ final long keyNotConsumedGoFallback = -2;
+ final long keyConsumed = -1;
final long keyNotConsumed = 0;
long value = keyNotConsumed;
// TODO(b/358569822) Remove below once we have nicer API for listening to shortcuts
@@ -2667,6 +2676,16 @@ public class InputManagerService extends IInputManager.Stub
value = mWindowManagerCallbacks.interceptKeyBeforeDispatching(focus, event,
policyFlags);
}
+ if (fixSearchModifierFallbacks() && value == keyNotConsumed && event.isMetaPressed()) {
+ // If the key has not been consumed and includes the meta key, do not send the event
+ // to the app and attempt to generate a fallback.
+ final KeyCharacterMap kcm = event.getKeyCharacterMap();
+ final KeyCharacterMap.FallbackAction fallbackAction =
+ kcm.getFallbackAction(event.getKeyCode(), event.getMetaState());
+ if (fallbackAction != null) {
+ return keyNotConsumedGoFallback;
+ }
+ }
return value;
}
@@ -3310,9 +3329,10 @@ public class InputManagerService extends IInputManager.Stub
* @param token the window token that's about to receive this event
* @param event the key event that's being dispatched
* @param policyFlags the policy flags
- * @return negative value if the key should be skipped (not sent to the app). 0 if the key
- * should proceed getting dispatched to the app. positive value to indicate the additional
- * time delay, in nanoseconds, to wait before sending this key to the app.
+ * @return -1 if the key should be skipped (not sent to the app). -2 if the key should not
+ * be sent to the app, but it should still generate a fallback.
+ * 0 if the key should proceed getting dispatched to the app. positive value to indicate the
+ * additional time delay, in nanoseconds, to wait before sending this key to the app.
*/
long interceptKeyBeforeDispatching(IBinder token, KeyEvent event, int policyFlags);
@@ -3429,7 +3449,7 @@ public class InputManagerService extends IInputManager.Stub
}
@Override
- public void sendInputEvent(InputEvent event, int policyFlags) {
+ public void sendInputEvent(@NonNull InputEvent event, int policyFlags) {
if (!checkCallingPermission(android.Manifest.permission.INJECT_EVENTS,
"sendInputEvent()")) {
throw new SecurityException(
@@ -3451,9 +3471,9 @@ public class InputManagerService extends IInputManager.Stub
* Interface for the system to handle request from InputMonitors.
*/
private final class InputMonitorHost extends IInputMonitorHost.Stub {
- private final IBinder mInputChannelToken;
+ private final @NonNull IBinder mInputChannelToken;
- InputMonitorHost(IBinder inputChannelToken) {
+ InputMonitorHost(@NonNull IBinder inputChannelToken) {
mInputChannelToken = inputChannelToken;
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
index 41fd29da64f1..bbf7732c9596 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
@@ -184,8 +184,11 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
long expiryMillis = RELIABLE_MESSAGE_DUPLICATE_DETECTION_TIMEOUT.toMillis();
if (nowMillis >= nextEntry.getValue() + expiryMillis) {
iterator.remove();
+ } else {
+ // Safe to break since LinkedHashMap is insertion-ordered, so the next entry
+ // will have a later timestamp and will not be expired.
+ break;
}
- break;
}
return mRxMessageHistoryMap.containsKey(message.getMessageSequenceNumber());
diff --git a/services/core/java/com/android/server/locksettings/Android.bp b/services/core/java/com/android/server/locksettings/Android.bp
deleted file mode 100644
index 53f1ac668e49..000000000000
--- a/services/core/java/com/android/server/locksettings/Android.bp
+++ /dev/null
@@ -1,11 +0,0 @@
-aconfig_declarations {
- name: "locksettings_flags",
- package: "com.android.server.locksettings",
- container: "system",
- srcs: ["*.aconfig"],
-}
-
-java_aconfig_library {
- name: "locksettings_flags_lib",
- aconfig_declarations: "locksettings_flags",
-}
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
index 820c0efcc1cf..4b704d01c3b9 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
@@ -428,15 +428,11 @@ class RebootEscrowManager {
/** Wrapper function to set error code serialized through handler, */
private void setLoadEscrowDataErrorCode(@RebootEscrowErrorCode int value, Handler handler) {
- if (Flags.waitForInternetRor()) {
- mInjector.post(
- handler,
- () -> {
- mLoadEscrowDataErrorCode = value;
- });
- } else {
- mLoadEscrowDataErrorCode = value;
- }
+ mInjector.post(
+ handler,
+ () -> {
+ mLoadEscrowDataErrorCode = value;
+ });
}
/** Wrapper function to compare and set error code serialized through handler. */
@@ -511,23 +507,17 @@ class RebootEscrowManager {
mWakeLock.acquire(mInjector.getWakeLockTimeoutMillis());
}
- if (Flags.waitForInternetRor()) {
- // Timeout to stop retrying same as the wake lock timeout.
- mInjector.postDelayed(
- retryHandler,
- () -> {
- mRebootEscrowTimedOut = true;
- },
- mInjector.getLoadEscrowTimeoutMillis());
-
- mInjector.post(
- retryHandler,
- () -> loadRebootEscrowDataOnInternet(retryHandler, users, rebootEscrowUsers));
- return;
- }
+ // Timeout to stop retrying same as the wake lock timeout.
+ mInjector.postDelayed(
+ retryHandler,
+ () -> {
+ mRebootEscrowTimedOut = true;
+ },
+ mInjector.getLoadEscrowTimeoutMillis());
- mInjector.post(retryHandler, () -> loadRebootEscrowDataWithRetry(
- retryHandler, 0, users, rebootEscrowUsers));
+ mInjector.post(
+ retryHandler,
+ () -> loadRebootEscrowDataOnInternet(retryHandler, users, rebootEscrowUsers));
}
void scheduleLoadRebootEscrowDataOrFail(
@@ -548,27 +538,13 @@ class RebootEscrowManager {
return;
}
- if (Flags.waitForInternetRor()) {
- if (mRebootEscrowTimedOut) {
- Slog.w(TAG, "Failed to load reboot escrow data within timeout");
- compareAndSetLoadEscrowDataErrorCode(
- ERROR_NONE, ERROR_TIMEOUT_EXHAUSTED, retryHandler);
- } else {
- Slog.w(
- TAG,
- "Failed to load reboot escrow data after " + attemptNumber + " attempts");
- compareAndSetLoadEscrowDataErrorCode(
- ERROR_NONE, ERROR_RETRY_COUNT_EXHAUSTED, retryHandler);
- }
- onGetRebootEscrowKeyFailed(users, attemptNumber, retryHandler);
- return;
- }
-
- Slog.w(TAG, "Failed to load reboot escrow data after " + attemptNumber + " attempts");
- if (mInjector.serverBasedResumeOnReboot() && !mInjector.isNetworkConnected()) {
- mLoadEscrowDataErrorCode = ERROR_NO_NETWORK;
+ if (mRebootEscrowTimedOut) {
+ Slog.w(TAG, "Failed to load reboot escrow data within timeout");
+ compareAndSetLoadEscrowDataErrorCode(ERROR_NONE, ERROR_TIMEOUT_EXHAUSTED, retryHandler);
} else {
- mLoadEscrowDataErrorCode = ERROR_RETRY_COUNT_EXHAUSTED;
+ Slog.w(TAG, "Failed to load reboot escrow data after " + attemptNumber + " attempts");
+ compareAndSetLoadEscrowDataErrorCode(
+ ERROR_NONE, ERROR_RETRY_COUNT_EXHAUSTED, retryHandler);
}
onGetRebootEscrowKeyFailed(users, attemptNumber, retryHandler);
}
diff --git a/services/core/java/com/android/server/locksettings/flags.aconfig b/services/core/java/com/android/server/locksettings/flags.aconfig
deleted file mode 100644
index 6818de91c98e..000000000000
--- a/services/core/java/com/android/server/locksettings/flags.aconfig
+++ /dev/null
@@ -1,9 +0,0 @@
-package: "com.android.server.locksettings"
-container: "system"
-
-flag {
- name: "wait_for_internet_ror"
- namespace: "sudo"
- description: "Feature flag to wait for internet connectivity before calling resume on reboot server."
- bug: "231660348"
-} \ No newline at end of file
diff --git a/services/core/java/com/android/server/media/projection/Android.bp b/services/core/java/com/android/server/media/projection/Android.bp
new file mode 100644
index 000000000000..114be7d20d5b
--- /dev/null
+++ b/services/core/java/com/android/server/media/projection/Android.bp
@@ -0,0 +1,21 @@
+//
+// 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.
+
+team {
+ name: "trendy_team_media_projection",
+
+ // go/trendy/manage/engineers/6362947212640256
+ trendy_team_id: "6362947212640256",
+}
diff --git a/services/core/java/com/android/server/media/projection/TEST_MAPPING b/services/core/java/com/android/server/media/projection/TEST_MAPPING
index b33097c50002..7061044aaeee 100644
--- a/services/core/java/com/android/server/media/projection/TEST_MAPPING
+++ b/services/core/java/com/android/server/media/projection/TEST_MAPPING
@@ -2,6 +2,34 @@
"presubmit": [
{
"name": "MediaProjectionTests"
+ },
+ {
+ "name": "CtsMediaProjectionTestCases"
+ },
+ {
+ "name": "CtsMediaProjectionSDK33TestCases"
+ },
+ {
+ "name": "CtsMediaProjectionSDK34TestCases"
+ },
+ {
+ "name": "CtsMediaAudioTestCases",
+ "options": [
+ {
+ "include-filter": "android.media.audio.cts.RemoteSubmixTest"
+ },
+ {
+ "include-filter": "android.media.audio.cts.AudioPlaybackCaptureTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsStatsdAtomHostTestCases",
+ "options": [
+ {
+ "include-filter": "android.cts.statsdatom.media.projection.MediaProjectionAtomsTests"
+ }
+ ]
}
]
}
diff --git a/services/core/java/com/android/server/media/quality/MediaQualityUtils.java b/services/core/java/com/android/server/media/quality/MediaQualityUtils.java
index cf8b703a2641..05aac5587c2c 100644
--- a/services/core/java/com/android/server/media/quality/MediaQualityUtils.java
+++ b/services/core/java/com/android/server/media/quality/MediaQualityUtils.java
@@ -18,13 +18,20 @@ package com.android.server.media.quality;
import android.content.ContentValues;
import android.database.Cursor;
+import android.hardware.tv.mediaquality.ColorRange;
+import android.hardware.tv.mediaquality.ColorSpace;
+import android.hardware.tv.mediaquality.ColorTemperature;
import android.hardware.tv.mediaquality.DolbyAudioProcessing;
import android.hardware.tv.mediaquality.DtsVirtualX;
+import android.hardware.tv.mediaquality.Gamma;
import android.hardware.tv.mediaquality.ParameterDefaultValue;
import android.hardware.tv.mediaquality.ParameterName;
import android.hardware.tv.mediaquality.ParameterRange;
import android.hardware.tv.mediaquality.PictureParameter;
+import android.hardware.tv.mediaquality.PictureQualityEventType;
+import android.hardware.tv.mediaquality.QualityLevel;
import android.hardware.tv.mediaquality.SoundParameter;
+import android.media.quality.MediaQualityContract;
import android.media.quality.MediaQualityContract.BaseParameters;
import android.media.quality.MediaQualityContract.PictureQuality;
import android.media.quality.MediaQualityContract.SoundQuality;
@@ -371,7 +378,7 @@ public final class MediaQualityUtils {
}
List<PictureParameter> pictureParams = new ArrayList<>();
if (params.containsKey(PictureQuality.PARAMETER_BRIGHTNESS)) {
- pictureParams.add(PictureParameter.brightness(params.getLong(
+ pictureParams.add(PictureParameter.brightness((float) params.getDouble(
PictureQuality.PARAMETER_BRIGHTNESS)));
params.remove(PictureQuality.PARAMETER_BRIGHTNESS);
}
@@ -441,28 +448,46 @@ public final class MediaQualityUtils {
params.remove(PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN);
}
if (params.containsKey(PictureQuality.PARAMETER_NOISE_REDUCTION)) {
- pictureParams.add(PictureParameter.noiseReduction(
- (byte) params.getInt(PictureQuality.PARAMETER_NOISE_REDUCTION)));
+ String noiseReductionString = params.getString(
+ PictureQuality.PARAMETER_NOISE_REDUCTION);
+ if (noiseReductionString != null) {
+ byte noiseReductionByte = mapQualityLevel(noiseReductionString);
+ pictureParams.add(PictureParameter.noiseReduction(noiseReductionByte));
+ }
params.remove(PictureQuality.PARAMETER_NOISE_REDUCTION);
}
if (params.containsKey(PictureQuality.PARAMETER_MPEG_NOISE_REDUCTION)) {
- pictureParams.add(PictureParameter.mpegNoiseReduction(
- (byte) params.getInt(PictureQuality.PARAMETER_MPEG_NOISE_REDUCTION)));
+ String mpegNoiseReductionString = params.getString(
+ PictureQuality.PARAMETER_MPEG_NOISE_REDUCTION);
+ if (mpegNoiseReductionString != null) {
+ byte mpegNoiseReductionByte = mapQualityLevel(mpegNoiseReductionString);
+ pictureParams.add(PictureParameter.mpegNoiseReduction(mpegNoiseReductionByte));
+ }
params.remove(PictureQuality.PARAMETER_MPEG_NOISE_REDUCTION);
}
if (params.containsKey(PictureQuality.PARAMETER_FLESH_TONE)) {
- pictureParams.add(PictureParameter.fleshTone(
- (byte) params.getInt(PictureQuality.PARAMETER_FLESH_TONE)));
+ String fleshToneString = params.getString(PictureQuality.PARAMETER_FLESH_TONE);
+ if (fleshToneString != null) {
+ byte fleshToneByte = mapQualityLevel(fleshToneString);
+ pictureParams.add(PictureParameter.fleshTone(fleshToneByte));
+ }
params.remove(PictureQuality.PARAMETER_FLESH_TONE);
}
if (params.containsKey(PictureQuality.PARAMETER_DECONTOUR)) {
- pictureParams.add(PictureParameter.deContour(
- (byte) params.getInt(PictureQuality.PARAMETER_DECONTOUR)));
+ String decontourString = params.getString(PictureQuality.PARAMETER_DECONTOUR);
+ if (decontourString != null) {
+ byte decontourByte = mapQualityLevel(decontourString);
+ pictureParams.add(PictureParameter.deContour(decontourByte));
+ }
params.remove(PictureQuality.PARAMETER_DECONTOUR);
}
if (params.containsKey(PictureQuality.PARAMETER_DYNAMIC_LUMA_CONTROL)) {
- pictureParams.add(PictureParameter.dynamicLumaControl(
- (byte) params.getInt(PictureQuality.PARAMETER_DYNAMIC_LUMA_CONTROL)));
+ String dynamicLunaControlString = params.getString(
+ PictureQuality.PARAMETER_DYNAMIC_LUMA_CONTROL);
+ if (dynamicLunaControlString != null) {
+ byte dynamicLunaControlByte = mapQualityLevel(dynamicLunaControlString);
+ pictureParams.add(PictureParameter.dynamicLumaControl(dynamicLunaControlByte));
+ }
params.remove(PictureQuality.PARAMETER_DYNAMIC_LUMA_CONTROL);
}
if (params.containsKey(PictureQuality.PARAMETER_FILM_MODE)) {
@@ -481,9 +506,48 @@ public final class MediaQualityUtils {
params.remove(PictureQuality.PARAMETER_COLOR_TUNE);
}
if (params.containsKey(PictureQuality.PARAMETER_COLOR_TEMPERATURE)) {
- pictureParams.add(PictureParameter.colorTemperature(
- (byte) params.getInt(
- PictureQuality.PARAMETER_COLOR_TEMPERATURE)));
+ String colorTemperatureString = params.getString(
+ PictureQuality.PARAMETER_COLOR_TEMPERATURE);
+ if (colorTemperatureString != null) {
+ byte colorTemperatureByte;
+ switch (colorTemperatureString) {
+ case MediaQualityContract.COLOR_TEMP_USER:
+ colorTemperatureByte = ColorTemperature.USER;
+ break;
+ case MediaQualityContract.COLOR_TEMP_COOL:
+ colorTemperatureByte = ColorTemperature.COOL;
+ break;
+ case MediaQualityContract.COLOR_TEMP_STANDARD:
+ colorTemperatureByte = ColorTemperature.STANDARD;
+ break;
+ case MediaQualityContract.COLOR_TEMP_WARM:
+ colorTemperatureByte = ColorTemperature.WARM;
+ break;
+ case MediaQualityContract.COLOR_TEMP_USER_HDR10PLUS:
+ colorTemperatureByte = ColorTemperature.USER_HDR10PLUS;
+ break;
+ case MediaQualityContract.COLOR_TEMP_COOL_HDR10PLUS:
+ colorTemperatureByte = ColorTemperature.COOL_HDR10PLUS;
+ break;
+ case MediaQualityContract.COLOR_TEMP_STANDARD_HDR10PLUS:
+ colorTemperatureByte = ColorTemperature.STANDARD_HDR10PLUS;
+ break;
+ case MediaQualityContract.COLOR_TEMP_WARM_HDR10PLUS:
+ colorTemperatureByte = ColorTemperature.WARM_HDR10PLUS;
+ break;
+ case MediaQualityContract.COLOR_TEMP_FMMSDR:
+ colorTemperatureByte = ColorTemperature.FMMSDR;
+ break;
+ case MediaQualityContract.COLOR_TEMP_FMMHDR:
+ colorTemperatureByte = ColorTemperature.FMMHDR;
+ break;
+ default:
+ colorTemperatureByte = ColorTemperature.STANDARD;
+ Log.e("PictureParams", "Invalid color_temp string: "
+ + colorTemperatureString);
+ }
+ pictureParams.add(PictureParameter.colorTemperature(colorTemperatureByte));
+ }
params.remove(PictureQuality.PARAMETER_COLOR_TEMPERATURE);
}
if (params.containsKey(PictureQuality.PARAMETER_GLOBAL_DIMMING)) {
@@ -517,8 +581,26 @@ public final class MediaQualityUtils {
params.remove(PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN);
}
if (params.containsKey(PictureQuality.PARAMETER_LEVEL_RANGE)) {
- pictureParams.add(PictureParameter.levelRange(
- (byte) params.getInt(PictureQuality.PARAMETER_LEVEL_RANGE)));
+ String levelRangeString = params.getString(PictureQuality.PARAMETER_LEVEL_RANGE);
+ if (levelRangeString != null) {
+ byte levelRangeByte;
+ switch (levelRangeString) {
+ case "AUTO":
+ levelRangeByte = ColorRange.AUTO;
+ break;
+ case "LIMITED":
+ levelRangeByte = ColorRange.LIMITED;
+ break;
+ case "FULL":
+ levelRangeByte = ColorRange.FULL;
+ break;
+ default:
+ levelRangeByte = ColorRange.AUTO;
+ Log.e("PictureParams", "Invalid color_range string: "
+ + levelRangeString);
+ }
+ pictureParams.add(PictureParameter.levelRange(levelRangeByte));
+ }
params.remove(PictureQuality.PARAMETER_LEVEL_RANGE);
}
if (params.containsKey(PictureQuality.PARAMETER_GAMUT_MAPPING)) {
@@ -547,13 +629,61 @@ public final class MediaQualityUtils {
params.remove(PictureQuality.PARAMETER_CVRR);
}
if (params.containsKey(PictureQuality.PARAMETER_HDMI_RGB_RANGE)) {
- pictureParams.add(PictureParameter.hdmiRgbRange(
- (byte) params.getInt(PictureQuality.PARAMETER_HDMI_RGB_RANGE)));
+ String hdmiRgbRangeString = params.getString(PictureQuality.PARAMETER_HDMI_RGB_RANGE);
+ if (hdmiRgbRangeString != null) {
+ byte hdmiRgbRangeByte;
+ switch (hdmiRgbRangeString) {
+ case "AUTO":
+ hdmiRgbRangeByte = ColorRange.AUTO;
+ break;
+ case "LIMITED":
+ hdmiRgbRangeByte = ColorRange.LIMITED;
+ break;
+ case "FULL":
+ hdmiRgbRangeByte = ColorRange.FULL;
+ break;
+ default:
+ hdmiRgbRangeByte = ColorRange.AUTO;
+ Log.e("PictureParams", "Invalid hdmi_rgb_range string: "
+ + hdmiRgbRangeByte);
+ }
+ pictureParams.add(PictureParameter.hdmiRgbRange(hdmiRgbRangeByte));
+ }
params.remove(PictureQuality.PARAMETER_HDMI_RGB_RANGE);
}
if (params.containsKey(PictureQuality.PARAMETER_COLOR_SPACE)) {
- pictureParams.add(PictureParameter.colorSpace(
- (byte) params.getInt(PictureQuality.PARAMETER_COLOR_SPACE)));
+ String colorSpaceString = params.getString(PictureQuality.PARAMETER_COLOR_SPACE);
+ if (colorSpaceString != null) {
+ byte colorSpaceByte;
+ switch (colorSpaceString) {
+ case "AUTO":
+ colorSpaceByte = ColorSpace.AUTO;
+ break;
+ case "S_RGB_BT_709":
+ colorSpaceByte = ColorSpace.S_RGB_BT_709;
+ break;
+ case "DCI":
+ colorSpaceByte = ColorSpace.DCI;
+ break;
+ case "ADOBE_RGB":
+ colorSpaceByte = ColorSpace.ADOBE_RGB;
+ break;
+ case "BT2020":
+ colorSpaceByte = ColorSpace.BT2020;
+ break;
+ case "ON":
+ colorSpaceByte = ColorSpace.ON;
+ break;
+ case "OFF":
+ colorSpaceByte = ColorSpace.OFF;
+ break;
+ default:
+ colorSpaceByte = ColorSpace.OFF;
+ Log.e("PictureParams", "Invalid color_space string: "
+ + colorSpaceString);
+ }
+ pictureParams.add(PictureParameter.colorSpace(colorSpaceByte));
+ }
params.remove(PictureQuality.PARAMETER_COLOR_SPACE);
}
if (params.containsKey(PictureQuality.PARAMETER_PANEL_INIT_MAX_LUMINCE_NITS)) {
@@ -567,8 +697,25 @@ public final class MediaQualityUtils {
params.remove(PictureQuality.PARAMETER_PANEL_INIT_MAX_LUMINCE_VALID);
}
if (params.containsKey(PictureQuality.PARAMETER_GAMMA)) {
- pictureParams.add(PictureParameter.gamma(
- (byte) params.getInt(PictureQuality.PARAMETER_GAMMA)));
+ String gammaString = params.getString(PictureQuality.PARAMETER_GAMMA);
+ if (gammaString != null) {
+ byte gammaByte;
+ switch (gammaString) {
+ case "DARK":
+ gammaByte = Gamma.DARK;
+ break;
+ case "MIDDLE":
+ gammaByte = Gamma.MIDDLE;
+ break;
+ case "BRIGHT":
+ gammaByte = Gamma.BRIGHT;
+ break;
+ default:
+ gammaByte = Gamma.DARK;
+ Log.e("PictureParams", "Invalid gamma string: " + gammaString);
+ }
+ pictureParams.add(PictureParameter.gamma(gammaByte));
+ }
params.remove(PictureQuality.PARAMETER_GAMMA);
}
if (params.containsKey(PictureQuality.PARAMETER_COLOR_TEMPERATURE_RED_OFFSET)) {
@@ -602,13 +749,19 @@ public final class MediaQualityUtils {
params.remove(PictureQuality.PARAMETER_ELEVEN_POINT_BLUE);
}
if (params.containsKey(PictureQuality.PARAMETER_LOW_BLUE_LIGHT)) {
- pictureParams.add(PictureParameter.lowBlueLight(
- (byte) params.getInt(PictureQuality.PARAMETER_LOW_BLUE_LIGHT)));
+ String lowBlueLightString = params.getString(PictureQuality.PARAMETER_LOW_BLUE_LIGHT);
+ if (lowBlueLightString != null) {
+ byte lowBlueLightByte = mapQualityLevel(lowBlueLightString);
+ pictureParams.add(PictureParameter.lowBlueLight(lowBlueLightByte));
+ }
params.remove(PictureQuality.PARAMETER_LOW_BLUE_LIGHT);
}
if (params.containsKey(PictureQuality.PARAMETER_LD_MODE)) {
- pictureParams.add(PictureParameter.LdMode(
- (byte) params.getInt(PictureQuality.PARAMETER_LD_MODE)));
+ String ldModeString = params.getString(PictureQuality.PARAMETER_LD_MODE);
+ if (ldModeString != null) {
+ byte ldModeByte = mapQualityLevel(ldModeString);
+ pictureParams.add(PictureParameter.LdMode(ldModeByte));
+ }
params.remove(PictureQuality.PARAMETER_LD_MODE);
}
if (params.containsKey(PictureQuality.PARAMETER_OSD_RED_GAIN)) {
@@ -767,8 +920,44 @@ public final class MediaQualityUtils {
params.remove(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_FLESH);
}
if (params.containsKey(PictureQuality.PARAMETER_PICTURE_QUALITY_EVENT_TYPE)) {
- pictureParams.add(PictureParameter.pictureQualityEventType(
- (byte) params.getInt(PictureQuality.PARAMETER_PICTURE_QUALITY_EVENT_TYPE)));
+ String pictureQualityEventTypeString = params.getString(
+ PictureQuality.PARAMETER_PICTURE_QUALITY_EVENT_TYPE);
+ if (pictureQualityEventTypeString != null) {
+ byte pictureQualityEventTypeByte;
+ switch (pictureQualityEventTypeString) {
+ case "NONE":
+ pictureQualityEventTypeByte = PictureQualityEventType.NONE;
+ break;
+ case "BBD_RESULT":
+ pictureQualityEventTypeByte = PictureQualityEventType.BBD_RESULT;
+ break;
+ case "VIDEO_DELAY_CHANGE":
+ pictureQualityEventTypeByte = PictureQualityEventType.VIDEO_DELAY_CHANGE;
+ break;
+ case "CAPTUREPOINT_INFO_CHANGE":
+ pictureQualityEventTypeByte =
+ PictureQualityEventType.CAPTUREPOINT_INFO_CHANGE;
+ break;
+ case "VIDEOPATH_CHANGE":
+ pictureQualityEventTypeByte = PictureQualityEventType.VIDEOPATH_CHANGE;
+ break;
+ case "EXTRA_FRAME_CHANGE":
+ pictureQualityEventTypeByte = PictureQualityEventType.EXTRA_FRAME_CHANGE;
+ break;
+ case "DOLBY_IQ_CHANGE":
+ pictureQualityEventTypeByte = PictureQualityEventType.DOLBY_IQ_CHANGE;
+ break;
+ case "DOLBY_APO_CHANGE":
+ pictureQualityEventTypeByte = PictureQualityEventType.DOLBY_APO_CHANGE;
+ break;
+ default:
+ pictureQualityEventTypeByte = PictureQualityEventType.NONE;
+ Log.e("PictureParams", "Invalid event type string: "
+ + pictureQualityEventTypeString);
+ }
+ pictureParams.add(
+ PictureParameter.pictureQualityEventType(pictureQualityEventTypeByte));
+ }
params.remove(PictureQuality.PARAMETER_PICTURE_QUALITY_EVENT_TYPE);
}
return pictureParams.toArray(new PictureParameter[0]);
@@ -1657,6 +1846,19 @@ public final class MediaQualityUtils {
return colIndex != -1 ? cursor.getString(colIndex) : null;
}
+ private static byte mapQualityLevel(String qualityLevel) {
+ return switch (qualityLevel) {
+ case MediaQualityContract.LEVEL_OFF -> QualityLevel.OFF;
+ case MediaQualityContract.LEVEL_LOW -> QualityLevel.LOW;
+ case MediaQualityContract.LEVEL_MEDIUM -> QualityLevel.MEDIUM;
+ case MediaQualityContract.LEVEL_HIGH -> QualityLevel.HIGH;
+ default -> {
+ Log.e("PictureParams", "Invalid noise_reduction string: " + qualityLevel);
+ yield QualityLevel.OFF;
+ }
+ };
+ }
+
private MediaQualityUtils() {
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 8d787fe99e3a..651111e431c3 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -985,8 +985,8 @@ public final class OverlayManagerService extends SystemService {
final String pkgName = request.overlay.getPackageName();
if (callingUid != Process.ROOT_UID && !ArrayUtils.contains(
mPackageManager.getPackagesForUid(callingUid), pkgName)) {
- throw new IllegalArgumentException("UID " + callingUid + " does own package"
- + "name " + pkgName);
+ throw new IllegalArgumentException("UID " + callingUid + " does not own "
+ + "packageName " + pkgName);
}
} else {
// Enforce actor requirements for enabling, disabling, and reordering overlays.
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index b441e9dd561d..7e5ada54c953 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -1994,6 +1994,9 @@ public class ComputerEngine implements Computer {
if (Process.isSdkSandboxUid(uid)) {
uid = getBaseSdkSandboxUid();
}
+ if(isKnownIsolatedComputeApp(uid)) {
+ uid = getIsolatedOwner(uid);
+ }
final int appId = UserHandle.getAppId(uid);
return getPackagesForUidInternalBody(callingUid, userId, appId, isCallerInstantApp);
}
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 38aa57f785e5..bbee77ce58ad 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -425,7 +425,7 @@ final class DeletePackageHelper {
user == null || user.getIdentifier() == USER_ALL;
if ((!deleteSystem || deleteAllUsers) && disabledPs == null) {
Slog.w(TAG, "Attempt to delete unknown system package "
- + ps.getPkg().getPackageName());
+ + ps.getName());
return null;
}
// Confirmed if the system package has been updated
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index 3361dbc2df07..72f2068800d2 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -869,6 +869,9 @@ final class InstallRequest {
public void setScannedPackageSettingFirstInstallTimeFromReplaced(
@Nullable PackageStateInternal replacedPkgSetting, int[] userId) {
assertScanResultExists();
+ if (replacedPkgSetting == null) {
+ return;
+ }
mScanResult.mPkgSetting.setFirstInstallTimeFromReplaced(replacedPkgSetting, userId);
}
diff --git a/services/core/java/com/android/server/pm/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java
index f88681dbcaeb..e98176b0e82b 100644
--- a/services/core/java/com/android/server/pm/UserManagerInternal.java
+++ b/services/core/java/com/android/server/pm/UserManagerInternal.java
@@ -18,6 +18,7 @@ package com.android.server.pm;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SpecialUsers.CanBeNULL;
import android.annotation.UserIdInt;
import android.content.Context;
import android.content.pm.LauncherUserInfo;
@@ -620,11 +621,17 @@ public abstract class UserManagerInternal {
* Returns the user id of the communal profile, or {@link android.os.UserHandle#USER_NULL}
* if there is no such user.
*/
- public abstract @UserIdInt int getCommunalProfileId();
+ public abstract @CanBeNULL @UserIdInt int getCommunalProfileId();
/**
- * Checks whether to show a notification for sounds (e.g., alarms, timers, etc.) from
- * background users.
+ * Returns the user id of the supervising profile, or {@link android.os.UserHandle#USER_NULL} if
+ * there is no such user.
+ */
+ public abstract @CanBeNULL @UserIdInt int getSupervisingProfileId();
+
+ /**
+ * Checks whether to show a notification for sounds (e.g., alarms, timers, etc.) from background
+ * users.
*/
public static boolean shouldShowNotificationForBackgroundUserSounds() {
return Flags.addUiForSoundsFromBackgroundUsers() && Resources.getSystem().getBoolean(
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 33a7e7476cf6..053b4aae90dd 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1530,6 +1530,20 @@ public class UserManagerService extends IUserManager.Stub {
return UserHandle.USER_NULL;
}
+ /** Returns the currently-designated supervising profile, or USER_NULL if not present. */
+ private @CanBeNULL @UserIdInt int getSupervisingProfileId() {
+ synchronized (mUsersLock) {
+ final int userSize = mUsers.size();
+ for (int i = 0; i < userSize; i++) {
+ final UserInfo user = mUsers.valueAt(i).info;
+ if (user.isSupervisingProfile() && !mRemovingUserIds.get(user.id)) {
+ return user.id;
+ }
+ }
+ }
+ return UserHandle.USER_NULL;
+ }
+
public @NonNull List<UserInfo> getUsers(boolean excludeDying) {
return getUsers(/*excludePartial= */ true, excludeDying, /* excludePreCreated= */
true);
@@ -8348,10 +8362,14 @@ public class UserManagerService extends IUserManager.Stub {
}
@Override
- public @UserIdInt int getCommunalProfileId() {
+ public @CanBeNULL @UserIdInt int getCommunalProfileId() {
return getCommunalProfileIdUnchecked();
}
+ @Override
+ public @CanBeNULL @UserIdInt int getSupervisingProfileId() {
+ return UserManagerService.this.getSupervisingProfileId();
+ }
} // class LocalService
diff --git a/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java b/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java
index e989d6875d15..4c14e96e6c98 100644
--- a/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java
+++ b/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java
@@ -36,6 +36,7 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.SparseArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.function.DodecFunction;
import com.android.internal.util.function.HexConsumer;
@@ -141,150 +142,191 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe
class AccessCheckDelegateImpl implements AccessCheckDelegate {
public static final String SHELL_PKG = "com.android.shell";
+
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
private int mDelegateAndOwnerUid = INVALID_UID;
@Nullable
+ @GuardedBy("mLock")
private String mDelegatePackage;
@Nullable
+ @GuardedBy("mLock")
private String[] mDelegatePermissions;
+ @GuardedBy("mLock")
boolean mDelegateAllPermissions;
@Nullable
+ @GuardedBy("mLock")
private SparseArray<ArrayMap<String, Integer>> mOverridePermissionStates;
@Override
public void setShellPermissionDelegate(int uid, @NonNull String packageName,
@Nullable String[] permissions) {
- mDelegateAndOwnerUid = uid;
- mDelegatePackage = packageName;
- mDelegatePermissions = permissions;
- mDelegateAllPermissions = permissions == null;
+ synchronized (mLock) {
+ mDelegateAndOwnerUid = uid;
+ mDelegatePackage = packageName;
+ mDelegatePermissions = permissions;
+ mDelegateAllPermissions = permissions == null;
+ }
PackageManager.invalidatePackageInfoCache();
}
@Override
public void removeShellPermissionDelegate() {
- mDelegatePackage = null;
- mDelegatePermissions = null;
- mDelegateAllPermissions = false;
+ synchronized (mLock) {
+ mDelegatePackage = null;
+ mDelegatePermissions = null;
+ mDelegateAllPermissions = false;
+ }
PackageManager.invalidatePackageInfoCache();
}
@Override
public void addOverridePermissionState(int ownerUid, int uid, @NonNull String permission,
int state) {
- if (mOverridePermissionStates == null) {
- mDelegateAndOwnerUid = ownerUid;
- mOverridePermissionStates = new SparseArray<>();
- }
+ synchronized (mLock) {
+ if (mOverridePermissionStates == null) {
+ mDelegateAndOwnerUid = ownerUid;
+ mOverridePermissionStates = new SparseArray<>();
+ }
- int uidIdx = mOverridePermissionStates.indexOfKey(uid);
- ArrayMap<String, Integer> perUidOverrides;
- if (uidIdx < 0) {
- perUidOverrides = new ArrayMap<>();
- mOverridePermissionStates.put(uid, perUidOverrides);
- } else {
- perUidOverrides = mOverridePermissionStates.valueAt(uidIdx);
- }
+ int uidIdx = mOverridePermissionStates.indexOfKey(uid);
+ ArrayMap<String, Integer> perUidOverrides;
+ if (uidIdx < 0) {
+ perUidOverrides = new ArrayMap<>();
+ mOverridePermissionStates.put(uid, perUidOverrides);
+ } else {
+ perUidOverrides = mOverridePermissionStates.valueAt(uidIdx);
+ }
- perUidOverrides.put(permission, state);
+ perUidOverrides.put(permission, state);
+ }
PackageManager.invalidatePackageInfoCache();
}
@Override
public void removeOverridePermissionState(int uid, @NonNull String permission) {
- if (mOverridePermissionStates == null) {
- return;
- }
+ synchronized (mLock) {
+ if (mOverridePermissionStates == null) {
+ return;
+ }
- ArrayMap<String, Integer> perUidOverrides = mOverridePermissionStates.get(uid);
+ ArrayMap<String, Integer> perUidOverrides = mOverridePermissionStates.get(uid);
- if (perUidOverrides == null) {
- return;
- }
+ if (perUidOverrides == null) {
+ return;
+ }
- perUidOverrides.remove(permission);
- PackageManager.invalidatePackageInfoCache();
+ perUidOverrides.remove(permission);
- if (perUidOverrides.isEmpty()) {
- mOverridePermissionStates.remove(uid);
- }
- if (mOverridePermissionStates.size() == 0) {
- mOverridePermissionStates = null;
+ if (perUidOverrides.isEmpty()) {
+ mOverridePermissionStates.remove(uid);
+ }
+ if (mOverridePermissionStates.size() == 0) {
+ mOverridePermissionStates = null;
+ }
}
+ PackageManager.invalidatePackageInfoCache();
}
@Override
public void clearOverridePermissionStates(int uid) {
- if (mOverridePermissionStates == null) {
- return;
- }
+ synchronized (mLock) {
+ if (mOverridePermissionStates == null) {
+ return;
+ }
- mOverridePermissionStates.remove(uid);
- PackageManager.invalidatePackageInfoCache();
+ mOverridePermissionStates.remove(uid);
- if (mOverridePermissionStates.size() == 0) {
- mOverridePermissionStates = null;
+ if (mOverridePermissionStates.size() == 0) {
+ mOverridePermissionStates = null;
+ }
}
+ PackageManager.invalidatePackageInfoCache();
}
@Override
public void clearAllOverridePermissionStates() {
- mOverridePermissionStates = null;
+ synchronized (mLock) {
+ mOverridePermissionStates = null;
+ }
PackageManager.invalidatePackageInfoCache();
}
@Override
public List<String> getDelegatedPermissionNames() {
- return mDelegatePermissions == null ? null : List.of(mDelegatePermissions);
+ synchronized (mLock) {
+ return mDelegatePermissions == null ? null : List.of(mDelegatePermissions);
+ }
}
@Override
public boolean hasShellPermissionDelegate() {
- return mDelegateAllPermissions || mDelegatePermissions != null;
+ synchronized (mLock) {
+ return mDelegateAllPermissions || mDelegatePermissions != null;
+ }
}
@Override
public boolean isDelegatePackage(int uid, @NonNull String packageName) {
- return mDelegateAndOwnerUid == uid && TextUtils.equals(mDelegatePackage, packageName);
+ synchronized (mLock) {
+ return mDelegateAndOwnerUid == uid
+ && TextUtils.equals(mDelegatePackage, packageName);
+ }
}
@Override
public boolean hasOverriddenPermissions() {
- return mOverridePermissionStates != null;
+ synchronized (mLock) {
+ return mOverridePermissionStates != null;
+ }
}
@Override
public boolean isDelegateAndOwnerUid(int uid) {
- return uid == mDelegateAndOwnerUid;
+ synchronized (mLock) {
+ return uid == mDelegateAndOwnerUid;
+ }
}
@Override
public boolean hasDelegateOrOverrides() {
- return hasShellPermissionDelegate() || hasOverriddenPermissions();
+ synchronized (mLock) {
+ return hasShellPermissionDelegate() || hasOverriddenPermissions();
+ }
}
@Override
public int checkPermission(@NonNull String packageName, @NonNull String permissionName,
@NonNull String persistentDeviceId, @UserIdInt int userId,
@NonNull QuadFunction<String, String, String, Integer, Integer> superImpl) {
- if (TextUtils.equals(mDelegatePackage, packageName) && !SHELL_PKG.equals(packageName)) {
- if (isDelegatePermission(permissionName)) {
- final long identity = Binder.clearCallingIdentity();
- try {
- return checkPermission(SHELL_PKG, permissionName, persistentDeviceId,
- userId, superImpl);
- } finally {
- Binder.restoreCallingIdentity(identity);
+ boolean useShellDelegate;
+
+ synchronized (mLock) {
+ useShellDelegate = !SHELL_PKG.equals(packageName)
+ && TextUtils.equals(mDelegatePackage, packageName)
+ && isDelegatePermission(permissionName);
+
+ if (!useShellDelegate && mOverridePermissionStates != null) {
+ int uid = LocalServices.getService(PackageManagerInternal.class)
+ .getPackageUid(packageName, 0, userId);
+ if (uid >= 0) {
+ Map<String, Integer> permissionGrants = mOverridePermissionStates.get(uid);
+ if (permissionGrants != null
+ && permissionGrants.containsKey(permissionName)) {
+ return permissionGrants.get(permissionName);
+ }
}
}
}
- if (mOverridePermissionStates != null) {
- int uid = LocalServices.getService(PackageManagerInternal.class)
- .getPackageUid(packageName, 0, userId);
- if (uid >= 0) {
- Map<String, Integer> permissionGrants = mOverridePermissionStates.get(uid);
- if (permissionGrants != null && permissionGrants.containsKey(permissionName)) {
- return permissionGrants.get(permissionName);
- }
+
+ if (useShellDelegate) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return checkPermission(SHELL_PKG, permissionName, persistentDeviceId, userId,
+ superImpl);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
}
return superImpl.apply(packageName, permissionName, persistentDeviceId, userId);
@@ -294,21 +336,27 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe
public int checkUidPermission(int uid, @NonNull String permissionName,
@NonNull String persistentDeviceId,
@NonNull TriFunction<Integer, String, String, Integer> superImpl) {
- if (uid == mDelegateAndOwnerUid && uid != Process.SHELL_UID) {
- if (isDelegatePermission(permissionName)) {
- final long identity = Binder.clearCallingIdentity();
- try {
- return checkUidPermission(Process.SHELL_UID, permissionName,
- persistentDeviceId, superImpl);
- } finally {
- Binder.restoreCallingIdentity(identity);
+ boolean useShellDelegate;
+
+ synchronized (mLock) {
+ useShellDelegate = uid != Process.SHELL_UID && uid == mDelegateAndOwnerUid
+ && isDelegatePermission(permissionName);
+
+ if (!useShellDelegate && mOverridePermissionStates != null) {
+ Map<String, Integer> permissionGrants = mOverridePermissionStates.get(uid);
+ if (permissionGrants != null && permissionGrants.containsKey(permissionName)) {
+ return permissionGrants.get(permissionName);
}
}
}
- if (mOverridePermissionStates != null) {
- Map<String, Integer> permissionGrants = mOverridePermissionStates.get(uid);
- if (permissionGrants != null && permissionGrants.containsKey(permissionName)) {
- return permissionGrants.get(permissionName);
+
+ if (useShellDelegate) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return checkUidPermission(Process.SHELL_UID, permissionName, persistentDeviceId,
+ superImpl);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
}
return superImpl.apply(uid, permissionName, persistentDeviceId);
@@ -319,7 +367,13 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe
@Nullable String attributionTag, int virtualDeviceId, boolean raw,
@NonNull HexFunction<Integer, Integer, String, String, Integer, Boolean, Integer>
superImpl) {
- if (uid == mDelegateAndOwnerUid && isDelegateOp(code)) {
+ boolean useShellDelegate;
+
+ synchronized (mLock) {
+ useShellDelegate = uid == mDelegateAndOwnerUid && isDelegateOp(code);
+ }
+
+ if (useShellDelegate) {
final int shellUid = UserHandle.getUid(UserHandle.getUserId(uid),
Process.SHELL_UID);
final long identity = Binder.clearCallingIdentity();
@@ -335,7 +389,13 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe
@Override
public int checkAudioOperation(int code, int usage, int uid, @Nullable String packageName,
@NonNull QuadFunction<Integer, Integer, Integer, String, Integer> superImpl) {
- if (uid == mDelegateAndOwnerUid && isDelegateOp(code)) {
+ boolean useShellDelegate;
+
+ synchronized (mLock) {
+ useShellDelegate = uid == mDelegateAndOwnerUid && isDelegateOp(code);
+ }
+
+ if (useShellDelegate) {
final int shellUid = UserHandle.getUid(UserHandle.getUserId(uid),
Process.SHELL_UID);
final long identity = Binder.clearCallingIdentity();
@@ -354,7 +414,13 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe
@Nullable String message, boolean shouldCollectMessage, int notedCount,
@NonNull NonaFunction<Integer, Integer, String, String, Integer, Boolean, String,
Boolean, Integer, SyncNotedAppOp> superImpl) {
- if (uid == mDelegateAndOwnerUid && isDelegateOp(code)) {
+ boolean useShellDelegate;
+
+ synchronized (mLock) {
+ useShellDelegate = uid == mDelegateAndOwnerUid && isDelegateOp(code);
+ }
+
+ if (useShellDelegate) {
final int shellUid = UserHandle.getUid(UserHandle.getUserId(uid),
Process.SHELL_UID);
final long identity = Binder.clearCallingIdentity();
@@ -375,21 +441,29 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe
@Nullable String message, boolean shouldCollectMessage, boolean skiProxyOperation,
@NonNull HexFunction<Integer, AttributionSource, Boolean, String, Boolean,
Boolean, SyncNotedAppOp> superImpl) {
- if (!isDelegateOp(code)) {
- return superImpl.apply(code, attributionSource, shouldCollectAsyncNotedOp,
- message, shouldCollectMessage, skiProxyOperation);
+ boolean isDelegateOp;
+ int delegateAndOwnerUid;
+
+ synchronized (mLock) {
+ isDelegateOp = isDelegateOp(code);
+ delegateAndOwnerUid = mDelegateAndOwnerUid;
+ }
+
+ if (!isDelegateOp) {
+ return superImpl.apply(code, attributionSource, shouldCollectAsyncNotedOp, message,
+ shouldCollectMessage, skiProxyOperation);
}
final int shellUid = UserHandle.getUid(
UserHandle.getUserId(attributionSource.getUid()), Process.SHELL_UID);
AttributionSource next = attributionSource.getNext();
- if (next != null && next.getUid() == mDelegateAndOwnerUid) {
+ if (next != null && next.getUid() == delegateAndOwnerUid) {
next = new AttributionSource(shellUid, Process.INVALID_PID, SHELL_PKG,
next.getAttributionTag(), next.getToken(), /*renouncedPermissions*/ null,
next.getDeviceId(), next.getNext());
attributionSource = new AttributionSource(attributionSource, next);
}
- if (attributionSource.getUid() == mDelegateAndOwnerUid) {
+ if (attributionSource.getUid() == delegateAndOwnerUid) {
attributionSource = new AttributionSource(shellUid, Process.INVALID_PID, SHELL_PKG,
attributionSource.getAttributionTag(),
attributionSource.getToken(), /*renouncedPermissions*/ null,
@@ -397,9 +471,8 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe
}
final long identity = Binder.clearCallingIdentity();
try {
- return superImpl.apply(code, attributionSource,
- shouldCollectAsyncNotedOp, message, shouldCollectMessage,
- skiProxyOperation);
+ return superImpl.apply(code, attributionSource, shouldCollectAsyncNotedOp, message,
+ shouldCollectMessage, skiProxyOperation);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -413,7 +486,13 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe
@AttributionFlags int attributionFlags, int attributionChainId,
@NonNull DodecFunction<IBinder, Integer, Integer, String, String, Integer, Boolean,
Boolean, String, Boolean, Integer, Integer, SyncNotedAppOp> superImpl) {
- if (uid == mDelegateAndOwnerUid && isDelegateOp(code)) {
+ boolean useShellDelegate;
+
+ synchronized (mLock) {
+ useShellDelegate = uid == mDelegateAndOwnerUid && isDelegateOp(code);
+ }
+
+ if (useShellDelegate) {
final int shellUid = UserHandle.getUid(UserHandle.getUserId(uid),
Process.SHELL_UID);
final long identity = Binder.clearCallingIdentity();
@@ -440,7 +519,14 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe
@NonNull UndecFunction<IBinder, Integer, AttributionSource, Boolean,
Boolean, String, Boolean, Boolean, Integer, Integer, Integer,
SyncNotedAppOp> superImpl) {
- if (attributionSource.getUid() == mDelegateAndOwnerUid && isDelegateOp(code)) {
+ boolean useShellDelegate;
+
+ synchronized (mLock) {
+ useShellDelegate = attributionSource.getUid() == mDelegateAndOwnerUid
+ && isDelegateOp(code);
+ }
+
+ if (useShellDelegate) {
final int shellUid = UserHandle.getUid(UserHandle.getUserId(
attributionSource.getUid()), Process.SHELL_UID);
final long identity = Binder.clearCallingIdentity();
@@ -467,7 +553,14 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe
@NonNull AttributionSource attributionSource, boolean skipProxyOperation,
@NonNull QuadFunction<IBinder, Integer, AttributionSource, Boolean,
Void> superImpl) {
- if (attributionSource.getUid() == mDelegateAndOwnerUid && isDelegateOp(code)) {
+ boolean useShellDelegate;
+
+ synchronized (mLock) {
+ useShellDelegate = attributionSource.getUid() == mDelegateAndOwnerUid
+ && isDelegateOp(code);
+ }
+
+ if (useShellDelegate) {
final int shellUid = UserHandle.getUid(UserHandle.getUserId(
attributionSource.getUid()), Process.SHELL_UID);
final long identity = Binder.clearCallingIdentity();
@@ -490,7 +583,13 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe
public void finishOperation(IBinder clientId, int code, int uid, String packageName,
String attributionTag, int virtualDeviceId, @NonNull HexConsumer<IBinder, Integer,
Integer, String, String, Integer> superImpl) {
- if (uid == mDelegateAndOwnerUid && isDelegateOp(code)) {
+ boolean useShellDelegate;
+
+ synchronized (mLock) {
+ useShellDelegate = uid == mDelegateAndOwnerUid && isDelegateOp(code);
+ }
+
+ if (useShellDelegate) {
final int shellUid =
UserHandle.getUid(UserHandle.getUserId(uid), Process.SHELL_UID);
final long identity = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 2744721c3a46..e9f522d08328 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -86,6 +86,7 @@ import static android.view.contentprotection.flags.Flags.createAccessibilityOver
import static com.android.hardware.input.Flags.enableNew25q2Keycodes;
import static com.android.hardware.input.Flags.enableTalkbackAndMagnifierKeyGestures;
import static com.android.hardware.input.Flags.enableVoiceAccessKeyGestures;
+import static com.android.hardware.input.Flags.fixSearchModifierFallbacks;
import static com.android.hardware.input.Flags.inputManagerLifecycleSupport;
import static com.android.hardware.input.Flags.keyboardA11yShortcutControl;
import static com.android.hardware.input.Flags.modifierShortcutDump;
@@ -4181,6 +4182,13 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return true;
}
+ if (fixSearchModifierFallbacks()) {
+ // Pass event as unhandled to give other services, e.g. InputManagerService, the
+ // opportunity to determine if the event can be modified, e.g. generating a fallback for
+ // meta/search events.
+ return false;
+ }
+
// Reserve all the META modifier combos for system behavior
return (metaState & KeyEvent.META_META_ON) != 0;
}
diff --git a/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java b/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java
index cb82f480d73b..d152a1dbe17d 100644
--- a/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java
+++ b/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java
@@ -35,6 +35,7 @@ import static com.android.server.wm.AppCompatUtils.isChangeEnabled;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.window.DesktopModeFlags;
import com.android.server.wm.utils.OptPropFactory;
import com.android.window.flags.Flags;
@@ -177,7 +178,7 @@ class AppCompatCameraOverrides {
* </ul>
*/
boolean shouldApplyFreeformTreatmentForCameraCompat() {
- return Flags.enableCameraCompatForDesktopWindowing()
+ return DesktopModeFlags.ENABLE_CAMERA_COMPAT_SIMULATE_REQUESTED_ORIENTATION.isTrue()
&& (shouldEnableCameraCompatFreeformTreatmentForApp()
|| shouldEnableCameraCompatFreeformTreatmentForAllApps());
}
diff --git a/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java b/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java
index 276c7d2cbaa0..e7a0803df916 100644
--- a/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java
@@ -26,9 +26,9 @@ import android.app.CameraCompatTaskInfo;
import android.content.pm.ActivityInfo.ScreenOrientation;
import android.content.res.Configuration;
import android.widget.Toast;
+import android.window.DesktopModeFlags;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.window.flags.Flags;
/**
* Encapsulate policy logic related to app compat display rotation.
@@ -53,7 +53,7 @@ class AppCompatCameraPolicy {
final boolean needsDisplayRotationCompatPolicy =
wmService.mAppCompatConfiguration.isCameraCompatTreatmentEnabledAtBuildTime();
final boolean needsCameraCompatFreeformPolicy =
- Flags.enableCameraCompatForDesktopWindowing()
+ DesktopModeFlags.ENABLE_CAMERA_COMPAT_SIMULATE_REQUESTED_ORIENTATION.isTrue()
&& DesktopModeHelper.canEnterDesktopMode(wmService.mContext);
if (needsDisplayRotationCompatPolicy || needsCameraCompatFreeformPolicy) {
mCameraStateMonitor = new CameraStateMonitor(displayContent, wmService.mH);
diff --git a/services/core/java/com/android/server/wm/DeviceStateAutoRotateSettingIssueLogger.java b/services/core/java/com/android/server/wm/DeviceStateAutoRotateSettingIssueLogger.java
index 937039d838e5..a7d03485058f 100644
--- a/services/core/java/com/android/server/wm/DeviceStateAutoRotateSettingIssueLogger.java
+++ b/services/core/java/com/android/server/wm/DeviceStateAutoRotateSettingIssueLogger.java
@@ -29,13 +29,13 @@ import java.util.function.LongSupplier;
/**
* Logs potential race conditions that lead to incorrect auto-rotate setting.
*
- * Before go/auto-rotate-refactor, there is a race condition that happen during device state
+ * <p>Before go/auto-rotate-refactor, there is a race condition that happen during device state
* changes, as a result, incorrect auto-rotate setting are written for a device state in
* DEVICE_STATE_ROTATION_LOCK. Realistically, users shouldn’t be able to change
* DEVICE_STATE_ROTATION_LOCK while the device folds/unfolds.
*
- * This class monitors the time between a device state change and a subsequent change to the device
- * state based auto-rotate setting. If the duration is less than a threshold
+ * <p>This class monitors the time between a device state change and a subsequent change to the
+ * device state based auto-rotate setting. If the duration is less than a threshold
* (DEVICE_STATE_AUTO_ROTATE_SETTING_ISSUE_THRESHOLD), a potential issue is logged. The logging of
* the atom is not expected to occur often, realistically estimated once a month on few devices.
* But the number could be bigger, as that's what this metric is set to reveal.
@@ -72,23 +72,33 @@ public class DeviceStateAutoRotateSettingIssueLogger {
}
private void onStateChange() {
- // Only move forward if both of the events have occurred already
- if (mLastDeviceStateChangeTime != TIME_NOT_SET
- && mLastDeviceStateAutoRotateSettingChangeTime != TIME_NOT_SET) {
- final long duration =
- mLastDeviceStateAutoRotateSettingChangeTime - mLastDeviceStateChangeTime;
- boolean isDeviceStateChangeFirst = duration > 0;
+ // Only move forward if both of the events have occurred already.
+ if (mLastDeviceStateChangeTime == TIME_NOT_SET
+ || mLastDeviceStateAutoRotateSettingChangeTime == TIME_NOT_SET) {
+ return;
+ }
+ final long duration =
+ mLastDeviceStateAutoRotateSettingChangeTime - mLastDeviceStateChangeTime;
+ boolean isDeviceStateChangeFirst = duration > 0;
- if (abs(duration)
- < DEVICE_STATE_AUTO_ROTATE_SETTING_ISSUE_THRESHOLD_MILLIS) {
- FrameworkStatsLog.write(
- FrameworkStatsLog.DEVICE_STATE_AUTO_ROTATE_SETTING_ISSUE_REPORTED,
- (int) abs(duration),
- isDeviceStateChangeFirst);
- }
+ if (abs(duration)
+ < DEVICE_STATE_AUTO_ROTATE_SETTING_ISSUE_THRESHOLD_MILLIS) {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.DEVICE_STATE_AUTO_ROTATE_SETTING_ISSUE_REPORTED,
+ (int) abs(duration),
+ isDeviceStateChangeFirst);
+ // This pair is logged, reset both timestamps.
mLastDeviceStateAutoRotateSettingChangeTime = TIME_NOT_SET;
mLastDeviceStateChangeTime = TIME_NOT_SET;
+ } else {
+ // This pair was not logged, reset the earlier timestamp.
+ if (isDeviceStateChangeFirst) {
+ mLastDeviceStateChangeTime = TIME_NOT_SET;
+ } else {
+ mLastDeviceStateAutoRotateSettingChangeTime = TIME_NOT_SET;
+ }
}
+
}
}
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index ec8794f8073f..017284cded8e 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -1978,6 +1978,11 @@ NativeInputManager::interceptKeyBeforeDispatching(const sp<IBinder>& token,
return inputdispatcher::KeyEntry::InterceptKeyResult::SKIP;
}
+ // -2 : Skip sending even to application and go directly to post processing e.g. fallbacks.
+ if (delayMillis == -2) {
+ return inputdispatcher::KeyEntry::InterceptKeyResult::FALLBACK;
+ }
+
return milliseconds_to_nanoseconds(delayMillis);
}
diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
index 232bb83fdf9f..5a140d53a4d8 100644
--- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
@@ -1753,6 +1753,13 @@ class AppIdPermissionPolicy : SchemePolicy() {
}
val appIdPermissionFlags = newState.mutateUserState(userId)!!.mutateAppIdPermissionFlags()
val permissionFlags = appIdPermissionFlags.mutateOrPut(appId) { MutableIndexedMap() }
+ // for debugging possible races TODO(b/401768134)
+ oldState.userStates[userId]?.appIdPermissionFlags[appId]?.map?.let {
+ if (permissionFlags.map === it) {
+ throw IllegalStateException("Unexpected sharing between old/new state")
+ }
+ }
+
permissionFlags.putWithDefault(permissionName, newFlags, 0)
if (permissionFlags.isEmpty()) {
appIdPermissionFlags -= appId
diff --git a/services/supervision/java/com/android/server/supervision/SupervisionService.java b/services/supervision/java/com/android/server/supervision/SupervisionService.java
index bb6339c79502..0b5a95b0e888 100644
--- a/services/supervision/java/com/android/server/supervision/SupervisionService.java
+++ b/services/supervision/java/com/android/server/supervision/SupervisionService.java
@@ -27,6 +27,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
+import android.app.KeyguardManager;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.supervision.ISupervisionManager;
@@ -147,9 +148,21 @@ public class SupervisionService extends ISupervisionManager.Stub {
@Override
@Nullable
public Intent createConfirmSupervisionCredentialsIntent() {
- // TODO(b/392961554): (1) Return null if supervision is not enabled.
- // (2) check if PIN exists before return a valid intent.
enforceAnyPermission(QUERY_USERS, MANAGE_USERS);
+ if (!isSupervisionEnabledForUser(mContext.getUserId())) {
+ return null;
+ }
+ // Verify the supervising user profile exists and has a secure credential set.
+ final int supervisingUserId = mInjector.getUserManagerInternal().getSupervisingProfileId();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (supervisingUserId == UserHandle.USER_NULL
+ || !mInjector.getKeyguardManager().isDeviceSecure(supervisingUserId)) {
+ return null;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
final Intent intent = new Intent(ACTION_CONFIRM_SUPERVISION_CREDENTIALS);
// explicitly set the package for security
intent.setPackage("com.android.settings");
@@ -277,6 +290,7 @@ public class SupervisionService extends ISupervisionManager.Stub {
static class Injector {
private final Context mContext;
private DevicePolicyManagerInternal mDpmInternal;
+ private KeyguardManager mKeyguardManager;
private PackageManager mPackageManager;
private UserManagerInternal mUserManagerInternal;
@@ -292,6 +306,13 @@ public class SupervisionService extends ISupervisionManager.Stub {
return mDpmInternal;
}
+ KeyguardManager getKeyguardManager() {
+ if (mKeyguardManager == null) {
+ mKeyguardManager = mContext.getSystemService(KeyguardManager.class);
+ }
+ return mKeyguardManager;
+ }
+
PackageManager getPackageManager() {
if (mPackageManager == null) {
mPackageManager = mContext.getPackageManager();
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java
index 3289d70b89ac..fe4baeb80ee7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java
@@ -79,7 +79,8 @@ import java.util.ArrayList;
public class ApplicationStartInfoTest {
private static final String TAG = ApplicationStartInfoTest.class.getSimpleName();
- private static final ComponentName COMPONENT = new ComponentName("com.android.test", ".Foo");
+ private static final ComponentName COMPONENT =
+ new ComponentName("com.android.test", "com.android.test.Foo");
private static final int APP_1_UID = 10123;
private static final int APP_1_PID_1 = 12345;
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 067fba9893e5..64e6d323bdfd 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -93,7 +93,6 @@ android_test {
"net_flags_lib",
"CtsVirtualDeviceCommonLib",
"com_android_server_accessibility_flags_lib",
- "locksettings_flags_lib",
] + select(soong_config_variable("ANDROID", "release_crashrecovery_module"), {
"true": ["service-crashrecovery-pre-jarjar"],
default: [],
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index e0bc3e76f31d..457d8a96fea4 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -87,6 +87,9 @@ import android.content.res.XmlResourceParser;
import android.graphics.drawable.Icon;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerGlobal;
+import android.hardware.input.IInputManager;
+import android.hardware.input.InputManager;
+import android.hardware.input.InputManagerGlobal;
import android.hardware.input.KeyGestureEvent;
import android.net.Uri;
import android.os.Build;
@@ -237,6 +240,9 @@ public class AccessibilityManagerServiceTest {
@Mock
private HearingDevicePhoneCallNotificationController
mMockHearingDevicePhoneCallNotificationController;
+ @Mock
+ private IInputManager mMockInputManagerService;
+ private InputManagerGlobal.TestSession mInputManagerTestSession;
@Spy private IUserInitializationCompleteCallback mUserInitializationCompleteCallback;
@Captor private ArgumentCaptor<Intent> mIntentArgumentCaptor;
private IAccessibilityManager mA11yManagerServiceOnDevice;
@@ -270,6 +276,10 @@ public class AccessibilityManagerServiceTest {
mInputFilter = mock(FakeInputFilter.class);
mTestableContext.addMockSystemService(DevicePolicyManager.class, mDevicePolicyManager);
+ mInputManagerTestSession = InputManagerGlobal.createTestSession(mMockInputManagerService);
+ InputManager mockInputManager = new InputManager(mTestableContext);
+ mTestableContext.addMockSystemService(InputManager.class, mockInputManager);
+
when(mMockPackageManagerInternal.getSystemUiServiceComponent()).thenReturn(
new ComponentName("com.android.systemui", "com.android.systemui.SystemUIService"));
when(mMockPackageManagerInternal.getPackageUid(eq("com.android.systemui"), anyLong(),
@@ -334,6 +344,9 @@ public class AccessibilityManagerServiceTest {
FieldSetter.setField(
am, AccessibilityManager.class.getDeclaredField("mService"),
mA11yManagerServiceOnDevice);
+ if (mInputManagerTestSession != null) {
+ mInputManagerTestSession.close();
+ }
}
private void setupAccessibilityServiceConnection(int serviceInfoFlag) {
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioVolumeChangeHandlerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioVolumeChangeHandlerTest.java
new file mode 100644
index 000000000000..f252a9848bbf
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioVolumeChangeHandlerTest.java
@@ -0,0 +1,142 @@
+/*
+ * 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.server.audio;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.media.INativeAudioVolumeGroupCallback;
+import android.media.audio.common.AudioVolumeGroupChangeEvent;
+import android.media.audiopolicy.IAudioVolumeChangeDispatcher;
+import android.os.IBinder;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@MediumTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class AudioVolumeChangeHandlerTest {
+ private static final long DEFAULT_TIMEOUT_MS = 1000;
+
+ private AudioSystemAdapter mSpyAudioSystem;
+
+ AudioVolumeChangeHandler mAudioVolumeChangedHandler;
+
+ private final IAudioVolumeChangeDispatcher.Stub mMockDispatcher =
+ mock(IAudioVolumeChangeDispatcher.Stub.class);
+
+ @Before
+ public void setUp() {
+ mSpyAudioSystem = spy(new NoOpAudioSystemAdapter());
+ when(mMockDispatcher.asBinder()).thenReturn(mock(IBinder.class));
+ mAudioVolumeChangedHandler = new AudioVolumeChangeHandler(mSpyAudioSystem);
+ }
+
+ @Test
+ public void registerListener_withInvalidCallback() {
+ IAudioVolumeChangeDispatcher.Stub nullCb = null;
+ NullPointerException thrown = assertThrows(NullPointerException.class, () -> {
+ mAudioVolumeChangedHandler.registerListener(nullCb);
+ });
+
+ assertWithMessage("Exception for invalid registration").that(thrown).hasMessageThat()
+ .contains("Volume group callback");
+ }
+
+ @Test
+ public void unregisterListener_withInvalidCallback() {
+ IAudioVolumeChangeDispatcher.Stub nullCb = null;
+ mAudioVolumeChangedHandler.registerListener(mMockDispatcher);
+
+ NullPointerException thrown = assertThrows(NullPointerException.class, () -> {
+ mAudioVolumeChangedHandler.unregisterListener(nullCb);
+ });
+
+ assertWithMessage("Exception for invalid un-registration").that(thrown).hasMessageThat()
+ .contains("Volume group callback");
+ }
+
+ @Test
+ public void registerListener() {
+ mAudioVolumeChangedHandler.registerListener(mMockDispatcher);
+
+ verify(mSpyAudioSystem).registerAudioVolumeGroupCallback(any());
+ }
+
+ @Test
+ public void onAudioVolumeGroupChanged() throws Exception {
+ mAudioVolumeChangedHandler.registerListener(mMockDispatcher);
+ AudioVolumeGroupChangeEvent volEvent = new AudioVolumeGroupChangeEvent();
+ volEvent.groupId = 666;
+ volEvent.flags = AudioVolumeGroupChangeEvent.VOLUME_FLAG_FROM_KEY;
+
+ captureRegisteredNativeCallback().onAudioVolumeGroupChanged(volEvent);
+
+ verify(mMockDispatcher, timeout(DEFAULT_TIMEOUT_MS)).onAudioVolumeGroupChanged(
+ eq(volEvent.groupId), eq(volEvent.flags));
+ }
+
+ @Test
+ public void onAudioVolumeGroupChanged_withMultipleCallback() throws Exception {
+ int callbackCount = 10;
+ List<IAudioVolumeChangeDispatcher.Stub> validCbs =
+ new ArrayList<IAudioVolumeChangeDispatcher.Stub>();
+ for (int i = 0; i < callbackCount; i++) {
+ IAudioVolumeChangeDispatcher.Stub cb = mock(IAudioVolumeChangeDispatcher.Stub.class);
+ when(cb.asBinder()).thenReturn(mock(IBinder.class));
+ validCbs.add(cb);
+ }
+ for (IAudioVolumeChangeDispatcher.Stub cb : validCbs) {
+ mAudioVolumeChangedHandler.registerListener(cb);
+ }
+ AudioVolumeGroupChangeEvent volEvent = new AudioVolumeGroupChangeEvent();
+ volEvent.groupId = 666;
+ volEvent.flags = AudioVolumeGroupChangeEvent.VOLUME_FLAG_FROM_KEY;
+ captureRegisteredNativeCallback().onAudioVolumeGroupChanged(volEvent);
+
+ for (IAudioVolumeChangeDispatcher.Stub cb : validCbs) {
+ verify(cb, timeout(DEFAULT_TIMEOUT_MS)).onAudioVolumeGroupChanged(
+ eq(volEvent.groupId), eq(volEvent.flags));
+ }
+ }
+
+ private INativeAudioVolumeGroupCallback captureRegisteredNativeCallback() {
+ ArgumentCaptor<INativeAudioVolumeGroupCallback> nativeAudioVolumeGroupCallbackCaptor =
+ ArgumentCaptor.forClass(INativeAudioVolumeGroupCallback.class);
+ verify(mSpyAudioSystem, timeout(DEFAULT_TIMEOUT_MS))
+ .registerAudioVolumeGroupCallback(nativeAudioVolumeGroupCallbackCaptor.capture());
+ return nativeAudioVolumeGroupCallbackCaptor.getValue();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
index ae781dcb834a..52c7c48cc8b7 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
@@ -60,10 +60,6 @@ import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -74,7 +70,6 @@ import com.android.server.locksettings.ResumeOnRebootServiceProvider.ResumeOnReb
import com.android.server.pm.UserManagerInternal;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -113,9 +108,6 @@ public class RebootEscrowManagerTests {
0x26, 0x52, 0x72, 0x63, 0x63, 0x61, 0x78, 0x23,
};
- @Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
private Context mContext;
private UserManager mUserManager;
private UserManagerInternal mUserManagerInternal;
@@ -847,53 +839,6 @@ public class RebootEscrowManagerTests {
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_WAIT_FOR_INTERNET_ROR)
- public void loadRebootEscrowDataIfAvailable_ServerBasedIoError_RetryFailure() throws Exception {
- setServerBasedRebootEscrowProvider();
-
- when(mInjected.getBootCount()).thenReturn(0);
- RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
- mService.setRebootEscrowListener(mockListener);
- mService.prepareRebootEscrow();
-
- clearInvocations(mServiceConnection);
- callToRebootEscrowIfNeededAndWait(PRIMARY_USER_ID);
- verify(mockListener).onPreparedForReboot(eq(true));
- verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
-
- // Use x -> x for both wrap & unwrap functions.
- when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
- .thenAnswer(invocation -> invocation.getArgument(0));
- assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded());
- verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
- assertTrue(mStorage.hasRebootEscrowServerBlob());
-
- // pretend reboot happens here
- when(mInjected.getBootCount()).thenReturn(1);
- ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
- ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class);
- doNothing()
- .when(mInjected)
- .reportMetric(
- metricsSuccessCaptor.capture(),
- metricsErrorCodeCaptor.capture(),
- eq(2) /* Server based */,
- eq(2) /* attempt count */,
- anyInt(),
- eq(0) /* vbmeta status */,
- anyInt());
- when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(IOException.class);
-
- mService.loadRebootEscrowDataIfAvailable(mHandler);
- // Sleep 5s for the retry to complete
- Thread.sleep(5 * 1000);
- assertFalse(metricsSuccessCaptor.getValue());
- assertEquals(
- Integer.valueOf(RebootEscrowManager.ERROR_NO_NETWORK),
- metricsErrorCodeCaptor.getValue());
- }
-
- @Test
public void loadRebootEscrowDataIfAvailable_ServerBased_RetrySuccess() throws Exception {
setServerBasedRebootEscrowProvider();
@@ -941,7 +886,6 @@ public class RebootEscrowManagerTests {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_WAIT_FOR_INTERNET_ROR)
public void loadRebootEscrowDataIfAvailable_waitForInternet_networkUnavailable()
throws Exception {
setServerBasedRebootEscrowProvider();
@@ -989,7 +933,6 @@ public class RebootEscrowManagerTests {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_WAIT_FOR_INTERNET_ROR)
public void loadRebootEscrowDataIfAvailable_waitForInternet_networkLost() throws Exception {
setServerBasedRebootEscrowProvider();
@@ -1044,7 +987,6 @@ public class RebootEscrowManagerTests {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_WAIT_FOR_INTERNET_ROR)
public void loadRebootEscrowDataIfAvailable_waitForInternet_networkAvailableWithDelay()
throws Exception {
setServerBasedRebootEscrowProvider();
@@ -1103,7 +1045,6 @@ public class RebootEscrowManagerTests {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_WAIT_FOR_INTERNET_ROR)
public void loadRebootEscrowDataIfAvailable_waitForInternet_timeoutExhausted()
throws Exception {
setServerBasedRebootEscrowProvider();
@@ -1163,7 +1104,6 @@ public class RebootEscrowManagerTests {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_WAIT_FOR_INTERNET_ROR)
public void loadRebootEscrowDataIfAvailable_serverBasedWaitForNetwork_retryCountExhausted()
throws Exception {
setServerBasedRebootEscrowProvider();
@@ -1219,7 +1159,6 @@ public class RebootEscrowManagerTests {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_WAIT_FOR_INTERNET_ROR)
public void loadRebootEscrowDataIfAvailable_ServerBasedWaitForInternet_RetrySuccess()
throws Exception {
setServerBasedRebootEscrowProvider();
diff --git a/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt b/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt
index fbf906586e8b..c59f0a05c619 100644
--- a/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt
+++ b/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt
@@ -17,6 +17,7 @@
package com.android.server.supervision
import android.app.Activity
+import android.app.KeyguardManager
import android.app.admin.DevicePolicyManager
import android.app.admin.DevicePolicyManagerInternal
import android.app.supervision.flags.Flags
@@ -61,6 +62,9 @@ class SupervisionServiceTest {
@get:Rule val mocks: MockitoRule = MockitoJUnit.rule()
@Mock private lateinit var mockDpmInternal: DevicePolicyManagerInternal
+
+ @Mock
+ private lateinit var mockKeyguardManager: KeyguardManager
@Mock private lateinit var mockPackageManager: PackageManager
@Mock private lateinit var mockUserManagerInternal: UserManagerInternal
@@ -71,7 +75,7 @@ class SupervisionServiceTest {
@Before
fun setUp() {
context = InstrumentationRegistry.getInstrumentation().context
- context = SupervisionContextWrapper(context, mockPackageManager)
+ context = SupervisionContextWrapper(context, mockKeyguardManager, mockPackageManager)
LocalServices.removeServiceForTest(DevicePolicyManagerInternal::class.java)
LocalServices.addService(DevicePolicyManagerInternal::class.java, mockDpmInternal)
@@ -250,11 +254,41 @@ class SupervisionServiceTest {
@Test
fun createConfirmSupervisionCredentialsIntent() {
+ service.mInternal.setSupervisionEnabledForUser(context.getUserId(), true)
+ whenever(mockUserManagerInternal.getSupervisingProfileId()).thenReturn(SUPERVISING_USER_ID)
+ whenever(mockKeyguardManager.isDeviceSecure(SUPERVISING_USER_ID)).thenReturn(true)
+
val intent = checkNotNull(service.createConfirmSupervisionCredentialsIntent())
assertThat(intent.action).isEqualTo(ACTION_CONFIRM_SUPERVISION_CREDENTIALS)
assertThat(intent.getPackage()).isEqualTo("com.android.settings")
}
+ @Test
+ fun createConfirmSupervisionCredentialsIntent_supervisionNotEnabled_returnsNull() {
+ service.mInternal.setSupervisionEnabledForUser(context.getUserId(), false)
+ whenever(mockUserManagerInternal.getSupervisingProfileId()).thenReturn(SUPERVISING_USER_ID)
+ whenever(mockKeyguardManager.isDeviceSecure(SUPERVISING_USER_ID)).thenReturn(true)
+
+ assertThat(service.createConfirmSupervisionCredentialsIntent()).isNull()
+ }
+
+ @Test
+ fun createConfirmSupervisionCredentialsIntent_noSupervisingUser_returnsNull() {
+ service.mInternal.setSupervisionEnabledForUser(context.getUserId(), true)
+ whenever(mockUserManagerInternal.getSupervisingProfileId()).thenReturn(UserHandle.USER_NULL)
+
+ assertThat(service.createConfirmSupervisionCredentialsIntent()).isNull()
+ }
+
+ @Test
+ fun createConfirmSupervisionCredentialsIntent_supervisingUserMissingSecureLock_returnsNull() {
+ service.mInternal.setSupervisionEnabledForUser(context.getUserId(), true)
+ whenever(mockUserManagerInternal.getSupervisingProfileId()).thenReturn(SUPERVISING_USER_ID)
+ whenever(mockKeyguardManager.isDeviceSecure(SUPERVISING_USER_ID)).thenReturn(false)
+
+ assertThat(service.createConfirmSupervisionCredentialsIntent()).isNull()
+ }
+
private val systemSupervisionPackage: String
get() = context.getResources().getString(R.string.config_systemSupervision)
@@ -279,6 +313,7 @@ class SupervisionServiceTest {
private companion object {
const val USER_ID = 100
const val APP_UID = USER_ID * UserHandle.PER_USER_RANGE
+ const val SUPERVISING_USER_ID = 10
}
}
@@ -286,10 +321,19 @@ class SupervisionServiceTest {
* A context wrapper that allows broadcast intents to immediately invoke the receivers without
* performing checks on the sending user.
*/
-private class SupervisionContextWrapper(val context: Context, val pkgManager: PackageManager) :
- ContextWrapper(context) {
+private class SupervisionContextWrapper(
+ val context: Context,
+ val keyguardManager: KeyguardManager,
+ val pkgManager: PackageManager,
+) : ContextWrapper(context) {
val interceptors = mutableListOf<Pair<BroadcastReceiver, IntentFilter>>()
+ override fun getSystemService(name: String): Any =
+ when (name) {
+ Context.KEYGUARD_SERVICE -> keyguardManager
+ else -> super.getSystemService(name)
+ }
+
override fun getPackageManager() = pkgManager
override fun registerReceiverForAllUsers(
diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
index 857a9767d9ee..842c441e09f2 100644
--- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
@@ -452,14 +452,14 @@ public class SystemConfigTest {
+ " <library \n"
+ " name=\"foo\"\n"
+ " file=\"" + mFooJar + "\"\n"
- + " on-bootclasspath-before=\"A\"\n"
+ + " on-bootclasspath-before=\"Q\"\n"
+ " on-bootclasspath-since=\"W\"\n"
+ " />\n\n"
+ " </permissions>";
parseSharedLibraries(contents);
assertFooIsOnlySharedLibrary();
SystemConfig.SharedLibraryEntry entry = mSysConfig.getSharedLibraries().get("foo");
- assertThat(entry.onBootclasspathBefore).isEqualTo("A");
+ assertThat(entry.onBootclasspathBefore).isEqualTo("Q");
assertThat(entry.onBootclasspathSince).isEqualTo("W");
}
diff --git a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
index f3d5e39ec127..8d164e1acf74 100644
--- a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
@@ -53,14 +53,16 @@ import android.app.AppOpsManager;
import android.content.Context;
import android.hardware.input.InputManager;
import android.os.Bundle;
+import android.os.IBinder;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.service.dreams.DreamManagerInternal;
import android.testing.TestableContext;
-import android.view.contentprotection.flags.Flags;
+import android.view.KeyEvent;
import androidx.test.filters.SmallTest;
@@ -102,6 +104,8 @@ public class PhoneWindowManagerTests {
public final TestableContext mContext = spy(
new TestableContext(getInstrumentation().getContext()));
+ @Mock private IBinder mInputToken;
+
PhoneWindowManager mPhoneWindowManager;
@Mock
private ActivityTaskManagerInternal mAtmInternal;
@@ -125,6 +129,8 @@ public class PhoneWindowManagerTests {
@Mock
private LockPatternUtils mLockPatternUtils;
+ private static final int INTERCEPT_SYSTEM_KEY_NOT_CONSUMED_DELAY = 0;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -216,7 +222,7 @@ public class PhoneWindowManagerTests {
@Test
public void testCheckAddPermission_withoutAccessibilityOverlay_noAccessibilityAppOpLogged() {
- mSetFlagsRule.enableFlags(Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED);
+ mSetFlagsRule.enableFlags(android.view.contentprotection.flags.Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED);
int[] outAppOp = new int[1];
assertEquals(ADD_OKAY, mPhoneWindowManager.checkAddPermission(TYPE_WALLPAPER,
/* isRoundedCornerOverlay= */ false, "test.pkg", outAppOp, DEFAULT_DISPLAY));
@@ -225,7 +231,7 @@ public class PhoneWindowManagerTests {
@Test
public void testCheckAddPermission_withAccessibilityOverlay() {
- mSetFlagsRule.enableFlags(Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED);
+ mSetFlagsRule.enableFlags(android.view.contentprotection.flags.Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED);
int[] outAppOp = new int[1];
assertEquals(ADD_OKAY, mPhoneWindowManager.checkAddPermission(TYPE_ACCESSIBILITY_OVERLAY,
/* isRoundedCornerOverlay= */ false, "test.pkg", outAppOp, DEFAULT_DISPLAY));
@@ -234,7 +240,7 @@ public class PhoneWindowManagerTests {
@Test
public void testCheckAddPermission_withAccessibilityOverlay_flagDisabled() {
- mSetFlagsRule.disableFlags(Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED);
+ mSetFlagsRule.disableFlags(android.view.contentprotection.flags.Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED);
int[] outAppOp = new int[1];
assertEquals(ADD_OKAY, mPhoneWindowManager.checkAddPermission(TYPE_ACCESSIBILITY_OVERLAY,
/* isRoundedCornerOverlay= */ false, "test.pkg", outAppOp, DEFAULT_DISPLAY));
@@ -401,6 +407,35 @@ public class PhoneWindowManagerTests {
verify(mDreamManagerInternal).requestDream();
}
+ @EnableFlags(com.android.hardware.input.Flags.FLAG_FIX_SEARCH_MODIFIER_FALLBACKS)
+ public void testInterceptKeyBeforeDispatching() {
+ // Handle sub-tasks of init().
+ doNothing().when(mPhoneWindowManager).updateSettings(any());
+ doNothing().when(mPhoneWindowManager).initializeHdmiState();
+ final DisplayPolicy displayPolicy = mock(DisplayPolicy.class);
+ mPhoneWindowManager.mDefaultDisplayPolicy = displayPolicy;
+ mPhoneWindowManager.mDefaultDisplayRotation = mock(DisplayRotation.class);
+ final PowerManager pm = mock(PowerManager.class);
+ doReturn(true).when(pm).isInteractive();
+ doReturn(pm).when(mContext).getSystemService(eq(Context.POWER_SERVICE));
+
+ mContext.getMainThreadHandler().runWithScissors(() -> mPhoneWindowManager.init(
+ new PhoneWindowManager.Injector(mContext,
+ mock(WindowManagerPolicy.WindowManagerFuncs.class))), 0);
+
+ // Case: KeyNotConsumed with meta key.
+ KeyEvent keyEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN,
+ KeyEvent.KEYCODE_A, 0, KeyEvent.META_META_ON);
+ long result = mPhoneWindowManager.interceptKeyBeforeDispatching(mInputToken, keyEvent, 0);
+ assertEquals(INTERCEPT_SYSTEM_KEY_NOT_CONSUMED_DELAY, result);
+
+ // Case: KeyNotConsumed without meta key.
+ KeyEvent keyEvent1 = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN,
+ KeyEvent.KEYCODE_ESCAPE, 0, 0);
+ long result1 = mPhoneWindowManager.interceptKeyBeforeDispatching(mInputToken, keyEvent1, 0);
+ assertEquals(INTERCEPT_SYSTEM_KEY_NOT_CONSUMED_DELAY, result1);
+ }
+
private void initPhoneWindowManager() {
mPhoneWindowManager.mDefaultDisplayPolicy = mDisplayPolicy;
mPhoneWindowManager.mDefaultDisplayRotation = mock(DisplayRotation.class);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DeviceStateAutoRotateSettingIssueLoggerTests.java b/services/tests/wmtests/src/com/android/server/wm/DeviceStateAutoRotateSettingIssueLoggerTests.java
index f76a9cdbb894..ba9bf1bf8045 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DeviceStateAutoRotateSettingIssueLoggerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DeviceStateAutoRotateSettingIssueLoggerTests.java
@@ -24,6 +24,7 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import android.platform.test.annotations.Presubmit;
@@ -143,4 +144,46 @@ public class DeviceStateAutoRotateSettingIssueLoggerTests {
anyInt(),
anyBoolean()), never());
}
+
+ @Test
+ public void onStateChange_issueOccurredSettingChangedTwice_reportOnlyOnce() {
+ mDeviceStateAutoRotateSettingIssueLogger.onDeviceStateAutoRotateSettingChange();
+ mDeviceStateAutoRotateSettingIssueLogger.onDeviceStateChange();
+ mDeviceStateAutoRotateSettingIssueLogger.onDeviceStateAutoRotateSettingChange();
+
+ verify(() ->
+ FrameworkStatsLog.write(
+ eq(FrameworkStatsLog.DEVICE_STATE_AUTO_ROTATE_SETTING_ISSUE_REPORTED),
+ anyInt(),
+ anyBoolean()), times(1));
+ }
+
+ @Test
+ public void onStateChange_issueOccurredDeviceStateChangedTwice_reportOnlyOnce() {
+ mDeviceStateAutoRotateSettingIssueLogger.onDeviceStateChange();
+ mDeviceStateAutoRotateSettingIssueLogger.onDeviceStateAutoRotateSettingChange();
+ mDeviceStateAutoRotateSettingIssueLogger.onDeviceStateChange();
+
+ verify(() ->
+ FrameworkStatsLog.write(
+ eq(FrameworkStatsLog.DEVICE_STATE_AUTO_ROTATE_SETTING_ISSUE_REPORTED),
+ anyInt(),
+ anyBoolean()), times(1));
+ }
+
+ @Test
+ public void onStateChange_issueOccurredAfterDelay_reportOnce() {
+ mDeviceStateAutoRotateSettingIssueLogger.onDeviceStateAutoRotateSettingChange();
+ mTestTimeSupplier.delay(
+ DEVICE_STATE_AUTO_ROTATE_SETTING_ISSUE_THRESHOLD_MILLIS + DELAY);
+ mDeviceStateAutoRotateSettingIssueLogger.onDeviceStateChange();
+ mTestTimeSupplier.delay(DELAY);
+ mDeviceStateAutoRotateSettingIssueLogger.onDeviceStateAutoRotateSettingChange();
+
+ verify(() ->
+ FrameworkStatsLog.write(
+ eq(FrameworkStatsLog.DEVICE_STATE_AUTO_ROTATE_SETTING_ISSUE_REPORTED),
+ eq(DELAY),
+ anyBoolean()), times(1));
+ }
}
diff --git a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
index 7ec8f9ce9864..71c7a6b1119d 100644
--- a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
+++ b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
@@ -35,8 +35,8 @@ import android.os.PermissionEnforcer
import android.os.SystemClock
import android.os.test.FakePermissionEnforcer
import android.os.test.TestLooper
-import android.platform.test.annotations.Presubmit
import android.platform.test.annotations.EnableFlags
+import android.platform.test.annotations.Presubmit
import android.platform.test.flag.junit.SetFlagsRule
import android.provider.Settings
import android.view.View.OnKeyListener
@@ -373,6 +373,29 @@ class InputManagerServiceTests {
}
@Test
+ @EnableFlags(com.android.hardware.input.Flags.FLAG_FIX_SEARCH_MODIFIER_FALLBACKS)
+ fun testInterceptKeyBeforeDispatchingWithFallthroughEvent() {
+ service.systemRunning()
+ overrideSendActionKeyEventsToFocusedWindow(
+ /* hasPermission = */false,
+ /* hasPrivateFlag = */false
+ )
+ whenever(wmCallbacks.interceptKeyBeforeDispatching(any(), any(), anyInt())).thenReturn(0)
+
+ // Create a fallback for a key event with a meta modifier. Should result in -2,
+ // which represents the fallback event, which indicates that original key event will
+ // be ignored (not sent to app) and instead the fallback will be created and sent to the
+ // app.
+ val fallbackAction: KeyCharacterMap.FallbackAction = KeyCharacterMap.FallbackAction.obtain()
+ fallbackAction.keyCode = KeyEvent.KEYCODE_SEARCH
+ whenever(kcm.getFallbackAction(anyInt(), anyInt())).thenReturn(fallbackAction)
+
+ val event = KeyEvent( /* downTime= */0, /* eventTime= */0, KeyEvent.ACTION_DOWN,
+ KeyEvent.KEYCODE_SPACE, /* repeat= */0, KeyEvent.META_META_ON)
+ assertEquals(-2, service.interceptKeyBeforeDispatching(null, event, 0))
+ }
+
+ @Test
fun testKeyEventsNotForwardedToFocusedWindow_whenWmConsumes() {
service.systemRunning()
overrideSendActionKeyEventsToFocusedWindow(
diff --git a/tools/protologtool/src/com/android/protolog/tool/exceptions.kt b/tools/protologtool/src/com/android/protolog/tool/Exceptions.kt
index ae00df123353..4f7e8d13c4ba 100644
--- a/tools/protologtool/src/com/android/protolog/tool/exceptions.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/Exceptions.kt
@@ -18,21 +18,24 @@ package com.android.protolog.tool
import java.lang.Exception
-open class CodeProcessingException(message: String, context: ParsingContext)
- : Exception("Code processing error in ${context.filePath}:${context.lineNumber}:\n" +
- " $message")
+open class CodeProcessingException(
+ message: String, context: ParsingContext, cause: Throwable? = null
+) : Exception("Code processing error in ${context.filePath}:${context.lineNumber}:\n" +
+ " $message", cause)
-class HashCollisionException(message: String, context: ParsingContext) :
- CodeProcessingException(message, context)
+class HashCollisionException(
+ message: String, context: ParsingContext, cause: Throwable? = null
+) : CodeProcessingException(message, context, cause)
-class IllegalImportException(message: String, context: ParsingContext) :
- CodeProcessingException("Illegal import: $message", context)
+class IllegalImportException(message: String, context: ParsingContext, cause: Throwable? = null) :
+ CodeProcessingException("Illegal import: $message", context, cause)
-class InvalidProtoLogCallException(message: String, context: ParsingContext)
- : CodeProcessingException("InvalidProtoLogCall: $message", context)
+class InvalidProtoLogCallException(
+ message: String, context: ParsingContext, cause: Throwable? = null
+) : CodeProcessingException("InvalidProtoLogCall: $message", context, cause)
-class ParsingException(message: String, context: ParsingContext)
- : CodeProcessingException(message, context)
+class ParsingException(message: String, context: ParsingContext, cause: Throwable? = null) :
+ CodeProcessingException(message, context, cause)
class InvalidViewerConfigException(message: String) : Exception(message)
diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessorImpl.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessorImpl.kt
index 272d8bb1793d..1f6db5fe2d37 100644
--- a/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessorImpl.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessorImpl.kt
@@ -118,9 +118,17 @@ class ProtoLogCallProcessorImpl(
"- not a ProtoLogGroup enum member: $call", context)
}
- logCallVisitor?.processCall(call, messageString, getLevelForMethodName(
- call.name.toString(), call, context), groupMap.getValue(groupName),
- context.lineNumber)
+ try {
+ logCallVisitor?.processCall(
+ call, messageString, getLevelForMethodName(
+ call.name.toString(), call, context
+ ), groupMap.getValue(groupName),
+ context.lineNumber
+ )
+ } catch (e: Throwable) {
+ throw InvalidProtoLogCallException("Error processing log call: $call",
+ context, e)
+ }
} else if (call.name.id == "init") {
// No processing
} else {
diff --git a/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorImplTest.kt b/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorImplTest.kt
index 004d97babbad..732824ae841c 100644
--- a/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorImplTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorImplTest.kt
@@ -16,11 +16,14 @@
package com.android.protolog.tool
+import com.android.internal.protolog.common.InvalidFormatStringException
import com.android.internal.protolog.common.LogLevel
import com.github.javaparser.StaticJavaParser
import com.github.javaparser.ast.expr.MethodCallExpr
import org.junit.Assert.assertEquals
+import org.junit.Assert.assertThrows
import org.junit.Test
+import com.google.common.truth.Truth
class ProtoLogCallProcessorImplTest {
private data class LogCall(
@@ -228,4 +231,38 @@ class ProtoLogCallProcessorImplTest {
visitor.process(StaticJavaParser.parse(code), processor, "")
checkCalls()
}
+
+ @Test
+ fun throws_clear_error_message_on_invalid_format_exception() {
+ val code = """
+ package org.example;
+
+ class Test {
+ void test() {
+ ProtoLog.d(ProtoLogGroup.TEST, "Invalid message %9 %");
+ }
+ }
+ """
+ groupMap["TEST"] = LogGroup("TEST", false, true, "WindowManager")
+
+ val processor = object : ProtoLogCallVisitor {
+ override fun processCall(
+ call: MethodCallExpr,
+ messageString: String,
+ level: LogLevel,
+ group: LogGroup,
+ lineNumber: Int,
+ ) {
+ throw InvalidFormatStringException("Invalid Protolog message format")
+ }
+ }
+
+ val exception = assertThrows(InvalidProtoLogCallException::class.java) {
+ visitor.process(StaticJavaParser.parse(code), processor, "MyTestFile.java")
+ }
+ Truth.assertThat(exception).hasMessageThat()
+ .contains("Code processing error in MyTestFile.java:6")
+ Truth.assertThat(exception.cause).hasMessageThat()
+ .contains("Invalid Protolog message format")
+ }
}