summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AconfigFlags.bp5
-rw-r--r--api/StubLibraries.bp6
-rw-r--r--core/api/current.txt17
-rw-r--r--core/api/system-current.txt7
-rw-r--r--core/java/android/app/ActivityTaskManager.java14
-rw-r--r--core/java/android/app/IActivityTaskManager.aidl6
-rw-r--r--core/java/android/app/StatusBarManager.java57
-rw-r--r--core/java/android/app/UiAutomation.java102
-rw-r--r--core/java/android/app/notification.aconfig14
-rw-r--r--core/java/android/app/supervision/flags.aconfig16
-rw-r--r--core/java/android/content/pm/UserInfo.java6
-rw-r--r--core/java/android/hardware/usb/OWNERS6
-rw-r--r--core/java/android/inputmethodservice/NavigationBarController.java6
-rw-r--r--core/java/android/inputmethodservice/navigationbar/NavigationBarView.java22
-rw-r--r--core/java/android/os/Binder.java16
-rw-r--r--core/java/android/os/CombinedMessageQueue/MessageQueue.java43
-rw-r--r--core/java/android/os/UidBatteryConsumer.java2
-rw-r--r--core/java/android/os/UserManager.java14
-rw-r--r--core/java/android/service/notification/Adjustment.java9
-rw-r--r--core/java/android/service/notification/NotificationListenerService.java24
-rw-r--r--core/java/android/service/notification/StatusBarNotification.java7
-rw-r--r--core/java/android/text/StaticLayout.java1
-rw-r--r--core/java/android/window/DesktopExperienceFlags.java124
-rw-r--r--core/java/android/window/DesktopModeFlags.java9
-rw-r--r--core/java/android/window/OWNERS1
-rw-r--r--core/java/android/window/flags/lse_desktop_experience.aconfig20
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBar.aidl8
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBarService.aidl8
-rw-r--r--core/jni/android_util_Binder.cpp34
-rw-r--r--core/res/res/values/config.xml4
-rw-r--r--core/res/res/values/public-final.xml82
-rw-r--r--core/res/res/values/public-staging.xml69
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java20
-rw-r--r--core/tests/coretests/src/android/window/DesktopExperienceFlagsTest.java164
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt4
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java2
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java40
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java67
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java49
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java319
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubblesListener.aidl12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java54
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java89
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java22
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java50
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java46
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java40
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt5
-rw-r--r--libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromAllAppsLandscape.kt44
-rw-r--r--libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromAllAppsPortrait.kt44
-rw-r--r--libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromTaskbarLandscape.kt44
-rw-r--r--libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromTaskbarPortrait.kt44
-rw-r--r--libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppFromAllApps.kt10
-rw-r--r--libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/CopyContentInSplit.kt2
-rw-r--r--libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByDivider.kt2
-rw-r--r--libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByGoHome.kt2
-rw-r--r--libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DragDividerToResize.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenAutoEnterPipOnGoToHomeTest.kt3
-rw-r--r--libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java182
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java19
-rw-r--r--libs/hwui/Android.bp3
-rw-r--r--media/java/android/media/AudioManager.java87
-rw-r--r--media/java/android/media/MediaCodec.java33
-rw-r--r--media/java/android/media/quality/MediaQualityContract.java344
-rw-r--r--media/java/android/mtp/OWNERS6
-rw-r--r--media/jni/android_mtp_MtpDatabase.cpp4
-rw-r--r--media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java39
-rw-r--r--media/tests/MtpTests/OWNERS6
-rw-r--r--packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java5
-rw-r--r--packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java17
-rw-r--r--packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java43
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt4
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt2
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt5
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt3
-rw-r--r--packages/SystemUI/AndroidManifest.xml6
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig36
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt85
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt2
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt15
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepositoryTest.kt20
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt50
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModelTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java36
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt23
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorImplTest.kt41
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolverTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/BundleNotificationInfoTest.java9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java127
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfoTest.java156
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractorTest.kt30
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractorTest.kt22
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInputEventsInteractorTest.kt13
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt5
-rw-r--r--packages/SystemUI/res-keyguard/values/styles.xml11
-rw-r--r--packages/SystemUI/res/layout/promoted_notification_info.xml387
-rw-r--r--packages/SystemUI/res/layout/volume_dialog_slider.xml8
-rw-r--r--packages/SystemUI/res/values-xlarge-land/config.xml1
-rw-r--r--packages/SystemUI/res/values/config.xml5
-rw-r--r--packages/SystemUI/res/values/strings.xml20
-rw-r--r--packages/SystemUI/res/values/styles.xml10
-rw-r--r--packages/SystemUI/res/xml/gradient_color_wallpaper.xml20
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java18
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextView.kt30
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/AccessibilityShortcutsSource.kt76
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WallpaperFocalAreaInteractor.kt176
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/BlurConfig.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotCrossProfileService.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt43
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java74
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/headsup/shared/StatusBarNoHunBehavior.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BundleNotificationInfo.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt124
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java104
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTransitionAnimatorController.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogEventModel.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractor.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/data/repository/VolumeDialogSliderTouchEventsRepository.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInputEventsInteractor.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/shared/model/SliderInputEvent.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogOverscrollViewBinder.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderHapticsViewBinder.kt82
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt249
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/compose/VolumeDialogSliderTrack.kt347
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogOverscrollViewModel.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderInputEventsViewModel.kt43
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt51
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/haptics/ui/VolumeHapticsConfigsProvider.kt41
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt70
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt19
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt41
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java7
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt9
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WallpaperFocalAreaInteractorKosmos.kt39
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt3
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorKosmos.kt43
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/RankingBuilder.java10
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponentKosmos.kt5
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderHapticsViewBinderKosmos.kt33
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinderKosmos.kt6
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderInputEventsViewModelKosmos.kt29
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryKosmos.kt4
-rw-r--r--packages/Vcn/framework-b/framework-vcn-jarjar-rules.txt3
-rw-r--r--ravenwood/Android.bp3
-rw-r--r--ravenwood/Framework.bp52
-rwxr-xr-xravenwood/scripts/pta-framework.sh3
-rw-r--r--ravenwood/texts/ravenwood-framework-policies.txt2
-rw-r--r--ravenwood/texts/ravenwood-standard-options.txt3
-rw-r--r--ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/Annotations.kt3
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java34
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java10
-rw-r--r--services/core/java/com/android/server/MasterClearReceiver.java2
-rw-r--r--services/core/java/com/android/server/am/BroadcastController.java2
-rw-r--r--services/core/java/com/android/server/am/PendingIntentRecord.java12
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java89
-rw-r--r--services/core/java/com/android/server/audio/AudioManagerShellCommand.java26
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java28
-rw-r--r--services/core/java/com/android/server/audio/PlaybackActivityMonitor.java67
-rw-r--r--services/core/java/com/android/server/display/feature/DisplayManagerFlags.java3
-rw-r--r--services/core/java/com/android/server/input/InputGestureManager.java10
-rw-r--r--services/core/java/com/android/server/media/quality/MediaQualityService.java343
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java9
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java60
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecord.java17
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecordExtractorData.java11
-rw-r--r--services/core/java/com/android/server/notification/ZenLog.java5
-rw-r--r--services/core/java/com/android/server/notification/flags.aconfig10
-rw-r--r--services/core/java/com/android/server/pm/DexOptHelper.java99
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java1
-rw-r--r--services/core/java/com/android/server/pm/UserManagerInternal.java6
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java6
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java28
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java10
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java93
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java4
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java10
-rw-r--r--services/core/jni/com_android_server_utils_AnrTimer.cpp10
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java34
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java120
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java18
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java16
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java10
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java83
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordExtractorDataTest.java55
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/CombinationKeyTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/DeferredKeyActionExecutorTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/KeyCombinationManagerTests.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java11
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java5
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java5
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/WindowWakeUpPolicyTests.java2
-rw-r--r--services/usb/OWNERS6
-rw-r--r--telecomm/java/com/android/internal/telecom/ITelecomService.aidl1
-rw-r--r--tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/RecentTasksUtils.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt)4
-rw-r--r--tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt24
280 files changed, 6366 insertions, 1699 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 302168d845fa..834398e5c2c2 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -824,6 +824,11 @@ java_aconfig_library {
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+cc_aconfig_library {
+ name: "android.media.tv.flags-aconfig-cc",
+ aconfig_declarations: "android.media.tv.flags-aconfig",
+}
+
// Permissions
aconfig_declarations {
name: "android.permission.flags-aconfig",
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index 787fdee6ee16..3314b4aa0d71 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -648,7 +648,7 @@ java_api_library {
java_api_library {
name: "android-non-updatable.stubs.module_lib.from-text",
- api_surface: "module_lib",
+ api_surface: "module-lib",
api_contributions: [
"api-stubs-docs-non-updatable.api.contribution",
"system-api-stubs-docs-non-updatable.api.contribution",
@@ -668,7 +668,7 @@ java_api_library {
// generated from this module, as this module is strictly used for hiddenapi only.
java_api_library {
name: "android-non-updatable.stubs.test_module_lib",
- api_surface: "module_lib",
+ api_surface: "module-lib",
api_contributions: [
"api-stubs-docs-non-updatable.api.contribution",
"system-api-stubs-docs-non-updatable.api.contribution",
@@ -689,7 +689,7 @@ java_api_library {
java_api_library {
name: "android-non-updatable.stubs.system_server.from-text",
- api_surface: "system_server",
+ api_surface: "system-server",
api_contributions: [
"api-stubs-docs-non-updatable.api.contribution",
"system-api-stubs-docs-non-updatable.api.contribution",
diff --git a/core/api/current.txt b/core/api/current.txt
index 6964866db7f3..3883a9667355 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -452,7 +452,7 @@ package android {
field public static final int activityCloseExitAnimation = 16842939; // 0x10100bb
field public static final int activityOpenEnterAnimation = 16842936; // 0x10100b8
field public static final int activityOpenExitAnimation = 16842937; // 0x10100b9
- field @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") public static final int adServiceTypes;
+ field @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") public static final int adServiceTypes = 16844452; // 0x10106a4
field public static final int addPrintersActivity = 16843750; // 0x10103e6
field public static final int addStatesFromChildren = 16842992; // 0x10100f0
field public static final int adjustViewBounds = 16843038; // 0x101011e
@@ -1017,7 +1017,7 @@ package android {
field public static final int insetRight = 16843192; // 0x10101b8
field public static final int insetTop = 16843193; // 0x10101b9
field public static final int installLocation = 16843447; // 0x10102b7
- field @FlaggedApi("android.security.enable_intent_matching_flags") public static final int intentMatchingFlags;
+ field @FlaggedApi("android.security.enable_intent_matching_flags") public static final int intentMatchingFlags = 16844457; // 0x10106a9
field public static final int interactiveUiTimeout = 16844181; // 0x1010595
field public static final int interpolator = 16843073; // 0x1010141
field public static final int intro = 16844395; // 0x101066b
@@ -1072,7 +1072,7 @@ package android {
field public static final int label = 16842753; // 0x1010001
field public static final int labelFor = 16843718; // 0x10103c6
field @Deprecated public static final int labelTextSize = 16843317; // 0x1010235
- field @FlaggedApi("android.view.inputmethod.ime_switcher_revamp_api") public static final int languageSettingsActivity;
+ field @FlaggedApi("android.view.inputmethod.ime_switcher_revamp_api") public static final int languageSettingsActivity = 16844453; // 0x10106a5
field public static final int languageTag = 16844040; // 0x1010508
field public static final int largeHeap = 16843610; // 0x101035a
field public static final int largeScreens = 16843398; // 0x1010286
@@ -1085,7 +1085,7 @@ package android {
field public static final int layout = 16842994; // 0x10100f2
field public static final int layoutAnimation = 16842988; // 0x10100ec
field public static final int layoutDirection = 16843698; // 0x10103b2
- field @FlaggedApi("android.view.inputmethod.ime_switcher_revamp_api") public static final int layoutLabel;
+ field @FlaggedApi("android.view.inputmethod.ime_switcher_revamp_api") public static final int layoutLabel = 16844458; // 0x10106aa
field public static final int layoutMode = 16843738; // 0x10103da
field public static final int layout_above = 16843140; // 0x1010184
field public static final int layout_alignBaseline = 16843142; // 0x1010186
@@ -1207,7 +1207,7 @@ package android {
field public static final int minResizeHeight = 16843670; // 0x1010396
field public static final int minResizeWidth = 16843669; // 0x1010395
field public static final int minSdkVersion = 16843276; // 0x101020c
- field @FlaggedApi("android.sdk.major_minor_versioning_scheme") public static final int minSdkVersionFull;
+ field @FlaggedApi("android.sdk.major_minor_versioning_scheme") public static final int minSdkVersionFull = 16844461; // 0x10106ad
field public static final int minWidth = 16843071; // 0x101013f
field public static final int minimumHorizontalAngle = 16843901; // 0x101047d
field public static final int minimumVerticalAngle = 16843902; // 0x101047e
@@ -1282,7 +1282,7 @@ package android {
field public static final int paddingStart = 16843699; // 0x10103b3
field public static final int paddingTop = 16842967; // 0x10100d7
field public static final int paddingVertical = 16844094; // 0x101053e
- field @FlaggedApi("android.content.pm.app_compat_option_16kb") public static final int pageSizeCompat;
+ field @FlaggedApi("android.content.pm.app_compat_option_16kb") public static final int pageSizeCompat = 16844459; // 0x10106ab
field public static final int panelBackground = 16842846; // 0x101005e
field public static final int panelColorBackground = 16842849; // 0x1010061
field public static final int panelColorForeground = 16842848; // 0x1010060
@@ -1624,7 +1624,7 @@ package android {
field public static final int summaryColumn = 16843426; // 0x10102a2
field public static final int summaryOff = 16843248; // 0x10101f0
field public static final int summaryOn = 16843247; // 0x10101ef
- field @FlaggedApi("android.view.accessibility.supplemental_description") public static final int supplementalDescription;
+ field @FlaggedApi("android.view.accessibility.supplemental_description") public static final int supplementalDescription = 16844456; // 0x10106a8
field public static final int supportedTypes = 16844369; // 0x1010651
field public static final int supportsAssist = 16844016; // 0x10104f0
field public static final int supportsBatteryGameMode = 16844374; // 0x1010656
@@ -1871,7 +1871,7 @@ package android {
field public static final int wallpaperIntraOpenExitAnimation = 16843416; // 0x1010298
field public static final int wallpaperOpenEnterAnimation = 16843411; // 0x1010293
field public static final int wallpaperOpenExitAnimation = 16843412; // 0x1010294
- field @FlaggedApi("android.nfc.nfc_associated_role_services") public static final int wantsRoleHolderPriority;
+ field @FlaggedApi("android.nfc.nfc_associated_role_services") public static final int wantsRoleHolderPriority = 16844460; // 0x10106ac
field public static final int webTextViewStyle = 16843449; // 0x10102b9
field public static final int webViewStyle = 16842885; // 0x1010085
field public static final int weekDayTextAppearance = 16843592; // 0x1010348
@@ -42267,6 +42267,7 @@ package android.service.notification {
method public int getRank();
method @NonNull public java.util.List<android.app.Notification.Action> getSmartActions();
method @NonNull public java.util.List<java.lang.CharSequence> getSmartReplies();
+ method @FlaggedApi("android.app.nm_summarization") @Nullable public String getSummarization();
method public int getSuppressedVisualEffects();
method public int getUserSentiment();
method public boolean isAmbient();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 937a9ffaf210..8a5276c1ce08 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -464,7 +464,7 @@ package android {
public static final class R.attr {
field public static final int allowClearUserDataOnFailedRestore = 16844288; // 0x1010600
- field @FlaggedApi("android.permission.flags.replace_body_sensor_permission_enabled") public static final int backgroundPermission;
+ field @FlaggedApi("android.permission.flags.replace_body_sensor_permission_enabled") public static final int backgroundPermission = 16844455; // 0x10106a7
field @FlaggedApi("android.content.res.manifest_flagging") public static final int featureFlag = 16844428; // 0x101068c
field public static final int gameSessionService = 16844373; // 0x1010655
field public static final int hotwordDetectionService = 16844326; // 0x1010626
@@ -543,7 +543,7 @@ package android {
field public static final int config_systemCallStreaming = 17039431; // 0x1040047
field public static final int config_systemCompanionDeviceProvider = 17039417; // 0x1040039
field public static final int config_systemContacts = 17039403; // 0x104002b
- field @FlaggedApi("android.content.pm.sdk_dependency_installer") public static final int config_systemDependencyInstaller;
+ field @FlaggedApi("android.content.pm.sdk_dependency_installer") public static final int config_systemDependencyInstaller = 17039434; // 0x104004a
field public static final int config_systemFinancedDeviceController = 17039430; // 0x1040046
field public static final int config_systemGallery = 17039399; // 0x1040027
field public static final int config_systemNotificationIntelligence = 17039413; // 0x1040035
@@ -555,7 +555,7 @@ package android {
field public static final int config_systemTextIntelligence = 17039414; // 0x1040036
field public static final int config_systemUi = 17039418; // 0x104003a
field public static final int config_systemUiIntelligence = 17039410; // 0x1040032
- field @FlaggedApi("android.permission.flags.system_vendor_intelligence_role_enabled") public static final int config_systemVendorIntelligence;
+ field @FlaggedApi("android.permission.flags.system_vendor_intelligence_role_enabled") public static final int config_systemVendorIntelligence = 17039435; // 0x104004b
field public static final int config_systemVisualIntelligence = 17039415; // 0x1040037
field public static final int config_systemWearHealthService = 17039428; // 0x1040044
field public static final int config_systemWellbeing = 17039408; // 0x1040030
@@ -13446,6 +13446,7 @@ package android.service.notification {
field public static final String KEY_RANKING_SCORE = "key_ranking_score";
field public static final String KEY_SENSITIVE_CONTENT = "key_sensitive_content";
field public static final String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
+ field @FlaggedApi("android.app.nm_summarization") public static final String KEY_SUMMARIZATION = "key_summarization";
field public static final String KEY_TEXT_REPLIES = "key_text_replies";
field @FlaggedApi("android.service.notification.notification_classification") public static final String KEY_TYPE = "key_type";
field public static final String KEY_USER_SENTIMENT = "key_user_sentiment";
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 16dcf2ad7e45..8d20e46c7df8 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -25,6 +25,7 @@ import android.annotation.SystemService;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -483,6 +484,19 @@ public class ActivityTaskManager {
}
/**
+ * @return Whether the app could be universal resizeable (assuming it's on a large screen and
+ * ignoring possible overrides)
+ * @hide
+ */
+ public boolean canBeUniversalResizeable(@NonNull ApplicationInfo appInfo) {
+ try {
+ return getService().canBeUniversalResizeable(appInfo);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Detaches the navigation bar from the app it was attached to during a transition.
* @hide
*/
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index c6f62a21641d..4b1afa517122 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -158,6 +158,12 @@ interface IActivityTaskManager {
void reportAssistContextExtras(in IBinder assistToken, in Bundle extras,
in AssistStructure structure, in AssistContent content, in Uri referrer);
+ /**
+ * @return whether the app could be universal resizeable (assuming it's on a large screen and
+ * ignoring possible overrides)
+ */
+ boolean canBeUniversalResizeable(in ApplicationInfo appInfo);
+
void setFocusedRootTask(int taskId);
ActivityTaskManager.RootTaskInfo getFocusedRootTaskInfo();
Rect getTaskBounds(int taskId);
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index b7285c38290c..7454c44997d6 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -60,6 +60,7 @@ import com.android.internal.statusbar.NotificationVisibility;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -195,12 +196,40 @@ public class StatusBarManager {
*/
private static final int DEFAULT_SIM_LOCKED_DISABLED_FLAGS = DISABLE_EXPAND;
- /** @hide */
- public static final int NAVIGATION_HINT_BACK_ALT = 1 << 0;
- /** @hide */
- public static final int NAVIGATION_HINT_IME_SHOWN = 1 << 1;
- /** @hide */
- public static final int NAVIGATION_HINT_IME_SWITCHER_SHOWN = 1 << 2;
+ /**
+ * The back button is visually adjusted to indicate that it will dismiss the IME when pressed.
+ * This only takes effect while the IME is visible. By default, it is set while the IME is
+ * visible, but may be overridden by the
+ * {@link android.inputmethodservice.InputMethodService.BackDispositionMode backDispositionMode}
+ * set by the IME.
+ *
+ * @hide
+ */
+ public static final int NAVIGATION_HINT_BACK_ALT = 1 << 0;
+ /**
+ * The IME is visible.
+ *
+ * @hide
+ */
+ public static final int NAVIGATION_HINT_IME_SHOWN = 1 << 1;
+ /**
+ * The IME Switcher button is visible. This only takes effect while the IME is visible.
+ *
+ * @hide
+ */
+ public static final int NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN = 1 << 2;
+ /**
+ * Navigation bar flags related to the IME state.
+ *
+ * @hide
+ */
+ @IntDef(flag = true, prefix = { "NAVIGATION_HINT_" }, value = {
+ NAVIGATION_HINT_BACK_ALT,
+ NAVIGATION_HINT_IME_SHOWN,
+ NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface NavigationHint {}
/** @hide */
public static final int WINDOW_STATUS_BAR = 1;
@@ -1325,6 +1354,22 @@ public class StatusBarManager {
}
/** @hide */
+ @NonNull
+ public static String navigationHintsToString(@NavigationHint int hints) {
+ final var hintStrings = new ArrayList<String>();
+ if ((hints & NAVIGATION_HINT_BACK_ALT) != 0) {
+ hintStrings.add("NAVIGATION_HINT_BACK_ALT");
+ }
+ if ((hints & NAVIGATION_HINT_IME_SHOWN) != 0) {
+ hintStrings.add("NAVIGATION_HINT_IME_SHOWN");
+ }
+ if ((hints & NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN) != 0) {
+ hintStrings.add("NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN");
+ }
+ return String.join(" | ", hintStrings);
+ }
+
+ /** @hide */
public static String windowStateToString(int state) {
if (state == WINDOW_STATE_HIDING) return "WINDOW_STATE_HIDING";
if (state == WINDOW_STATE_HIDDEN) return "WINDOW_STATE_HIDDEN";
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 6f8e335cff80..1035d8b93881 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -222,7 +222,8 @@ public final class UiAutomation {
private OnAccessibilityEventListener mOnAccessibilityEventListener;
- private boolean mWaitingForEventDelivery;
+ // Count the nested clients waiting for data delivery
+ private int mCurrentEventWatchersCount = 0;
private long mLastEventTimeMillis;
@@ -1132,73 +1133,70 @@ public final class UiAutomation {
*/
public AccessibilityEvent executeAndWaitForEvent(Runnable command,
AccessibilityEventFilter filter, long timeoutMillis) throws TimeoutException {
+ int watchersDepth;
+ // Track events added after the index for this command, it is to support nested calls.
+ // This doesn't support concurrent calls correctly.
+ int eventQueueStartIndex;
+ final long executionStartTimeMillis;
+
// Acquire the lock and prepare for receiving events.
synchronized (mLock) {
throwIfNotConnectedLocked();
- mEventQueue.clear();
- // Prepare to wait for an event.
- mWaitingForEventDelivery = true;
+ watchersDepth = ++mCurrentEventWatchersCount;
+ executionStartTimeMillis = SystemClock.uptimeMillis();
+ eventQueueStartIndex = mEventQueue.size();
+ }
+ if (DEBUG) {
+ Log.d(LOG_TAG, "executeAndWaitForEvent: watchersCount=" + watchersDepth
+ + ", eventQueueStartIndex=" + eventQueueStartIndex);
}
- // Note: We have to release the lock since calling out with this lock held
- // can bite. We will correctly filter out events from other interactions,
- // so starting to collect events before running the action is just fine.
-
- // We will ignore events from previous interactions.
- final long executionStartTimeMillis = SystemClock.uptimeMillis();
- // Execute the command *without* the lock being held.
- command.run();
-
- List<AccessibilityEvent> receivedEvents = new ArrayList<>();
-
- // Acquire the lock and wait for the event.
try {
- // Wait for the event.
- final long startTimeMillis = SystemClock.uptimeMillis();
- while (true) {
- List<AccessibilityEvent> localEvents = new ArrayList<>();
- synchronized (mLock) {
- localEvents.addAll(mEventQueue);
- mEventQueue.clear();
- }
- // Drain the event queue
- while (!localEvents.isEmpty()) {
- AccessibilityEvent event = localEvents.remove(0);
- // Ignore events from previous interactions.
- if (event.getEventTime() < executionStartTimeMillis) {
- continue;
- }
- if (filter.accept(event)) {
- return event;
- }
- receivedEvents.add(event);
- }
- // Check if timed out and if not wait.
- final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
- final long remainingTimeMillis = timeoutMillis - elapsedTimeMillis;
- if (remainingTimeMillis <= 0) {
- throw new TimeoutException("Expected event not received within: "
- + timeoutMillis + " ms among: " + receivedEvents);
+ // Execute the command *without* the lock being held.
+ command.run();
+ synchronized (mLock) {
+ if (watchersDepth != mCurrentEventWatchersCount) {
+ throw new IllegalStateException("Unexpected event watchers count, expected: "
+ + watchersDepth + ", actual: " + mCurrentEventWatchersCount);
}
+ }
+ final long startTimeMillis = SystemClock.uptimeMillis();
+ List<AccessibilityEvent> receivedEvents = new ArrayList<>();
+ long elapsedTimeMillis = 0;
+ int currentQueueSize = 0;
+ while (timeoutMillis > elapsedTimeMillis) {
+ AccessibilityEvent event = null;
synchronized (mLock) {
- if (mEventQueue.isEmpty()) {
+ currentQueueSize = mEventQueue.size();
+ if (eventQueueStartIndex < currentQueueSize) {
+ event = mEventQueue.get(eventQueueStartIndex++);
+ } else {
try {
- mLock.wait(remainingTimeMillis);
+ mLock.wait(timeoutMillis - elapsedTimeMillis);
} catch (InterruptedException ie) {
/* ignore */
}
}
}
+ elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
+ if (event == null || event.getEventTime() < executionStartTimeMillis) {
+ continue;
+ }
+ if (filter.accept(event)) {
+ return event;
+ }
+ receivedEvents.add(event);
}
- } finally {
- int size = receivedEvents.size();
- for (int i = 0; i < size; i++) {
- receivedEvents.get(i).recycle();
+ if (eventQueueStartIndex < currentQueueSize) {
+ Log.w(LOG_TAG, "Timed out before reading all events from the queue");
}
-
+ throw new TimeoutException("Expected event not received before timeout, events: "
+ + receivedEvents);
+ } finally {
synchronized (mLock) {
- mWaitingForEventDelivery = false;
- mEventQueue.clear();
+ if (--mCurrentEventWatchersCount == 0) {
+ mEventQueue.clear();
+ }
mLock.notifyAll();
}
}
@@ -1957,7 +1955,7 @@ public final class UiAutomation {
// It is not guaranteed that the accessibility framework sends events by the
// order of event timestamp.
mLastEventTimeMillis = Math.max(mLastEventTimeMillis, event.getEventTime());
- if (mWaitingForEventDelivery) {
+ if (mCurrentEventWatchersCount > 0) {
mEventQueue.add(AccessibilityEvent.obtain(event));
}
mLock.notifyAll();
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index 914ca73f1ce4..a4a6a55fc9ab 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -310,3 +310,17 @@ flag {
description: "removes sbnholder from NLS"
bug: "362981561"
}
+
+flag {
+ name: "nm_summarization"
+ namespace: "systemui"
+ description: "Allows the NAS to summarize notifications"
+ bug: "390417189"
+}
+
+flag {
+ name: "nm_summarization_ui"
+ namespace: "systemui"
+ description: "Shows summarized notifications in the UI"
+ bug: "390217880"
+}
diff --git a/core/java/android/app/supervision/flags.aconfig b/core/java/android/app/supervision/flags.aconfig
index 18182b804627..232883cbfe00 100644
--- a/core/java/android/app/supervision/flags.aconfig
+++ b/core/java/android/app/supervision/flags.aconfig
@@ -48,3 +48,19 @@ flag {
description: "Flag that enables the Supervision settings screen with top-level Android settings entry point"
bug: "383404606"
}
+
+flag {
+ name: "enable_app_approval"
+ is_exported: true
+ namespace: "supervision"
+ description: "Flag to enable the App Approval settings in Android settings UI"
+ bug: "390185393"
+}
+
+flag {
+ name: "enable_supervision_pin_recovery_screen"
+ is_exported: true
+ namespace: "supervision"
+ description: "Flag that enables the Supervision pin recovery screen with Supervision settings entry point"
+ bug: "390500290"
+}
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index 8a3a3ad56a7b..582a1a9442ce 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -183,6 +183,12 @@ public class UserInfo implements Parcelable {
*
* <p>This is not necessarily the system user. For example, it will not be the system user on
* devices for which {@link UserManager#isHeadlessSystemUserMode()} returns true.
+ *
+ * <p>NB: Features should ideally not limit functionality to the main user. Ideally, they
+ * should either work for all users or for all admin users. If a feature should only work for
+ * select users, its determination of which user should be done intelligently or be
+ * customizable. Not all devices support a main user, and the idea of singling out one user as
+ * special is contrary to overall multiuser goals.
*/
public static final int FLAG_MAIN = 0x00004000;
diff --git a/core/java/android/hardware/usb/OWNERS b/core/java/android/hardware/usb/OWNERS
index 37604bc2eb65..1de8a242acfc 100644
--- a/core/java/android/hardware/usb/OWNERS
+++ b/core/java/android/hardware/usb/OWNERS
@@ -1,7 +1,7 @@
# Bug component: 175220
-anothermark@google.com
+vmartensson@google.com
+nkapron@google.com
febinthattil@google.com
-aprasath@google.com
+shubhankarm@google.com
badhri@google.com
-kumarashishg@google.com \ No newline at end of file
diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java
index 019ba0045916..edc5cb7cdf6a 100644
--- a/core/java/android/inputmethodservice/NavigationBarController.java
+++ b/core/java/android/inputmethodservice/NavigationBarController.java
@@ -18,7 +18,7 @@ package android.inputmethodservice;
import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
-import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN;
import static android.view.WindowInsets.Type.captionBar;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
@@ -245,7 +245,7 @@ final class NavigationBarController {
// TODO(b/213337792): Set NAVIGATION_HINT_IME_SHOWN only when necessary.
final int hints = NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN
| (mShouldShowImeSwitcherWhenImeIsShown
- ? NAVIGATION_HINT_IME_SWITCHER_SHOWN : 0);
+ ? NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN : 0);
navigationBarView.setNavigationIconHints(hints);
navigationBarView.prepareNavButtons(this);
}
@@ -518,7 +518,7 @@ final class NavigationBarController {
// TODO(b/213337792): Set NAVIGATION_HINT_IME_SHOWN only when necessary.
final int hints = NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN
| (mShouldShowImeSwitcherWhenImeIsShown
- ? NAVIGATION_HINT_IME_SWITCHER_SHOWN : 0);
+ ? NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN : 0);
navigationBarView.setNavigationIconHints(hints);
}
} else {
diff --git a/core/java/android/inputmethodservice/navigationbar/NavigationBarView.java b/core/java/android/inputmethodservice/navigationbar/NavigationBarView.java
index e7e46a9482c8..622d5d1b1c5a 100644
--- a/core/java/android/inputmethodservice/navigationbar/NavigationBarView.java
+++ b/core/java/android/inputmethodservice/navigationbar/NavigationBarView.java
@@ -16,6 +16,8 @@
package android.inputmethodservice.navigationbar;
+import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN;
import static android.inputmethodservice.navigationbar.NavigationBarConstants.DARK_MODE_ICON_COLOR_SINGLE_TONE;
import static android.inputmethodservice.navigationbar.NavigationBarConstants.LIGHT_MODE_ICON_COLOR_SINGLE_TONE;
import static android.inputmethodservice.navigationbar.NavigationBarConstants.NAVBAR_BACK_BUTTON_IME_OFFSET;
@@ -28,6 +30,7 @@ import android.annotation.DrawableRes;
import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.app.StatusBarManager;
+import android.app.StatusBarManager.NavigationHint;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Canvas;
@@ -63,7 +66,8 @@ public final class NavigationBarView extends FrameLayout {
private int mCurrentRotation = -1;
int mDisabledFlags = 0;
- int mNavigationIconHints = StatusBarManager.NAVIGATION_HINT_BACK_ALT;
+ @NavigationHint
+ private int mNavigationIconHints = 0;
private final int mNavBarMode = NAV_BAR_MODE_GESTURAL;
private KeyButtonDrawable mBackIcon;
@@ -241,8 +245,7 @@ public final class NavigationBarView extends FrameLayout {
}
private void orientBackButton(KeyButtonDrawable drawable) {
- final boolean useAltBack =
- (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
+ final boolean useAltBack = (mNavigationIconHints & NAVIGATION_HINT_BACK_ALT) != 0;
final boolean isRtl = mConfiguration.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
float degrees = useAltBack ? (isRtl ? 90 : -90) : 0;
if (drawable.getRotation() == degrees) {
@@ -284,8 +287,10 @@ public final class NavigationBarView extends FrameLayout {
*
* @param hints bit flags defined in {@link StatusBarManager}.
*/
- public void setNavigationIconHints(int hints) {
- if (hints == mNavigationIconHints) return;
+ public void setNavigationIconHints(@NavigationHint int hints) {
+ if (hints == mNavigationIconHints) {
+ return;
+ }
final boolean newBackAlt = (hints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
final boolean oldBackAlt =
(mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
@@ -312,9 +317,10 @@ public final class NavigationBarView extends FrameLayout {
getImeSwitchButton().setImageDrawable(mImeSwitcherIcon);
// Update IME button visibility, a11y and rotate button always overrides the appearance
- final boolean imeSwitcherVisible =
- (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN) != 0;
- getImeSwitchButton().setVisibility(imeSwitcherVisible ? View.VISIBLE : View.INVISIBLE);
+ final boolean isImeSwitcherButtonVisible =
+ (mNavigationIconHints & NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN) != 0;
+ getImeSwitchButton()
+ .setVisibility(isImeSwitcherButtonVisible ? View.VISIBLE : View.INVISIBLE);
getBackButton().setVisibility(View.VISIBLE);
getHomeHandle().setVisibility(View.INVISIBLE);
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index ee62dea7f9e5..6b1e918a3c47 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -149,6 +149,11 @@ public class Binder implements IBinder {
private static volatile boolean sStackTrackingEnabled = false;
/**
+ * The extension binder object
+ */
+ private IBinder mExtension = null;
+
+ /**
* Enable Binder IPC stack tracking. If enabled, every binder transaction will be logged to
* {@link TransactionTracker}.
*
@@ -1237,7 +1242,9 @@ public class Binder implements IBinder {
/** @hide */
@Override
- public final native @Nullable IBinder getExtension();
+ public final @Nullable IBinder getExtension() {
+ return mExtension;
+ }
/**
* Set the binder extension.
@@ -1245,7 +1252,12 @@ public class Binder implements IBinder {
*
* @hide
*/
- public final native void setExtension(@Nullable IBinder extension);
+ public final void setExtension(@Nullable IBinder extension) {
+ mExtension = extension;
+ setExtensionNative(extension);
+ }
+
+ private final native void setExtensionNative(@Nullable IBinder extension);
/**
* Default implementation rewinds the parcels and calls onTransact. On
diff --git a/core/java/android/os/CombinedMessageQueue/MessageQueue.java b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
index 50b621c46778..1e663342522b 100644
--- a/core/java/android/os/CombinedMessageQueue/MessageQueue.java
+++ b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
@@ -39,7 +39,6 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.ravenwood.RavenwoodEnvironment;
import dalvik.annotation.optimization.NeverCompile;
-import dalvik.annotation.optimization.NeverInline;
import java.io.FileDescriptor;
import java.lang.annotation.Retention;
@@ -238,7 +237,6 @@ public final class MessageQueue {
private final MatchDeliverableMessages mMatchDeliverableMessages =
new MatchDeliverableMessages();
- @NeverInline
private boolean isIdleConcurrent() {
final long now = SystemClock.uptimeMillis();
@@ -269,7 +267,6 @@ public final class MessageQueue {
return true;
}
- @NeverInline
private boolean isIdleLegacy() {
synchronized (this) {
final long now = SystemClock.uptimeMillis();
@@ -292,14 +289,12 @@ public final class MessageQueue {
}
}
- @NeverInline
private void addIdleHandlerConcurrent(@NonNull IdleHandler handler) {
synchronized (mIdleHandlersLock) {
mIdleHandlers.add(handler);
}
}
- @NeverInline
private void addIdleHandlerLegacy(@NonNull IdleHandler handler) {
synchronized (this) {
mIdleHandlers.add(handler);
@@ -326,15 +321,11 @@ public final class MessageQueue {
addIdleHandlerLegacy(handler);
}
}
-
- @NeverInline
private void removeIdleHandlerConcurrent(@NonNull IdleHandler handler) {
synchronized (mIdleHandlersLock) {
mIdleHandlers.remove(handler);
}
}
-
- @NeverInline
private void removeIdleHandlerLegacy(@NonNull IdleHandler handler) {
synchronized (this) {
mIdleHandlers.remove(handler);
@@ -358,14 +349,12 @@ public final class MessageQueue {
}
}
- @NeverInline
private boolean isPollingConcurrent() {
// If the loop is quitting then it must not be idling.
// We can assume mPtr != 0 when sQuitting is false.
return !((boolean) sQuitting.getVolatile(this)) && nativeIsPolling(mPtr);
}
- @NeverInline
private boolean isPollingLegacy() {
synchronized (this) {
return isPollingLocked();
@@ -396,7 +385,6 @@ public final class MessageQueue {
// We can assume mPtr != 0 when mQuitting is false.
return !mQuitting && nativeIsPolling(mPtr);
}
- @NeverInline
private void addOnFileDescriptorEventListenerConcurrent(@NonNull FileDescriptor fd,
@OnFileDescriptorEventListener.Events int events,
@NonNull OnFileDescriptorEventListener listener) {
@@ -405,7 +393,6 @@ public final class MessageQueue {
}
}
- @NeverInline
private void addOnFileDescriptorEventListenerLegacy(@NonNull FileDescriptor fd,
@OnFileDescriptorEventListener.Events int events,
@NonNull OnFileDescriptorEventListener listener) {
@@ -455,14 +442,12 @@ public final class MessageQueue {
}
}
- @NeverInline
private void removeOnFileDescriptorEventListenerConcurrent(@NonNull FileDescriptor fd) {
synchronized (mFileDescriptorRecordsLock) {
updateOnFileDescriptorEventListenerLocked(fd, 0, null);
}
}
- @NeverInline
private void removeOnFileDescriptorEventListenerLegacy(@NonNull FileDescriptor fd) {
synchronized (this) {
updateOnFileDescriptorEventListenerLocked(fd, 0, null);
@@ -796,7 +781,6 @@ public final class MessageQueue {
}
}
- @NeverInline
private Message nextConcurrent() {
final long ptr = mPtr;
if (ptr == 0) {
@@ -871,7 +855,6 @@ public final class MessageQueue {
}
}
- @NeverInline
private Message nextLegacy() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
@@ -1036,13 +1019,11 @@ public final class MessageQueue {
}
}
- @NeverInline
private int postSyncBarrierConcurrent() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
- @NeverInline
private int postSyncBarrierLegacy() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
@@ -1162,7 +1143,6 @@ public final class MessageQueue {
}
}
- @NeverInline
private void removeSyncBarrierConcurrent(int token) {
boolean removed;
MessageNode first;
@@ -1189,7 +1169,6 @@ public final class MessageQueue {
}
}
- @NeverInline
private void removeSyncBarrierLegacy(int token) {
synchronized (this) {
Message prev = null;
@@ -1249,7 +1228,6 @@ public final class MessageQueue {
}
- @NeverInline
private boolean enqueueMessageConcurrent(Message msg, long when) {
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
@@ -1258,7 +1236,6 @@ public final class MessageQueue {
return enqueueMessageUnchecked(msg, when);
}
- @NeverInline
private boolean enqueueMessageLegacy(Message msg, long when) {
synchronized (this) {
if (msg.isInUse()) {
@@ -1495,13 +1472,11 @@ public final class MessageQueue {
private final MatchHandlerWhatAndObject mMatchHandlerWhatAndObject =
new MatchHandlerWhatAndObject();
- @NeverInline
private boolean hasMessagesConcurrent(Handler h, int what, Object object) {
return findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObject,
false);
}
- @NeverInline
private boolean hasMessagesLegacy(Handler h, int what, Object object) {
synchronized (this) {
Message p = mMessages;
@@ -1540,13 +1515,11 @@ public final class MessageQueue {
private final MatchHandlerWhatAndObjectEquals mMatchHandlerWhatAndObjectEquals =
new MatchHandlerWhatAndObjectEquals();
- @NeverInline
private boolean hasEqualMessagesConcurrent(Handler h, int what, Object object) {
return findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObjectEquals,
false);
}
- @NeverInline
private boolean hasEqualMessagesLegacy(Handler h, int what, Object object) {
synchronized (this) {
Message p = mMessages;
@@ -1585,13 +1558,11 @@ public final class MessageQueue {
private final MatchHandlerRunnableAndObject mMatchHandlerRunnableAndObject =
new MatchHandlerRunnableAndObject();
- @NeverInline
private boolean hasMessagesConcurrent(Handler h, Runnable r, Object object) {
return findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObject,
false);
}
- @NeverInline
private boolean hasMessagesLegacy(Handler h, Runnable r, Object object) {
synchronized (this) {
Message p = mMessages;
@@ -1626,12 +1597,10 @@ public final class MessageQueue {
}
private final MatchHandler mMatchHandler = new MatchHandler();
- @NeverInline
private boolean hasMessagesConcurrent(Handler h) {
return findOrRemoveMessages(h, -1, null, null, 0, mMatchHandler, false);
}
- @NeverInline
private boolean hasMessagesLegacy(Handler h) {
synchronized (this) {
Message p = mMessages;
@@ -1656,12 +1625,10 @@ public final class MessageQueue {
}
}
- @NeverInline
private void removeMessagesConcurrent(Handler h, int what, Object object) {
findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObject, true);
}
- @NeverInline
private void removeMessagesLegacy(Handler h, int what, Object object) {
synchronized (this) {
Message p = mMessages;
@@ -1716,12 +1683,10 @@ public final class MessageQueue {
}
}
- @NeverInline
private void removeEqualMessagesConcurrent(Handler h, int what, Object object) {
findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObjectEquals, true);
}
- @NeverInline
private void removeEqualMessagesLegacy(Handler h, int what, Object object) {
synchronized (this) {
Message p = mMessages;
@@ -1777,12 +1742,10 @@ public final class MessageQueue {
}
}
- @NeverInline
private void removeMessagesConcurrent(Handler h, Runnable r, Object object) {
findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObject, true);
}
- @NeverInline
private void removeMessagesLegacy(Handler h, Runnable r, Object object) {
synchronized (this) {
Message p = mMessages;
@@ -1852,12 +1815,10 @@ public final class MessageQueue {
private final MatchHandlerRunnableAndObjectEquals mMatchHandlerRunnableAndObjectEquals =
new MatchHandlerRunnableAndObjectEquals();
- @NeverInline
private void removeEqualMessagesConcurrent(Handler h, Runnable r, Object object) {
findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObjectEquals, true);
}
- @NeverInline
private void removeEqualMessagesLegacy(Handler h, Runnable r, Object object) {
synchronized (this) {
Message p = mMessages;
@@ -1926,12 +1887,10 @@ public final class MessageQueue {
}
private final MatchHandlerAndObject mMatchHandlerAndObject = new MatchHandlerAndObject();
- @NeverInline
private void removeCallbacksAndMessagesConcurrent(Handler h, Object object) {
findOrRemoveMessages(h, -1, object, null, 0, mMatchHandlerAndObject, true);
}
- @NeverInline
private void removeCallbacksAndMessagesLegacy(Handler h, Object object) {
synchronized (this) {
Message p = mMessages;
@@ -2000,12 +1959,10 @@ public final class MessageQueue {
private final MatchHandlerAndObjectEquals mMatchHandlerAndObjectEquals =
new MatchHandlerAndObjectEquals();
- @NeverInline
void removeCallbacksAndEqualMessagesConcurrent(Handler h, Object object) {
findOrRemoveMessages(h, -1, object, null, 0, mMatchHandlerAndObjectEquals, true);
}
- @NeverInline
void removeCallbacksAndEqualMessagesLegacy(Handler h, Object object) {
synchronized (this) {
Message p = mMessages;
diff --git a/core/java/android/os/UidBatteryConsumer.java b/core/java/android/os/UidBatteryConsumer.java
index 976bfe41ba45..62d5015af914 100644
--- a/core/java/android/os/UidBatteryConsumer.java
+++ b/core/java/android/os/UidBatteryConsumer.java
@@ -147,7 +147,7 @@ public final class UidBatteryConsumer extends BatteryConsumer {
for (int screenState = 0; screenState < SCREEN_STATE_COUNT; screenState++) {
if (mData.layout.screenStateDataIncluded
- && screenState == POWER_STATE_UNSPECIFIED) {
+ && screenState == SCREEN_STATE_UNSPECIFIED) {
continue;
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 967f55ce7a88..6c21dbf126bb 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2910,10 +2910,18 @@ public class UserManager {
* <p>Currently, on most form factors the first human user on the device will be the main user;
* in the future, the concept may be transferable, so a different user (or even no user at all)
* may be designated the main user instead. On other form factors there might not be a main
+ * user. In the future, the concept may be removed, i.e. typical future devices may have no main
* user.
*
* <p>Note that this will not be the system user on devices for which
* {@link #isHeadlessSystemUserMode()} returns true.
+ *
+ * <p>NB: Features should ideally not limit functionality to the main user. Ideally, they
+ * should either work for all users or for all admin users. If a feature should only work for
+ * select users, its determination of which user should be done intelligently or be
+ * customizable. Not all devices support a main user, and the idea of singling out one user as
+ * special is contrary to overall multiuser goals.
+ *
* @hide
*/
@SystemApi
@@ -2930,6 +2938,12 @@ public class UserManager {
/**
* Returns the designated "main user" of the device, or {@code null} if there is no main user.
*
+ * <p>NB: Features should ideally not limit functionality to the main user. Ideally, they
+ * should either work for all users or for all admin users. If a feature should only work for
+ * select users, its determination of which user should be done intelligently or be
+ * customizable. Not all devices support a main user, and the idea of singling out one user as
+ * special is contrary to overall multiuser goals.
+ *
* @see #isMainUser()
* @hide
*/
diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java
index 073f512a85fb..09ebc9f030c2 100644
--- a/core/java/android/service/notification/Adjustment.java
+++ b/core/java/android/service/notification/Adjustment.java
@@ -21,6 +21,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringDef;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.app.Notification;
import android.os.Build;
import android.os.Bundle;
@@ -223,6 +224,14 @@ public final class Adjustment implements Parcelable {
public static final int TYPE_CONTENT_RECOMMENDATION = 4;
/**
+ * Data type: String, the classification type of this notification. The OS may display
+ * notifications differently depending on the type, and may change the alerting level of the
+ * notification.
+ */
+ @FlaggedApi(android.app.Flags.FLAG_NM_SUMMARIZATION)
+ public static final String KEY_SUMMARIZATION = "key_summarization";
+
+ /**
* Create a notification adjustment.
*
* @param pkg The package of the notification.
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 72569075c2ed..f23006584621 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -23,6 +23,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.annotation.UiThread;
import android.app.ActivityManager;
import android.app.INotificationManager;
@@ -56,6 +57,7 @@ import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.widget.RemoteViews;
@@ -1824,6 +1826,7 @@ public abstract class NotificationListenerService extends Service {
private int mProposedImportance;
// Sensitive info detected by the notification assistant
private boolean mSensitiveContent;
+ private String mSummarization;
private static final int PARCEL_VERSION = 2;
@@ -1864,6 +1867,7 @@ public abstract class NotificationListenerService extends Service {
out.writeBoolean(mIsBubble);
out.writeInt(mProposedImportance);
out.writeBoolean(mSensitiveContent);
+ out.writeString(mSummarization);
}
/** @hide */
@@ -1904,6 +1908,7 @@ public abstract class NotificationListenerService extends Service {
mIsBubble = in.readBoolean();
mProposedImportance = in.readInt();
mSensitiveContent = in.readBoolean();
+ mSummarization = in.readString();
}
@@ -2180,6 +2185,16 @@ public abstract class NotificationListenerService extends Service {
}
/**
+ * Returns a summary of the content in the notification, or potentially of the current
+ * notification and related notifications (for example, if this is provided for a group
+ * summary notification it may be summarizing all the child notifications).
+ */
+ @FlaggedApi(android.app.Flags.FLAG_NM_SUMMARIZATION)
+ public @Nullable String getSummarization() {
+ return mSummarization;
+ }
+
+ /**
* Returns the intended transition to ranking passed by {@link NotificationAssistantService}
* @hide
*/
@@ -2201,7 +2216,7 @@ public abstract class NotificationListenerService extends Service {
ArrayList<CharSequence> smartReplies, boolean canBubble,
boolean isTextChanged, boolean isConversation, ShortcutInfo shortcutInfo,
int rankingAdjustment, boolean isBubble, int proposedImportance,
- boolean sensitiveContent) {
+ boolean sensitiveContent, String summarization) {
mKey = key;
mRank = rank;
mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW;
@@ -2229,6 +2244,7 @@ public abstract class NotificationListenerService extends Service {
mIsBubble = isBubble;
mProposedImportance = proposedImportance;
mSensitiveContent = sensitiveContent;
+ mSummarization = TextUtils.nullIfEmpty(summarization);
}
/**
@@ -2271,7 +2287,8 @@ public abstract class NotificationListenerService extends Service {
other.mRankingAdjustment,
other.mIsBubble,
other.mProposedImportance,
- other.mSensitiveContent);
+ other.mSensitiveContent,
+ other.mSummarization);
}
/**
@@ -2332,7 +2349,8 @@ public abstract class NotificationListenerService extends Service {
&& Objects.equals(mRankingAdjustment, other.mRankingAdjustment)
&& Objects.equals(mIsBubble, other.mIsBubble)
&& Objects.equals(mProposedImportance, other.mProposedImportance)
- && Objects.equals(mSensitiveContent, other.mSensitiveContent);
+ && Objects.equals(mSensitiveContent, other.mSensitiveContent)
+ && Objects.equals(mSummarization, other.mSummarization);
}
}
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 105fa3ffd4cd..79957f411597 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -18,6 +18,8 @@ package android.service.notification;
import static android.text.TextUtils.formatSimple;
+import static com.android.window.flags.Flags.enablePerDisplayPackageContextCacheInStatusbarNotif;
+
import android.annotation.NonNull;
import android.app.Notification;
import android.app.NotificationManager;
@@ -37,9 +39,9 @@ import android.util.ArrayMap;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import static com.android.window.flags.Flags.enablePerDisplayPackageContextCacheInStatusbarNotif;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Map;
/**
@@ -81,7 +83,8 @@ public class StatusBarNotification implements Parcelable {
@Deprecated
private Context mContext; // used for inflation & icon expansion
// Maps display id to context used for remote view content inflation and status bar icon.
- private final Map<Integer, Context> mContextForDisplayId = new ArrayMap<>();
+ private final Map<Integer, Context> mContextForDisplayId =
+ Collections.synchronizedMap(new ArrayMap<>());
/** @hide */
public StatusBarNotification(String pkg, String opPkg, int id,
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index cb498503f201..a5d52957c40e 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -121,6 +121,7 @@ public class StaticLayout extends Layout {
b.mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE;
b.mJustificationMode = Layout.JUSTIFICATION_MODE_NONE;
b.mLineBreakConfig = LineBreakConfig.NONE;
+ b.mUseBoundsForWidth = false;
b.mMinimumFontMetrics = null;
return b;
}
diff --git a/core/java/android/window/DesktopExperienceFlags.java b/core/java/android/window/DesktopExperienceFlags.java
new file mode 100644
index 000000000000..0d1bb77ae8a2
--- /dev/null
+++ b/core/java/android/window/DesktopExperienceFlags.java
@@ -0,0 +1,124 @@
+/*
+ * 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 android.window;
+
+import static com.android.server.display.feature.flags.Flags.enableDisplayContentModeManagement;
+
+import android.annotation.Nullable;
+import android.os.SystemProperties;
+import android.util.Log;
+
+import com.android.window.flags.Flags;
+
+import java.util.function.BooleanSupplier;
+
+/**
+ * Checks Desktop Experience flag state.
+ *
+ * <p>This enum provides a centralized way to control the behavior of flags related to desktop
+ * experience features which are aiming for developer preview before their release. It allows
+ * developer option to override the default behavior of these flags.
+ *
+ * <p>The flags here will be controlled by the {@code
+ * persist.wm.debug.desktop_experience_devopts} system property.
+ *
+ * <p>NOTE: Flags should only be added to this enum when they have received Product and UX alignment
+ * that the feature is ready for developer preview, otherwise just do a flag check.
+ *
+ * @hide
+ */
+public enum DesktopExperienceFlags {
+ ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT(() -> enableDisplayContentModeManagement(), true);
+
+ /**
+ * Flag class, to be used in case the enum cannot be used because the flag is not accessible.
+ *
+ * <p>This class will still use the process-wide cache.
+ */
+ public static class DesktopExperienceFlag {
+ // Function called to obtain aconfig flag value.
+ private final BooleanSupplier mFlagFunction;
+ // Whether the flag state should be affected by developer option.
+ private final boolean mShouldOverrideByDevOption;
+
+ public DesktopExperienceFlag(BooleanSupplier flagFunction, boolean shouldOverrideByDevOption) {
+ this.mFlagFunction = flagFunction;
+ this.mShouldOverrideByDevOption = shouldOverrideByDevOption;
+ }
+
+ /**
+ * Determines state of flag based on the actual flag and desktop experience developer option
+ * overrides.
+ */
+ public boolean isTrue() {
+ return isFlagTrue(mFlagFunction, mShouldOverrideByDevOption);
+ }
+ }
+
+ private static final String TAG = "DesktopExperienceFlags";
+ // Function called to obtain aconfig flag value.
+ private final BooleanSupplier mFlagFunction;
+ // Whether the flag state should be affected by developer option.
+ private final boolean mShouldOverrideByDevOption;
+
+ // Local cache for toggle override, which is initialized once on its first access. It needs to
+ // be refreshed only on reboots as overridden state is expected to take effect on reboots.
+ @Nullable private static Boolean sCachedToggleOverride;
+
+ public static final String SYSTEM_PROPERTY_NAME = "persist.wm.debug.desktop_experience_devopts";
+
+ DesktopExperienceFlags(BooleanSupplier flagFunction, boolean shouldOverrideByDevOption) {
+ this.mFlagFunction = flagFunction;
+ this.mShouldOverrideByDevOption = shouldOverrideByDevOption;
+ }
+
+ /**
+ * Determines state of flag based on the actual flag and desktop experience developer option
+ * overrides.
+ */
+ public boolean isTrue() {
+ return isFlagTrue(mFlagFunction, mShouldOverrideByDevOption);
+ }
+
+ private static boolean isFlagTrue(
+ BooleanSupplier flagFunction, boolean shouldOverrideByDevOption) {
+ if (shouldOverrideByDevOption
+ && Flags.showDesktopExperienceDevOption()
+ && getToggleOverride()) {
+ return true;
+ }
+ return flagFunction.getAsBoolean();
+ }
+
+ private static boolean getToggleOverride() {
+ // If cached, return it
+ if (sCachedToggleOverride != null) {
+ return sCachedToggleOverride;
+ }
+
+ // Otherwise, fetch and cache it
+ boolean override = getToggleOverrideFromSystem();
+ sCachedToggleOverride = override;
+ Log.d(TAG, "Toggle override initialized to: " + override);
+ return override;
+ }
+
+ /** Returns the {@link ToggleOverride} from the system property.. */
+ private static boolean getToggleOverrideFromSystem() {
+ return SystemProperties.getBoolean(SYSTEM_PROPERTY_NAME, false);
+ }
+}
diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java
index d44b941082b5..e51ef4f6d04c 100644
--- a/core/java/android/window/DesktopModeFlags.java
+++ b/core/java/android/window/DesktopModeFlags.java
@@ -35,6 +35,10 @@ import java.util.function.BooleanSupplier;
* windowing features which are aiming for developer preview before their release. It allows
* developer option to override the default behavior of these flags.
*
+ * <p> The flags here will be controlled by either {@link
+ * Settings.Global#DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES} or the {@code
+ * persyst.wm.debug.desktop_experience_devopts} system property.
+ *
* <p>NOTE: Flags should only be added to this enum when they have received Product and UX
* alignment that the feature is ready for developer preview, otherwise just do a flag check.
*
@@ -89,7 +93,8 @@ public enum DesktopModeFlags {
ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX(
Flags::enableDesktopAppLaunchTransitionsBugfix, false),
INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC(
- Flags::includeTopTransparentFullscreenTaskInDesktopHeuristic, true);
+ Flags::includeTopTransparentFullscreenTaskInDesktopHeuristic, true),
+ ENABLE_MINIMIZE_BUTTON(Flags::enableMinimizeButton, true);
/**
* Flag class, to be used in case the enum cannot be used because the flag is not accessible.
@@ -109,7 +114,7 @@ public enum DesktopModeFlags {
/**
* Determines state of flag based on the actual flag and desktop mode developer option
- * overrides.
+ * or desktop experience developer option overrides.
*/
public boolean isTrue() {
return isFlagTrue(mFlagFunction, mShouldOverrideByDevOption);
diff --git a/core/java/android/window/OWNERS b/core/java/android/window/OWNERS
index 77c99b98cf4a..82d37244dc70 100644
--- a/core/java/android/window/OWNERS
+++ b/core/java/android/window/OWNERS
@@ -3,3 +3,4 @@ set noparent
include /services/core/java/com/android/server/wm/OWNERS
per-file DesktopModeFlags.java = file:/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS
+per-file DesktopExperienceFlags.java = file:/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index 73ebcdd8a07b..e35c3b80a58b 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -563,3 +563,23 @@ flag {
description: "Replace the freeform windowing dev options with a desktop experience one."
bug: "389092752"
}
+
+flag {
+ name: "enable_quickswitch_desktop_split_bugfix"
+ namespace: "lse_desktop_experience"
+ description: "Enables splitting QuickSwitch between fullscreen apps and Desktop workspaces."
+ bug: "345296916"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "enable_desktop_windowing_exit_by_minimize_transition_bugfix"
+ namespace: "lse_desktop_experience"
+ description: "Enables exit desktop windowing by minimize transition & motion polish changes"
+ bug: "390161102"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index ec3975205542..72cb9d1a20ac 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -51,6 +51,14 @@ oneway interface IStatusBar
void showWirelessChargingAnimation(int batteryLevel);
+ /**
+ * Sets the new IME window status.
+ *
+ * @param displayId The id of the display to which the IME is bound.
+ * @param vis The IME window visibility.
+ * @param backDisposition The IME back disposition mode.
+ * @param showImeSwitcher Whether the IME Switcher button should be shown.
+ */
void setImeWindowStatus(int displayId, int vis, int backDisposition, boolean showImeSwitcher);
void setWindowState(int display, int window, int state);
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index ec0954d5590a..1fa1e0bbc69a 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -61,6 +61,14 @@ interface IStatusBarService
void setIconVisibility(String slot, boolean visible);
@UnsupportedAppUsage
void removeIcon(String slot);
+ /**
+ * Sets the new IME window status.
+ *
+ * @param displayId The id of the display to which the IME is bound.
+ * @param vis The IME window visibility.
+ * @param backDisposition The IME back disposition mode.
+ * @param showImeSwitcher Whether the IME Switcher button should be shown.
+ */
void setImeWindowStatus(int displayId, int vis, int backDisposition, boolean showImeSwitcher);
void expandSettingsPanel(String subPanel);
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 639f5bff7614..91b25c2bda06 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -74,6 +74,7 @@ static struct bindernative_offsets_t
jmethodID mExecTransact;
jmethodID mGetInterfaceDescriptor;
jmethodID mTransactionCallback;
+ jmethodID mGetExtension;
// Object state.
jfieldID mObject;
@@ -489,8 +490,12 @@ public:
if (mVintf) {
::android::internal::Stability::markVintf(b.get());
}
- if (mExtension != nullptr) {
- b.get()->setExtension(mExtension);
+ if (mSetExtensionCalled) {
+ jobject javaIBinderObject = env->CallObjectMethod(obj, gBinderOffsets.mGetExtension);
+ sp<IBinder> extensionFromJava = ibinderForJavaObject(env, javaIBinderObject);
+ if (extensionFromJava != nullptr) {
+ b.get()->setExtension(extensionFromJava);
+ }
}
mBinder = b;
ALOGV("Creating JavaBinder %p (refs %p) for Object %p, weakCount=%" PRId32 "\n",
@@ -516,21 +521,12 @@ public:
mVintf = false;
}
- sp<IBinder> getExtension() {
- AutoMutex _l(mLock);
- sp<JavaBBinder> b = mBinder.promote();
- if (b != nullptr) {
- return b.get()->getExtension();
- }
- return mExtension;
- }
-
void setExtension(const sp<IBinder>& extension) {
AutoMutex _l(mLock);
- mExtension = extension;
+ mSetExtensionCalled = true;
sp<JavaBBinder> b = mBinder.promote();
if (b != nullptr) {
- b.get()->setExtension(mExtension);
+ b.get()->setExtension(extension);
}
}
@@ -542,8 +538,7 @@ private:
// is too much binder state here, we can think about making JavaBBinder an
// sp here (avoid recreating it)
bool mVintf = false;
-
- sp<IBinder> mExtension;
+ bool mSetExtensionCalled = false;
};
// ----------------------------------------------------------------------------
@@ -1249,10 +1244,6 @@ static void android_os_Binder_blockUntilThreadAvailable(JNIEnv* env, jobject cla
return IPCThreadState::self()->blockUntilThreadAvailable();
}
-static jobject android_os_Binder_getExtension(JNIEnv* env, jobject obj) {
- JavaBBinderHolder* jbh = (JavaBBinderHolder*) env->GetLongField(obj, gBinderOffsets.mObject);
- return javaObjectForIBinder(env, jbh->getExtension());
-}
static void android_os_Binder_setExtension(JNIEnv* env, jobject obj, jobject extensionObject) {
JavaBBinderHolder* jbh = (JavaBBinderHolder*) env->GetLongField(obj, gBinderOffsets.mObject);
@@ -1295,8 +1286,7 @@ static const JNINativeMethod gBinderMethods[] = {
{ "getNativeBBinderHolder", "()J", (void*)android_os_Binder_getNativeBBinderHolder },
{ "getNativeFinalizer", "()J", (void*)android_os_Binder_getNativeFinalizer },
{ "blockUntilThreadAvailable", "()V", (void*)android_os_Binder_blockUntilThreadAvailable },
- { "getExtension", "()Landroid/os/IBinder;", (void*)android_os_Binder_getExtension },
- { "setExtension", "(Landroid/os/IBinder;)V", (void*)android_os_Binder_setExtension },
+ { "setExtensionNative", "(Landroid/os/IBinder;)V", (void*)android_os_Binder_setExtension },
};
// clang-format on
@@ -1313,6 +1303,8 @@ static int int_register_android_os_Binder(JNIEnv* env)
gBinderOffsets.mTransactionCallback =
GetStaticMethodIDOrDie(env, clazz, "transactionCallback", "(IIII)V");
gBinderOffsets.mObject = GetFieldIDOrDie(env, clazz, "mObject", "J");
+ gBinderOffsets.mGetExtension = GetMethodIDOrDie(env, clazz, "getExtension",
+ "()Landroid/os/IBinder;");
return RegisterMethodsOrDie(
env, kBinderPathName,
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index b894d3a6888f..586cafdd2b57 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2009,6 +2009,10 @@
<!-- Component name of the built in wallpaper used to display bitmap wallpapers. This must not be null. -->
<string name="image_wallpaper_component" translatable="false">com.android.systemui/com.android.systemui.wallpapers.ImageWallpaper</string>
+ <!-- Component name of the built in wallpaper that is used when the user-selected wallpaper is
+ incompatible with the display's resolution or aspect ratio. -->
+ <string name="fallback_wallpaper_component" translatable="false">com.android.systemui/com.android.systemui.wallpapers.GradientColorWallpaper</string>
+
<!-- True if WallpaperService is enabled -->
<bool name="config_enableWallpaperService">true</bool>
diff --git a/core/res/res/values/public-final.xml b/core/res/res/values/public-final.xml
index d421944917ea..d8e89318a134 100644
--- a/core/res/res/values/public-final.xml
+++ b/core/res/res/values/public-final.xml
@@ -3922,4 +3922,86 @@
<public type="color" name="system_error_900" id="0x010600d0" />
<public type="color" name="system_error_1000" id="0x010600d1" />
+ <!-- ===============================================================
+ Resources added in version NEXT of the platform
+
+ NOTE: After this version of the platform is forked, changes cannot be made to the root
+ branch's groups for that release. Only merge changes to the forked platform branch.
+ =============================================================== -->
+ <eat-comment/>
+
+ <staging-public-group-final type="attr" first-id="0x01b70000">
+ <public name="removed_" />
+ <!-- @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") -->
+ <public name="adServiceTypes" />
+ <!-- @FlaggedApi("android.view.inputmethod.ime_switcher_revamp_api") -->
+ <public name="languageSettingsActivity"/>
+ <!-- @FlaggedApi(android.service.controls.flags.Flags.FLAG_HOME_PANEL_DREAM) -->
+ <public name="dreamCategory"/>
+ <!-- @FlaggedApi("android.permission.flags.replace_body_sensor_permission_enabled")
+ @hide @SystemApi -->
+ <public name="backgroundPermission"/>
+ <!-- @FlaggedApi(android.view.accessibility.supplemental_description) -->
+ <public name="supplementalDescription"/>
+ <!-- @FlaggedApi("android.security.enable_intent_matching_flags") -->
+ <public name="intentMatchingFlags"/>
+ <!-- @FlaggedApi(android.view.inputmethod.Flags.FLAG_IME_SWITCHER_REVAMP_API) -->
+ <public name="layoutLabel"/>
+ <public name="removed_" />
+ <public name="removed_" />
+ <!-- @FlaggedApi(android.content.pm.Flags.FLAG_APP_COMPAT_OPTION_16KB) -->
+ <public name="pageSizeCompat" />
+ <!-- @FlaggedApi(android.nfc.Flags.FLAG_NFC_ASSOCIATED_ROLE_SERVICES) -->
+ <public name="wantsRoleHolderPriority"/>
+ <!-- @FlaggedApi(android.sdk.Flags.FLAG_MAJOR_MINOR_VERSIONING_SCHEME) -->
+ <public name="minSdkVersionFull"/>
+ <public name="removed_" />
+ <public name="removed_" />
+ <public name="removed_" />
+ <public name="removed_" />
+ </staging-public-group-final>
+
+ <!-- @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") -->
+ <public type="attr" name="adServiceTypes" id="0x010106a4" />
+ <!-- @FlaggedApi("android.view.inputmethod.ime_switcher_revamp_api") -->
+ <public type="attr" name="languageSettingsActivity" id="0x010106a5" />
+ <!-- @FlaggedApi(android.service.controls.flags.Flags.FLAG_HOME_PANEL_DREAM) -->
+ <public type="attr" name="dreamCategory" id="0x010106a6" />
+ <!-- @FlaggedApi("android.permission.flags.replace_body_sensor_permission_enabled")
+ @hide @SystemApi -->
+ <public type="attr" name="backgroundPermission" id="0x010106a7" />
+ <!-- @FlaggedApi(android.view.accessibility.supplemental_description) -->
+ <public type="attr" name="supplementalDescription" id="0x010106a8" />
+ <!-- @FlaggedApi("android.security.enable_intent_matching_flags") -->
+ <public type="attr" name="intentMatchingFlags" id="0x010106a9" />
+ <!-- @FlaggedApi(android.view.inputmethod.Flags.FLAG_IME_SWITCHER_REVAMP_API) -->
+ <public type="attr" name="layoutLabel" id="0x010106aa" />
+ <!-- @FlaggedApi(android.content.pm.Flags.FLAG_APP_COMPAT_OPTION_16KB) -->
+ <public type="attr" name="pageSizeCompat" id="0x010106ab" />
+ <!-- @FlaggedApi(android.nfc.Flags.FLAG_NFC_ASSOCIATED_ROLE_SERVICES) -->
+ <public type="attr" name="wantsRoleHolderPriority" id="0x010106ac" />
+ <!-- @FlaggedApi(android.sdk.Flags.FLAG_MAJOR_MINOR_VERSIONING_SCHEME) -->
+ <public type="attr" name="minSdkVersionFull" id="0x010106ad" />
+
+ <staging-public-group-final type="string" first-id="0x01b40000">
+ <!-- @FlaggedApi(android.content.pm.Flags.FLAG_SDK_DEPENDENCY_INSTALLER)
+ @hide @SystemApi -->
+ <public name="config_systemDependencyInstaller" />
+ <!-- @hide @SystemApi -->
+ <public name="removed_config_defaultReservedForTestingProfileGroupExclusivity" />
+ <!-- @FlaggedApi(android.permission.flags.Flags.FLAG_SYSTEM_VENDOR_INTELLIGENCE_ROLE_ENABLED)
+ @hide @SystemApi -->
+ <public name="config_systemVendorIntelligence" />
+ <public name="removed_" />
+ <public name="removed_" />
+ <public name="removed_" />
+ </staging-public-group-final>
+
+ <!-- @FlaggedApi(android.content.pm.Flags.FLAG_SDK_DEPENDENCY_INSTALLER)
+ @hide @SystemApi -->
+ <public type="string" name="config_systemDependencyInstaller" id="0x0104004a" />
+ <!-- @FlaggedApi(android.permission.flags.Flags.FLAG_SYSTEM_VENDOR_INTELLIGENCE_ROLE_ENABLED)
+ @hide @SystemApi -->
+ <public type="string" name="config_systemVendorIntelligence" id="0x0104004b" />
+
</resources>
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index a0c4c13a8702..2d411d0268b3 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -109,34 +109,13 @@
=============================================================== -->
<eat-comment/>
- <staging-public-group type="attr" first-id="0x01b70000">
+ <staging-public-group type="attr" first-id="0x01b30000">
<!-- @FlaggedApi("android.content.pm.sdk_lib_independence") -->
<public name="optional"/>
- <!-- @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") -->
- <public name="adServiceTypes" />
- <!-- @FlaggedApi("android.view.inputmethod.ime_switcher_revamp_api") -->
- <public name="languageSettingsActivity"/>
- <!-- @FlaggedApi(android.service.controls.flags.Flags.FLAG_HOME_PANEL_DREAM) -->
- <public name="dreamCategory"/>
- <!-- @FlaggedApi("android.permission.flags.replace_body_sensor_permission_enabled")
- @hide @SystemApi -->
- <public name="backgroundPermission"/>
- <!-- @FlaggedApi(android.view.accessibility.supplemental_description) -->
- <public name="supplementalDescription"/>
- <!-- @FlaggedApi("android.security.enable_intent_matching_flags") -->
- <public name="intentMatchingFlags"/>
- <!-- @FlaggedApi(android.view.inputmethod.Flags.FLAG_IME_SWITCHER_REVAMP_API) -->
- <public name="layoutLabel"/>
<!-- @FlaggedApi(android.content.pm.Flags.FLAG_CHANGE_LAUNCHER_BADGING) -->
<public name="alternateLauncherIcons"/>
<!-- @FlaggedApi(android.content.pm.Flags.FLAG_CHANGE_LAUNCHER_BADGING) -->
<public name="alternateLauncherLabels"/>
- <!-- @FlaggedApi(android.content.pm.Flags.FLAG_APP_COMPAT_OPTION_16KB) -->
- <public name="pageSizeCompat" />
- <!-- @FlaggedApi(android.nfc.Flags.FLAG_NFC_ASSOCIATED_ROLE_SERVICES) -->
- <public name="wantsRoleHolderPriority"/>
- <!-- @FlaggedApi(android.sdk.Flags.FLAG_MAJOR_MINOR_VERSIONING_SCHEME) -->
- <public name="minSdkVersionFull"/>
<!-- @hide Only for device overlay to use this. -->
<public name="pointerIconVectorFill"/>
<!-- @hide Only for device overlay to use this. -->
@@ -147,39 +126,27 @@
<public name="pointerIconVectorStrokeInverse"/>
</staging-public-group>
- <staging-public-group type="id" first-id="0x01b60000">
+ <staging-public-group type="id" first-id="0x01b20000">
<!-- @FlaggedApi(android.appwidget.flags.Flags.FLAG_ENGAGEMENT_METRICS) -->
<public name="remoteViewsMetricsId"/>
</staging-public-group>
- <staging-public-group type="style" first-id="0x01b50000">
+ <staging-public-group type="style" first-id="0x01b10000">
</staging-public-group>
- <staging-public-group type="string" first-id="0x01b40000">
- <!-- @FlaggedApi(android.content.pm.Flags.FLAG_SDK_DEPENDENCY_INSTALLER)
- @hide @SystemApi -->
- <public name="config_systemDependencyInstaller" />
- <!-- @hide @SystemApi -->
- <public name="removed_config_defaultReservedForTestingProfileGroupExclusivity" />
- <!-- @FlaggedApi(android.permission.flags.Flags.FLAG_SYSTEM_VENDOR_INTELLIGENCE_ROLE_ENABLED)
- @hide @SystemApi -->
- <public name="config_systemVendorIntelligence" />
-
+ <staging-public-group type="string" first-id="0x01b00000">
<!-- @FlaggedApi(android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE_MODULE)
@hide @SystemApi -->
<public name="config_defaultOnDeviceIntelligenceService" />
-
<!-- @FlaggedApi(android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE_MODULE)
@hide @SystemApi -->
<public name="config_defaultOnDeviceSandboxedInferenceService" />
-
<!-- @FlaggedApi(android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE_MODULE)
@hide @SystemApi -->
<public name="config_defaultOnDeviceIntelligenceDeviceConfigNamespace" />
-
</staging-public-group>
- <staging-public-group type="dimen" first-id="0x01b30000">
+ <staging-public-group type="dimen" first-id="0x01af0000">
<!-- @FlaggedApi(android.os.Flags.FLAG_MATERIAL_MOTION_TOKENS)-->
<public name="config_motionStandardFastSpatialDamping"/>
<!-- @FlaggedApi(android.os.Flags.FLAG_MATERIAL_MOTION_TOKENS)-->
@@ -216,7 +183,7 @@
<public name="config_shapeCornerRadiusXlarge"/>
</staging-public-group>
- <staging-public-group type="color" first-id="0x01b20000">
+ <staging-public-group type="color" first-id="0x01ae0000">
<!-- @FlaggedApi(android.os.Flags.FLAG_MATERIAL_COLORS_10_2024)-->
<public name="system_inverse_on_surface_light"/>
<!-- @FlaggedApi(android.os.Flags.FLAG_MATERIAL_COLORS_10_2024)-->
@@ -243,28 +210,28 @@
<public name="system_surface_tint_dark"/>
</staging-public-group>
- <staging-public-group type="array" first-id="0x01b10000">
+ <staging-public-group type="array" first-id="0x01ad0000">
</staging-public-group>
- <staging-public-group type="drawable" first-id="0x01b00000">
+ <staging-public-group type="drawable" first-id="0x01ac0000">
</staging-public-group>
- <staging-public-group type="layout" first-id="0x01af0000">
+ <staging-public-group type="layout" first-id="0x01ab0000">
</staging-public-group>
- <staging-public-group type="anim" first-id="0x01ae0000">
+ <staging-public-group type="anim" first-id="0x01aa0000">
</staging-public-group>
- <staging-public-group type="animator" first-id="0x01ad0000">
+ <staging-public-group type="animator" first-id="0x01a90000">
</staging-public-group>
- <staging-public-group type="interpolator" first-id="0x01ac0000">
+ <staging-public-group type="interpolator" first-id="0x01a80000">
</staging-public-group>
- <staging-public-group type="mipmap" first-id="0x01ab0000">
+ <staging-public-group type="mipmap" first-id="0x01a70000">
</staging-public-group>
- <staging-public-group type="integer" first-id="0x01aa0000">
+ <staging-public-group type="integer" first-id="0x01a60000">
<!-- @FlaggedApi(android.os.Flags.FLAG_MATERIAL_MOTION_TOKENS)-->
<public name="config_motionStandardFastSpatialStiffness"/>
<!-- @FlaggedApi(android.os.Flags.FLAG_MATERIAL_MOTION_TOKENS)-->
@@ -291,16 +258,16 @@
<public name="config_motionExpressiveSlowEffectStiffness"/>
</staging-public-group>
- <staging-public-group type="transition" first-id="0x01a90000">
+ <staging-public-group type="transition" first-id="0x01a50000">
</staging-public-group>
- <staging-public-group type="raw" first-id="0x01a80000">
+ <staging-public-group type="raw" first-id="0x01a40000">
</staging-public-group>
- <staging-public-group type="bool" first-id="0x01a70000">
+ <staging-public-group type="bool" first-id="0x01a30000">
</staging-public-group>
- <staging-public-group type="fraction" first-id="0x01a60000">
+ <staging-public-group type="fraction" first-id="0x01a20000">
</staging-public-group>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 8315e3cf1a11..772a7413a4a7 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2270,6 +2270,7 @@
<java-symbol type="string" name="heavy_weight_notification" />
<java-symbol type="string" name="heavy_weight_notification_detail" />
<java-symbol type="string" name="image_wallpaper_component" />
+ <java-symbol type="string" name="fallback_wallpaper_component" />
<java-symbol type="string" name="input_method_binding_label" />
<java-symbol type="string" name="input_method_ime_switch_long_click_action_desc" />
<java-symbol type="string" name="launch_warning_original" />
diff --git a/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java b/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java
index 1bdb006c3465..9f1580cb8b57 100644
--- a/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java
+++ b/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java
@@ -124,7 +124,8 @@ public class NotificationRankingUpdateTest {
getRankingAdjustment(i),
isBubble(i),
getProposedImportance(i),
- hasSensitiveContent(i)
+ hasSensitiveContent(i),
+ getSummarization(i)
);
rankings[i] = ranking;
}
@@ -334,6 +335,17 @@ public class NotificationRankingUpdateTest {
}
/**
+ * Produces a String that can be used to represent getSummarization, based on the provided
+ * index.
+ */
+ public static String getSummarization(int index) {
+ if ((android.app.Flags.nmSummarizationUi() || android.app.Flags.nmSummarization())) {
+ return "summary " + index;
+ }
+ return null;
+ }
+
+ /**
* Produces a boolean that can be used to represent isBubble, based on the provided index.
*/
public static boolean isBubble(int index) {
@@ -461,7 +473,8 @@ public class NotificationRankingUpdateTest {
/* rankingAdjustment= */ 0,
/* isBubble= */ false,
/* proposedImportance= */ 0,
- /* sensitiveContent= */ false
+ /* sensitiveContent= */ false,
+ /* summarization = */ null
);
return ranking;
}
@@ -550,7 +563,8 @@ public class NotificationRankingUpdateTest {
tweak.getRankingAdjustment(),
tweak.isBubble(),
tweak.getProposedImportance(),
- tweak.hasSensitiveContent()
+ tweak.hasSensitiveContent(),
+ tweak.getSummarization()
);
assertNotEquals(nru, nru2);
}
diff --git a/core/tests/coretests/src/android/window/DesktopExperienceFlagsTest.java b/core/tests/coretests/src/android/window/DesktopExperienceFlagsTest.java
new file mode 100644
index 000000000000..cc06f3d21332
--- /dev/null
+++ b/core/tests/coretests/src/android/window/DesktopExperienceFlagsTest.java
@@ -0,0 +1,164 @@
+/*
+ * 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 android.window;
+
+import static com.android.window.flags.Flags.FLAG_SHOW_DESKTOP_EXPERIENCE_DEV_OPTION;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assume.assumeTrue;
+import static org.junit.Assume.assumeFalse;
+
+import android.content.Context;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.FlagsParameterization;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.support.test.uiautomator.UiDevice;
+import android.window.DesktopExperienceFlags.DesktopExperienceFlag;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.window.flags.Flags;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
+import java.lang.reflect.Field;
+import java.util.List;
+
+/**
+ * Test class for {@link android.window.DesktopExperienceFlags}
+ *
+ * <p>Build/Install/Run: atest FrameworksCoreTests:DesktopExperienceFlagsTest
+ */
+@SmallTest
+@Presubmit
+@RunWith(ParameterizedAndroidJunit4.class)
+public class DesktopExperienceFlagsTest {
+
+ @Parameters(name = "{0}")
+ public static List<FlagsParameterization> getParams() {
+ return FlagsParameterization.allCombinationsOf(FLAG_SHOW_DESKTOP_EXPERIENCE_DEV_OPTION);
+ }
+
+ @Rule public SetFlagsRule mSetFlagsRule;
+
+ private UiDevice mUiDevice;
+ private Context mContext;
+ private boolean mLocalFlagValue = false;
+ private final DesktopExperienceFlag mOverriddenLocalFlag =
+ new DesktopExperienceFlag(() -> mLocalFlagValue, true);
+ private final DesktopExperienceFlag mNotOverriddenLocalFlag =
+ new DesktopExperienceFlag(() -> mLocalFlagValue, false);
+
+ private static final String OVERRIDE_OFF_SETTING = "0";
+ private static final String OVERRIDE_ON_SETTING = "1";
+ private static final String OVERRIDE_INVALID_SETTING = "garbage";
+
+ public DesktopExperienceFlagsTest(FlagsParameterization flags) {
+ mSetFlagsRule = new SetFlagsRule(flags);
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ setSysProp(null);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ resetCache();
+ setSysProp(null);
+ }
+
+ @Test
+ public void isTrue_overrideOff_featureFlagOn_returnsTrue() throws Exception {
+ mLocalFlagValue = true;
+ setSysProp(OVERRIDE_OFF_SETTING);
+
+ assertThat(mOverriddenLocalFlag.isTrue()).isTrue();
+ assertThat(mNotOverriddenLocalFlag.isTrue()).isTrue();
+ }
+
+ @Test
+ public void isTrue_overrideOn_featureFlagOn_returnsTrue() throws Exception {
+ mLocalFlagValue = true;
+ setSysProp(OVERRIDE_ON_SETTING);
+
+ assertThat(mOverriddenLocalFlag.isTrue()).isTrue();
+ assertThat(mNotOverriddenLocalFlag.isTrue()).isTrue();
+ }
+
+ @Test
+ public void isTrue_overrideOff_featureFlagOff_returnsFalse() throws Exception {
+ mLocalFlagValue = false;
+ setSysProp(OVERRIDE_OFF_SETTING);
+
+ assertThat(mOverriddenLocalFlag.isTrue()).isFalse();
+ assertThat(mNotOverriddenLocalFlag.isTrue()).isFalse();
+ }
+
+ @Test
+ public void isTrue_devOptionEnabled_overrideOn_featureFlagOff() throws Exception {
+ assumeTrue(Flags.showDesktopExperienceDevOption());
+ mLocalFlagValue = false;
+ setSysProp(OVERRIDE_ON_SETTING);
+
+ assertThat(mOverriddenLocalFlag.isTrue()).isTrue();
+ assertThat(mNotOverriddenLocalFlag.isTrue()).isFalse();
+ }
+
+ @Test
+ public void isTrue_devOptionDisabled_overrideOn_featureFlagOff_returnsFalse() throws Exception {
+ assumeFalse(Flags.showDesktopExperienceDevOption());
+ mLocalFlagValue = false;
+ setSysProp(OVERRIDE_ON_SETTING);
+
+ assertThat(mOverriddenLocalFlag.isTrue()).isFalse();
+ assertThat(mNotOverriddenLocalFlag.isTrue()).isFalse();
+ }
+
+ private void setSysProp(String value) throws Exception {
+ if (value == null) {
+ resetSysProp();
+ } else {
+ mUiDevice.executeShellCommand(
+ "setprop " + DesktopModeFlags.SYSTEM_PROPERTY_NAME + " " + value);
+ }
+ }
+
+ private void resetSysProp() throws Exception {
+ mUiDevice.executeShellCommand("setprop " + DesktopModeFlags.SYSTEM_PROPERTY_NAME + " ''");
+ }
+
+ private void resetCache() throws Exception {
+ Field cachedToggleOverride =
+ DesktopExperienceFlags.class.getDeclaredField("sCachedToggleOverride");
+ cachedToggleOverride.setAccessible(true);
+ cachedToggleOverride.set(null, null);
+ }
+}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt
index dd387b382dc6..09a93d501f8e 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt
@@ -296,5 +296,9 @@ class BubbleControllerBubbleBarTest {
override fun onBubbleStateChange(update: BubbleBarUpdate?) {}
override fun animateBubbleBarLocation(location: BubbleBarLocation?) {}
+
+ override fun onDragItemOverBubbleBarDragZone(location: BubbleBarLocation) {}
+
+ override fun onItemDraggedOutsideBubbleBarDropZone() {}
}
}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java
index 2ca011bfe000..0a1e3b9495a0 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java
@@ -111,7 +111,7 @@ public class GroupedTaskInfo implements Parcelable {
* Create new for a pair of tasks in split screen
*/
public static GroupedTaskInfo forSplitTasks(@NonNull TaskInfo task1,
- @NonNull TaskInfo task2, @Nullable SplitBounds splitBounds) {
+ @NonNull TaskInfo task2, @NonNull SplitBounds splitBounds) {
return new GroupedTaskInfo(List.of(task1, task2), splitBounds, TYPE_SPLIT,
null /* minimizedFreeformTasks */);
}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java
index 840de2c86f92..4d00c74155a8 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java
@@ -127,6 +127,46 @@ public class TransitionUtil {
}
/**
+ * Check if all changes in this transition are only ordering changes. If so, we won't animate.
+ */
+ public static boolean isAllOrderOnly(TransitionInfo info) {
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ if (!isOrderOnly(info.getChanges().get(i))) return false;
+ }
+ return true;
+ }
+
+ /**
+ * Look through a transition and see if all non-closing changes are no-animation. If so, no
+ * animation should play.
+ */
+ public static boolean isAllNoAnimation(TransitionInfo info) {
+ if (isClosingType(info.getType())) {
+ // no-animation is only relevant for launching (open) activities.
+ return false;
+ }
+ boolean hasNoAnimation = false;
+ final int changeSize = info.getChanges().size();
+ for (int i = changeSize - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ if (isClosingType(change.getMode())) {
+ // ignore closing apps since they are a side-effect of the transition and don't
+ // animate.
+ continue;
+ }
+ if (change.hasFlags(TransitionInfo.FLAG_NO_ANIMATION)) {
+ hasNoAnimation = true;
+ } else if (!isOrderOnly(change) && !change.hasFlags(TransitionInfo.FLAG_IS_OCCLUDED)) {
+ // Ignore the order only or occluded changes since they shouldn't be visible during
+ // animation. For anything else, we need to animate if at-least one relevant
+ // participant *is* animated,
+ return false;
+ }
+ }
+ return hasNoAnimation;
+ }
+
+ /**
* Filter that selects leaf-tasks only. THIS IS ORDER-DEPENDENT! For it to work properly, you
* MUST call `test` in the same order that the changes appear in the TransitionInfo.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index e8e25e20d8d8..e68a98fb7f21 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -28,6 +28,7 @@ import android.annotation.Nullable;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Person;
+import android.app.TaskInfo;
import android.content.Context;
import android.content.Intent;
import android.content.LocusId;
@@ -57,6 +58,7 @@ import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
import com.android.wm.shell.shared.annotations.ShellMainThread;
import com.android.wm.shell.shared.bubbles.BubbleInfo;
import com.android.wm.shell.shared.bubbles.ParcelableFlyoutMessage;
+import com.android.wm.shell.taskview.TaskView;
import java.io.PrintWriter;
import java.util.List;
@@ -204,6 +206,13 @@ public class Bubble implements BubbleViewProvider {
private Intent mAppIntent;
/**
+ * Set while preparing a transition for animation. Several steps are needed before animation
+ * starts, so this is used to detect and route associated events to the coordinating transition.
+ */
+ @Nullable
+ private BubbleTransitions.BubbleTransition mPreparingTransition;
+
+ /**
* Create a bubble with limited information based on given {@link ShortcutInfo}.
* Note: Currently this is only being used when the bubble is persisted to disk.
*/
@@ -280,6 +289,30 @@ public class Bubble implements BubbleViewProvider {
mShortcutInfo = info;
}
+ private Bubble(
+ TaskInfo task,
+ UserHandle user,
+ @Nullable Icon icon,
+ String key,
+ @ShellMainThread Executor mainExecutor,
+ @ShellBackgroundThread Executor bgExecutor) {
+ mGroupKey = null;
+ mLocusId = null;
+ mFlags = 0;
+ mUser = user;
+ mIcon = icon;
+ mIsAppBubble = true;
+ mKey = key;
+ mShowBubbleUpdateDot = false;
+ mMainExecutor = mainExecutor;
+ mBgExecutor = bgExecutor;
+ mTaskId = task.taskId;
+ mAppIntent = null;
+ mDesiredHeight = Integer.MAX_VALUE;
+ mPackageName = task.baseActivity.getPackageName();
+ }
+
+
/** Creates an app bubble. */
public static Bubble createAppBubble(Intent intent, UserHandle user, @Nullable Icon icon,
@ShellMainThread Executor mainExecutor, @ShellBackgroundThread Executor bgExecutor) {
@@ -291,6 +324,16 @@ public class Bubble implements BubbleViewProvider {
mainExecutor, bgExecutor);
}
+ /** Creates a task bubble. */
+ public static Bubble createTaskBubble(TaskInfo info, UserHandle user, @Nullable Icon icon,
+ @ShellMainThread Executor mainExecutor, @ShellBackgroundThread Executor bgExecutor) {
+ return new Bubble(info,
+ user,
+ icon,
+ getAppBubbleKeyForTask(info),
+ mainExecutor, bgExecutor);
+ }
+
/** Creates a shortcut bubble. */
public static Bubble createShortcutBubble(
ShortcutInfo info,
@@ -316,6 +359,15 @@ public class Bubble implements BubbleViewProvider {
return info.getPackage() + ":" + info.getUserId() + ":" + info.getId();
}
+ /**
+ * Returns the key for an app bubble from an app with package name, {@code packageName} on an
+ * Android user, {@code user}.
+ */
+ public static String getAppBubbleKeyForTask(TaskInfo taskInfo) {
+ Objects.requireNonNull(taskInfo);
+ return KEY_APP_BUBBLE + ":" + taskInfo.taskId;
+ }
+
@VisibleForTesting(visibility = PRIVATE)
public Bubble(@NonNull final BubbleEntry entry,
final Bubbles.BubbleMetadataFlagListener listener,
@@ -469,6 +521,10 @@ public class Bubble implements BubbleViewProvider {
return mBubbleTaskView;
}
+ public TaskView getTaskView() {
+ return mBubbleTaskView.getTaskView();
+ }
+
/**
* @return the ShortcutInfo id if it exists, or the metadata shortcut id otherwise.
*/
@@ -486,6 +542,10 @@ public class Bubble implements BubbleViewProvider {
return (mMetadataShortcutId != null && !mMetadataShortcutId.isEmpty());
}
+ BubbleTransitions.BubbleTransition getPreparingTransition() {
+ return mPreparingTransition;
+ }
+
/**
* Call this to clean up the task for the bubble. Ensure this is always called when done with
* the bubble.
@@ -556,6 +616,13 @@ public class Bubble implements BubbleViewProvider {
}
/**
+ * Sets the current bubble-transition that is coordinating a change in this bubble.
+ */
+ void setPreparingTransition(BubbleTransitions.BubbleTransition transit) {
+ mPreparingTransition = transit;
+ }
+
+ /**
* Sets whether this bubble is considered text changed. This method is purely for
* testing.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 4f9028e8aaf3..1f463295233f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -110,6 +110,7 @@ import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
import com.android.wm.shell.shared.annotations.ShellMainThread;
+import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
import com.android.wm.shell.shared.bubbles.BubbleBarUpdate;
import com.android.wm.shell.sysui.ConfigurationChangeListener;
@@ -287,6 +288,8 @@ public class BubbleController implements ConfigurationChangeListener,
/** Used to send updates to the views from {@link #mBubbleDataListener}. */
private BubbleViewCallback mBubbleViewCallback;
+ private final BubbleTransitions mBubbleTransitions;
+
public BubbleController(Context context,
ShellInit shellInit,
ShellCommandHandler shellCommandHandler,
@@ -350,12 +353,16 @@ public class BubbleController implements ConfigurationChangeListener,
context.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.importance_ring_stroke_width));
mDisplayController = displayController;
+ final TaskViewTransitions tvTransitions;
if (TaskViewTransitions.useRepo()) {
- mTaskViewController = new TaskViewTransitions(transitions, taskViewRepository,
- organizer, syncQueue);
+ tvTransitions = new TaskViewTransitions(transitions, taskViewRepository, organizer,
+ syncQueue);
} else {
- mTaskViewController = taskViewTransitions;
+ tvTransitions = taskViewTransitions;
}
+ mTaskViewController = tvTransitions;
+ mBubbleTransitions = new BubbleTransitions(transitions, organizer, taskViewRepository, data,
+ tvTransitions, context);
mTransitions = transitions;
mOneHandedOptional = oneHandedOptional;
mDragAndDropController = dragAndDropController;
@@ -1456,7 +1463,19 @@ public class BubbleController implements ConfigurationChangeListener,
* @param taskInfo the task.
*/
public void expandStackAndSelectBubble(ActivityManager.RunningTaskInfo taskInfo) {
- // TODO(384976265): Not implemented yet
+ if (!BubbleAnythingFlagHelper.enableBubbleToFullscreen()) return;
+ Bubble b = mBubbleData.getOrCreateBubble(taskInfo); // Removes from overflow
+ ProtoLog.v(WM_SHELL_BUBBLES, "expandStackAndSelectBubble - intent=%s", taskInfo.taskId);
+ if (b.isInflated()) {
+ mBubbleData.setSelectedBubbleAndExpandStack(b);
+ } else {
+ b.enable(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);
+ // Lazy init stack view when a bubble is created
+ ensureBubbleViewsAndWindowCreated();
+ mBubbleTransitions.startConvertToBubble(b, taskInfo, mExpandedViewManager,
+ mBubbleTaskViewFactory, mBubblePositioner, mLogger, mStackView, mLayerView,
+ mBubbleIconFactory, mInflateSynchronously);
+ }
}
/**
@@ -2261,9 +2280,16 @@ public class BubbleController implements ConfigurationChangeListener,
private void showExpandedViewForBubbleBar() {
BubbleViewProvider selectedBubble = mBubbleData.getSelectedBubble();
- if (selectedBubble != null && mLayerView != null) {
- mLayerView.showExpandedView(selectedBubble);
+ if (selectedBubble == null) return;
+ if (selectedBubble instanceof Bubble) {
+ final Bubble bubble = (Bubble) selectedBubble;
+ if (bubble.getPreparingTransition() != null) {
+ bubble.getPreparingTransition().continueExpand();
+ return;
+ }
}
+ if (mLayerView == null) return;
+ mLayerView.showExpandedView(selectedBubble);
}
private void collapseExpandedViewForBubbleBar() {
@@ -2613,6 +2639,17 @@ public class BubbleController implements ConfigurationChangeListener,
public void animateBubbleBarLocation(BubbleBarLocation location) {
mListener.call(l -> l.animateBubbleBarLocation(location));
}
+
+ @Override
+ public void onDragItemOverBubbleBarDragZone(
+ @NonNull BubbleBarLocation location) {
+ mListener.call(l -> l.onDragItemOverBubbleBarDragZone(location));
+ }
+
+ @Override
+ public void onItemDraggedOutsideBubbleBarDropZone() {
+ mListener.call(IBubblesListener::onItemDraggedOutsideBubbleBarDropZone);
+ }
};
IBubblesImpl(BubbleController controller) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index 74302094a296..96d0f6d5654e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -22,6 +22,7 @@ import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES;
import android.annotation.NonNull;
import android.app.PendingIntent;
+import android.app.TaskInfo;
import android.content.Context;
import android.content.Intent;
import android.content.LocusId;
@@ -470,6 +471,17 @@ public class BubbleData {
return bubbleToReturn;
}
+ Bubble getOrCreateBubble(TaskInfo taskInfo) {
+ UserHandle user = UserHandle.of(mCurrentUserId);
+ String bubbleKey = Bubble.getAppBubbleKeyForTask(taskInfo);
+ Bubble bubbleToReturn = findAndRemoveBubbleFromOverflow(bubbleKey);
+ if (bubbleToReturn == null) {
+ bubbleToReturn = Bubble.createTaskBubble(taskInfo, user, null, mMainExecutor,
+ mBgExecutor);
+ }
+ return bubbleToReturn;
+ }
+
@Nullable
private Bubble findAndRemoveBubbleFromOverflow(String key) {
Bubble bubbleToReturn = getBubbleInStackWithKey(key);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
index 89c038b4a26b..ae84f449c0e4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
@@ -109,7 +109,9 @@ public class BubbleTaskViewHelper {
MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS);
final boolean isShortcutBubble = (mBubble.hasMetadataShortcutId()
|| (mBubble.getShortcutInfo() != null && Flags.enableBubbleAnything()));
- if (mBubble.isAppBubble()) {
+ if (mBubble.getPreparingTransition() != null) {
+ mBubble.getPreparingTransition().surfaceCreated();
+ } else if (mBubble.isAppBubble()) {
Context context =
mContext.createContextAsUser(
mBubble.getUser(), Context.CONTEXT_RESTRICTED);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
new file mode 100644
index 000000000000..e37844f53b11
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
@@ -0,0 +1,319 @@
+/*
+ * 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.wm.shell.bubbles;
+
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.view.WindowManager.TRANSIT_CHANGE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.TaskInfo;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.util.Slog;
+import android.view.SurfaceControl;
+import android.view.SurfaceView;
+import android.window.TransitionInfo;
+import android.window.TransitionRequestInfo;
+import android.window.WindowContainerTransaction;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.launcher3.icons.BubbleIconFactory;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView;
+import com.android.wm.shell.bubbles.bar.BubbleBarLayerView;
+import com.android.wm.shell.taskview.TaskView;
+import com.android.wm.shell.taskview.TaskViewRepository;
+import com.android.wm.shell.taskview.TaskViewTaskController;
+import com.android.wm.shell.taskview.TaskViewTransitions;
+import com.android.wm.shell.transition.Transitions;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Implements transition coordination for bubble operations.
+ */
+public class BubbleTransitions {
+ private static final String TAG = "BubbleTransitions";
+
+ @NonNull final Transitions mTransitions;
+ @NonNull final ShellTaskOrganizer mTaskOrganizer;
+ @NonNull final TaskViewRepository mRepository;
+ @NonNull final Executor mMainExecutor;
+ @NonNull final BubbleData mBubbleData;
+ @NonNull final TaskViewTransitions mTaskViewTransitions;
+ @NonNull final Context mContext;
+
+ BubbleTransitions(@NonNull Transitions transitions, @NonNull ShellTaskOrganizer organizer,
+ @NonNull TaskViewRepository repository, @NonNull BubbleData bubbleData,
+ @NonNull TaskViewTransitions taskViewTransitions, Context context) {
+ mTransitions = transitions;
+ mTaskOrganizer = organizer;
+ mRepository = repository;
+ mMainExecutor = transitions.getMainExecutor();
+ mBubbleData = bubbleData;
+ mTaskViewTransitions = taskViewTransitions;
+ mContext = context;
+ }
+
+ /**
+ * Starts a convert-to-bubble transition.
+ *
+ * @see ConvertToBubble
+ */
+ public BubbleTransition startConvertToBubble(Bubble bubble, TaskInfo taskInfo,
+ BubbleExpandedViewManager expandedViewManager, BubbleTaskViewFactory factory,
+ BubblePositioner positioner, BubbleLogger logger, BubbleStackView stackView,
+ BubbleBarLayerView layerView, BubbleIconFactory iconFactory,
+ boolean inflateSync) {
+ ConvertToBubble convert = new ConvertToBubble(bubble, taskInfo, mContext,
+ expandedViewManager, factory, positioner, logger, stackView, layerView, iconFactory,
+ inflateSync);
+ return convert;
+ }
+
+ /**
+ * Interface to a bubble-specific transition. Bubble transitions have a multi-step lifecycle
+ * in order to coordinate with the bubble view logic. These steps are communicated on this
+ * interface.
+ */
+ interface BubbleTransition {
+ default void surfaceCreated() {}
+ default void continueExpand() {}
+ void skip();
+ }
+
+ /**
+ * BubbleTransition that coordinates the process of a non-bubble task becoming a bubble. The
+ * steps are as follows:
+ *
+ * 1. Start inflating the bubble view
+ * 2. Once inflated (but not-yet visible), tell WM to do the shell-transition.
+ * 3. Transition becomes ready, so notify Launcher
+ * 4. Launcher responds with showExpandedView which calls continueExpand() to make view visible
+ * 5. Surface is created which kicks off actual animation
+ *
+ * So, constructor -> onInflated -> startAnimation -> continueExpand -> surfaceCreated.
+ *
+ * continueExpand and surfaceCreated are set-up to happen in either order, though, to support
+ * UX/timing adjustments.
+ */
+ @VisibleForTesting
+ class ConvertToBubble implements Transitions.TransitionHandler, BubbleTransition {
+ final BubbleBarLayerView mLayerView;
+ Bubble mBubble;
+ IBinder mTransition;
+ Transitions.TransitionFinishCallback mFinishCb;
+ WindowContainerTransaction mFinishWct = null;
+ final Rect mStartBounds = new Rect();
+ SurfaceControl mSnapshot = null;
+ TaskInfo mTaskInfo;
+ boolean mFinishedExpand = false;
+ BubbleViewProvider mPriorBubble = null;
+
+ private SurfaceControl.Transaction mFinishT;
+ private SurfaceControl mTaskLeash;
+
+ ConvertToBubble(Bubble bubble, TaskInfo taskInfo, Context context,
+ BubbleExpandedViewManager expandedViewManager, BubbleTaskViewFactory factory,
+ BubblePositioner positioner, BubbleLogger logger, BubbleStackView stackView,
+ BubbleBarLayerView layerView, BubbleIconFactory iconFactory, boolean inflateSync) {
+ mBubble = bubble;
+ mTaskInfo = taskInfo;
+ mLayerView = layerView;
+ mBubble.setInflateSynchronously(inflateSync);
+ mBubble.setPreparingTransition(this);
+ mBubble.inflate(
+ this::onInflated,
+ context,
+ expandedViewManager,
+ factory,
+ positioner,
+ logger,
+ stackView,
+ layerView,
+ iconFactory,
+ false /* skipInflation */);
+ }
+
+ @VisibleForTesting
+ void onInflated(Bubble b) {
+ if (b != mBubble) {
+ throw new IllegalArgumentException("inflate callback doesn't match bubble");
+ }
+ final Rect launchBounds = new Rect();
+ mLayerView.getExpandedViewRestBounds(launchBounds);
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) {
+ if (mTaskInfo.getParentTaskId() != INVALID_TASK_ID) {
+ wct.reparent(mTaskInfo.token, null, true);
+ }
+ }
+
+ wct.setAlwaysOnTop(mTaskInfo.token, true);
+ wct.setWindowingMode(mTaskInfo.token, WINDOWING_MODE_MULTI_WINDOW);
+ wct.setBounds(mTaskInfo.token, launchBounds);
+
+ final TaskView tv = b.getTaskView();
+ tv.setSurfaceLifecycle(SurfaceView.SURFACE_LIFECYCLE_FOLLOWS_ATTACHMENT);
+ final TaskViewRepository.TaskViewState state = mRepository.byTaskView(
+ tv.getController());
+ if (state != null) {
+ state.mVisible = true;
+ }
+ mTaskViewTransitions.enqueueExternal(tv.getController(), () -> {
+ mTransition = mTransitions.startTransition(TRANSIT_CHANGE, wct, this);
+ return mTransition;
+ });
+ }
+
+ @Override
+ public void skip() {
+ mBubble.setPreparingTransition(null);
+ mFinishCb.onTransitionFinished(mFinishWct);
+ mFinishCb = null;
+ }
+
+ @Override
+ public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
+ @Nullable TransitionRequestInfo request) {
+ return null;
+ }
+
+ @Override
+ public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ }
+
+ @Override
+ public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
+ @NonNull SurfaceControl.Transaction finishTransaction) {
+ if (!aborted) return;
+ mTransition = null;
+ mTaskViewTransitions.onExternalDone(transition);
+ }
+
+ @Override
+ public boolean startAnimation(@NonNull IBinder transition,
+ @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ if (mTransition != transition) return false;
+ boolean found = false;
+ for (int i = 0; i < info.getChanges().size(); ++i) {
+ final TransitionInfo.Change chg = info.getChanges().get(i);
+ if (chg.getTaskInfo() == null) continue;
+ if (chg.getMode() != TRANSIT_CHANGE) continue;
+ if (!mTaskInfo.token.equals(chg.getTaskInfo().token)) continue;
+ mStartBounds.set(chg.getStartAbsBounds());
+ // Converting a task into taskview, so treat as "new"
+ mFinishWct = new WindowContainerTransaction();
+ mTaskInfo = chg.getTaskInfo();
+ mFinishT = finishTransaction;
+ mTaskLeash = chg.getLeash();
+ found = true;
+ mSnapshot = chg.getSnapshot();
+ break;
+ }
+ if (!found) {
+ Slog.w(TAG, "Expected a TaskView conversion in this transition but didn't get "
+ + "one, cleaning up the task view");
+ mBubble.getTaskView().getController().setTaskNotFound();
+ mTaskViewTransitions.onExternalDone(transition);
+ return false;
+ }
+ mFinishCb = finishCallback;
+
+ // Now update state (and talk to launcher) in parallel with snapshot stuff
+ mBubbleData.notificationEntryUpdated(mBubble, /* suppressFlyout= */ true,
+ /* showInShade= */ false);
+
+ startTransaction.show(mSnapshot);
+ // Move snapshot to root so that it remains visible while task is moved to taskview
+ startTransaction.reparent(mSnapshot, info.getRoot(0).getLeash());
+ startTransaction.setPosition(mSnapshot,
+ mStartBounds.left - info.getRoot(0).getOffset().x,
+ mStartBounds.top - info.getRoot(0).getOffset().y);
+ startTransaction.setLayer(mSnapshot, Integer.MAX_VALUE);
+ startTransaction.apply();
+
+ mTaskViewTransitions.onExternalDone(transition);
+ return true;
+ }
+
+ @Override
+ public void continueExpand() {
+ mFinishedExpand = true;
+ final boolean animate = mLayerView.canExpandView(mBubble);
+ if (animate) {
+ mPriorBubble = mLayerView.prepareConvertedView(mBubble);
+ }
+ if (mPriorBubble != null) {
+ // TODO: an animation. For now though, just remove it.
+ final BubbleBarExpandedView priorView = mPriorBubble.getBubbleBarExpandedView();
+ mLayerView.removeView(priorView);
+ mPriorBubble = null;
+ }
+ if (!animate || mBubble.getTaskView().getSurfaceControl() != null) {
+ playAnimation(animate);
+ }
+ }
+
+ @Override
+ public void surfaceCreated() {
+ mMainExecutor.execute(() -> {
+ final TaskViewTaskController tvc = mBubble.getTaskView().getController();
+ final TaskViewRepository.TaskViewState state = mRepository.byTaskView(tvc);
+ if (state == null) return;
+ state.mVisible = true;
+ if (mFinishedExpand) {
+ playAnimation(true /* animate */);
+ }
+ });
+ }
+
+ private void playAnimation(boolean animate) {
+ final TaskViewTaskController tv = mBubble.getTaskView().getController();
+ final SurfaceControl.Transaction startT = new SurfaceControl.Transaction();
+ mTaskViewTransitions.prepareOpenAnimation(tv, true /* new */, startT, mFinishT,
+ (ActivityManager.RunningTaskInfo) mTaskInfo, mTaskLeash, mFinishWct);
+
+ if (mFinishWct.isEmpty()) {
+ mFinishWct = null;
+ }
+
+ // Preparation is complete.
+ mBubble.setPreparingTransition(null);
+
+ if (animate) {
+ mLayerView.animateConvert(startT, mStartBounds, mSnapshot, mTaskLeash, () -> {
+ mFinishCb.onTransitionFinished(mFinishWct);
+ mFinishCb = null;
+ });
+ } else {
+ startT.apply();
+ mFinishCb.onTransitionFinished(mFinishWct);
+ mFinishCb = null;
+ }
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index 62895fe7c7cc..4297fac0f6a8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -36,6 +36,7 @@ import android.window.ScreenCapture.ScreenshotHardwareBuffer;
import android.window.ScreenCapture.SynchronousScreenCaptureListener;
import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.wm.shell.shared.annotations.ExternalThread;
@@ -330,6 +331,18 @@ public interface Bubbles {
* Does not result in a state change.
*/
void animateBubbleBarLocation(BubbleBarLocation location);
+
+ /**
+ * Called when an application icon is being dragged over the Bubble Bar drop zone.
+ * The location of the Bubble Bar is provided as an argument.
+ */
+ void onDragItemOverBubbleBarDragZone(@NonNull BubbleBarLocation location);
+
+ /**
+ * Called when an application icon is being dragged outside the Bubble Bar drop zone.
+ * Always called after {@link #onDragItemOverBubbleBarDragZone(BubbleBarLocation)}
+ */
+ void onItemDraggedOutsideBubbleBarDropZone();
}
/** Listener to find out about stack expansion / collapse events. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubblesListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubblesListener.aidl
index eb907dbb6597..9fc769f562a9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubblesListener.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubblesListener.aidl
@@ -33,4 +33,16 @@ oneway interface IBubblesListener {
* Does not result in a state change.
*/
void animateBubbleBarLocation(in BubbleBarLocation location);
+
+ /**
+ * Called when an application icon is being dragged over the Bubble Bar drop zone.
+ * The location of the Bubble Bar is provided as an argument.
+ */
+ void onDragItemOverBubbleBarDragZone(in BubbleBarLocation location);
+
+ /**
+ * Called when an application icon is being dragged outside the Bubble Bar drop zone.
+ * Always called after {@link #onDragItemOverBubbleBarDragZone(BubbleBarLocation)}
+ */
+ void onItemDraggedOutsideBubbleBarDropZone();
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
index de6d1f6c8852..52f20646fb4a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
@@ -36,17 +36,21 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
+import android.annotation.NonNull;
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.Log;
import android.util.Size;
+import android.view.SurfaceControl;
import android.widget.FrameLayout;
import androidx.annotation.Nullable;
import com.android.app.animation.Interpolators;
import com.android.wm.shell.R;
+import com.android.wm.shell.animation.SizeChangeAnimation;
+import com.android.wm.shell.bubbles.Bubble;
import com.android.wm.shell.bubbles.BubbleOverflow;
import com.android.wm.shell.bubbles.BubblePositioner;
import com.android.wm.shell.bubbles.BubbleViewProvider;
@@ -571,6 +575,49 @@ public class BubbleBarAnimationHelper {
}
/**
+ * Animates converting of a non-bubble task into an expanded bubble view.
+ */
+ public void animateConvert(BubbleViewProvider expandedBubble,
+ @NonNull SurfaceControl.Transaction startT,
+ @NonNull Rect origBounds,
+ @NonNull SurfaceControl snapshot,
+ @NonNull SurfaceControl taskLeash,
+ @Nullable Runnable afterAnimation) {
+ mExpandedBubble = expandedBubble;
+ final BubbleBarExpandedView bbev = getExpandedView();
+ if (bbev == null) {
+ return;
+ }
+
+ bbev.setTaskViewAlpha(1f);
+ SurfaceControl tvSf = ((Bubble) mExpandedBubble).getTaskView().getSurfaceControl();
+
+ final Size size = getExpandedViewSize();
+ Point position = getExpandedViewRestPosition(size);
+
+ final SizeChangeAnimation sca =
+ new SizeChangeAnimation(
+ new Rect(origBounds.left - position.x, origBounds.top - position.y,
+ origBounds.right - position.x, origBounds.bottom - position.y),
+ new Rect(0, 0, size.getWidth(), size.getHeight()));
+ sca.initialize(bbev, taskLeash, snapshot, startT);
+
+ Animator a = sca.buildViewAnimator(bbev, tvSf, snapshot, /* onFinish */ (va) -> {
+ updateExpandedView(bbev);
+ snapshot.release();
+ bbev.setSurfaceZOrderedOnTop(false);
+ bbev.setAnimating(false);
+ if (afterAnimation != null) {
+ afterAnimation.run();
+ }
+ });
+
+ bbev.setSurfaceZOrderedOnTop(true);
+ a.setDuration(EXPANDED_VIEW_ANIMATE_TO_REST_DURATION);
+ a.start();
+ }
+
+ /**
* Cancel current animations
*/
public void cancelAnimations() {
@@ -627,6 +674,13 @@ public class BubbleBarAnimationHelper {
bbev.maybeShowOverflow();
}
+ void getExpandedViewRestBounds(Rect out) {
+ final int width = mPositioner.getExpandedViewWidthForBubbleBar(false /* overflow */);
+ final int height = mPositioner.getExpandedViewHeightForBubbleBar(false /* overflow */);
+ Point position = getExpandedViewRestPosition(new Size(width, height));
+ out.set(position.x, position.y, position.x + width, position.y + height);
+ }
+
private Point getExpandedViewRestPosition(Size size) {
final int padding = mPositioner.getBubbleBarExpandedViewPadding();
Point point = new Point();
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 eaa0bd250fc4..91dcbdf5f117 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
@@ -28,6 +28,7 @@ import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.drawable.ColorDrawable;
import android.view.Gravity;
+import android.view.SurfaceControl;
import android.view.TouchDelegate;
import android.view.View;
import android.view.ViewTreeObserver;
@@ -174,14 +175,34 @@ public class BubbleBarLayerView extends FrameLayout
/** Shows the expanded view of the provided bubble. */
public void showExpandedView(BubbleViewProvider b) {
- BubbleBarExpandedView expandedView = b.getBubbleBarExpandedView();
- if (expandedView == null) {
- return;
- }
+ if (!canExpandView(b)) return;
+ animateExpand(prepareExpandedView(b));
+ }
+
+ /**
+ * @return whether it's possible to expand {@param b} right now. This is {@code false} if
+ * the bubble has no view or if the bubble is already showing.
+ */
+ public boolean canExpandView(BubbleViewProvider b) {
+ if (b.getBubbleBarExpandedView() == null) return false;
if (mExpandedBubble != null && mIsExpanded && b.getKey().equals(mExpandedBubble.getKey())) {
- // Already showing this bubble, skip animating
- return;
+ // Already showing this bubble so can't expand it.
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Prepares the expanded view of the provided bubble to be shown. This includes removing any
+ * stale content and cancelling any related animations.
+ *
+ * @return previous open bubble if there was one.
+ */
+ private BubbleViewProvider prepareExpandedView(BubbleViewProvider b) {
+ if (!canExpandView(b)) {
+ throw new IllegalStateException("Can't prepare expand. Check canExpandView(b) first.");
}
+ BubbleBarExpandedView expandedView = b.getBubbleBarExpandedView();
BubbleViewProvider previousBubble = null;
if (mExpandedBubble != null && !b.getKey().equals(mExpandedBubble.getKey())) {
if (mIsExpanded && mExpandedBubble.getBubbleBarExpandedView() != null) {
@@ -251,7 +272,20 @@ public class BubbleBarLayerView extends FrameLayout
mIsExpanded = true;
mBubbleController.getSysuiProxy().onStackExpandChanged(true);
+ showScrim(true);
+ return previousBubble;
+ }
+ /**
+ * Performs an animation to open a bubble with content that is not already visible.
+ *
+ * @param previousBubble If non-null, this is a bubble that is already showing before the new
+ * bubble is expanded.
+ */
+ public void animateExpand(BubbleViewProvider previousBubble) {
+ if (!mIsExpanded || mExpandedBubble == null) {
+ throw new IllegalStateException("Can't animateExpand without expnaded state");
+ }
final Runnable afterAnimation = () -> {
if (mExpandedView == null) return;
// Touch delegate for the menu
@@ -274,8 +308,49 @@ public class BubbleBarLayerView extends FrameLayout
} else {
mAnimationHelper.animateExpansion(mExpandedBubble, afterAnimation);
}
+ }
- showScrim(true);
+ /**
+ * Like {@link #prepareExpandedView} but also makes the current expanded bubble visible
+ * immediately so it gets a surface that can be animated. Since the surface may not be ready
+ * yet, this keeps the TaskView alpha=0.
+ */
+ public BubbleViewProvider prepareConvertedView(BubbleViewProvider b) {
+ final BubbleViewProvider prior = prepareExpandedView(b);
+
+ final BubbleBarExpandedView bbev = mExpandedBubble.getBubbleBarExpandedView();
+ if (bbev != null) {
+ updateExpandedView();
+ bbev.setAnimating(true);
+ bbev.setContentVisibility(true);
+ bbev.setSurfaceZOrderedOnTop(true);
+ bbev.setTaskViewAlpha(0.f);
+ bbev.setVisibility(VISIBLE);
+ }
+
+ return prior;
+ }
+
+ /**
+ * Starts and animates a conversion-from transition.
+ *
+ * @param startT A transaction with first-frame work. this *will* be applied here!
+ */
+ public void animateConvert(@NonNull SurfaceControl.Transaction startT,
+ @NonNull Rect startBounds, @NonNull SurfaceControl snapshot, SurfaceControl taskLeash,
+ Runnable animFinish) {
+ if (!mIsExpanded || mExpandedBubble == null) {
+ throw new IllegalStateException("Can't animateExpand without expanded state");
+ }
+ mAnimationHelper.animateConvert(mExpandedBubble, startT, startBounds, snapshot, taskLeash,
+ animFinish);
+ }
+
+ /**
+ * Populates {@param out} with the rest bounds of an expanded bubble.
+ */
+ public void getExpandedViewRestBounds(Rect out) {
+ mAnimationHelper.getExpandedViewRestBounds(out);
}
/** Removes the given {@code bubble}. */
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 2a988c15d49a..f275f4c5f829 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
@@ -3010,6 +3010,14 @@ class DesktopTasksController(
controller = null
}
+ override fun createDesk(displayId: Int) {
+ // TODO: b/362720497 - Implement this API.
+ }
+
+ override fun activateDesk(deskId: Int, remoteTransition: RemoteTransition?) {
+ // TODO: b/362720497 - Implement this API.
+ }
+
override fun showDesktopApps(displayId: Int, remoteTransition: RemoteTransition?) {
executeRemoteCallWithTaskPermission(controller, "showDesktopApps") { c ->
c.showDesktopApps(displayId, remoteTransition)
@@ -3037,17 +3045,6 @@ class DesktopTasksController(
)
}
- override fun getVisibleTaskCount(displayId: Int): Int {
- val result = IntArray(1)
- executeRemoteCallWithTaskPermission(
- controller,
- "visibleTaskCount",
- { controller -> result[0] = controller.visibleTaskCount(displayId) },
- /* blocking= */ true,
- )
- return result[0]
- }
-
override fun onDesktopSplitSelectAnimComplete(taskInfo: RunningTaskInfo) {
executeRemoteCallWithTaskPermission(controller, "onDesktopSplitSelectAnimComplete") { c
->
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
index a135e4462150..44f7e16e98c3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
@@ -29,6 +29,11 @@ import com.android.wm.shell.desktopmode.IMoveToDesktopCallback;
* Interface that is exposed to remote callers to manipulate desktop mode features.
*/
interface IDesktopMode {
+ /** If possible, creates a new desk on the display whose ID is `displayId`. */
+ oneway void createDesk(int displayId);
+
+ /** Activates the desk whose ID is `deskId` on whatever display it currently exists on. */
+ oneway void activateDesk(int deskId, in RemoteTransition remoteTransition);
/** Show apps on the desktop on the given display */
void showDesktopApps(int displayId, in RemoteTransition remoteTransition);
@@ -48,9 +53,6 @@ interface IDesktopMode {
oneway void showDesktopApp(int taskId, in @nullable RemoteTransition remoteTransition,
in DesktopTaskToFrontReason toFrontReason);
- /** Get count of visible desktop tasks on the given display */
- int getVisibleTaskCount(int displayId);
-
/** Perform cleanup transactions after the animation to split select is complete */
oneway void onDesktopSplitSelectAnimComplete(in RunningTaskInfo taskInfo);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 2d4d458292ea..4f2e028a1df0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -311,6 +311,9 @@ public class RecentTasksController implements TaskStackListenerCallback,
public void onTaskAdded(RunningTaskInfo taskInfo) {
notifyRunningTaskAppeared(taskInfo);
+ if (!enableShellTopTaskTracking()) {
+ notifyRecentTasksChanged();
+ }
}
public void onTaskRemoved(RunningTaskInfo taskInfo) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index afc6fee2eca3..55133780f517 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -222,7 +222,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
RecentsMixedHandler mixer = null;
Consumer<IBinder> setTransitionForMixer = null;
for (int i = 0; i < mMixers.size(); ++i) {
- setTransitionForMixer = mMixers.get(i).handleRecentsRequest(wct);
+ setTransitionForMixer = mMixers.get(i).handleRecentsRequest();
if (setTransitionForMixer != null) {
mixer = mMixers.get(i);
break;
@@ -1455,6 +1455,11 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
}
}
+ // Notify the mixers of the pending finish
+ for (int i = 0; i < mMixers.size(); ++i) {
+ mMixers.get(i).handleFinishRecents(returningToApp, wct, t);
+ }
+
if (Flags.enableRecentsBookendTransition()) {
if (!wct.isEmpty()) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
@@ -1653,15 +1658,22 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
*/
public interface RecentsMixedHandler extends Transitions.TransitionHandler {
/**
- * Called when a recents request comes in. The handler can add operations to outWCT. If
- * the handler wants to "accept" the transition, it should return a Consumer accepting the
- * IBinder for the transition. If not, it should return `null`.
+ * Called when a recents request comes in. If the handler wants to "accept" the transition,
+ * it should return a Consumer accepting the IBinder for the transition. If not, it should
+ * return `null`.
*
* If a mixed-handler accepts this recents, it will be the de-facto handler for this
* transition and is required to call the associated {@link #startAnimation},
* {@link #mergeAnimation}, and {@link #onTransitionConsumed} methods.
*/
@Nullable
- Consumer<IBinder> handleRecentsRequest(WindowContainerTransaction outWCT);
+ Consumer<IBinder> handleRecentsRequest();
+
+ /**
+ * Called when a recents transition has finished, with a WCT and SurfaceControl Transaction
+ * that can be used to add to any changes needed to restore the state.
+ */
+ void handleFinishRecents(boolean returnToApp, @NonNull WindowContainerTransaction finishWct,
+ @NonNull SurfaceControl.Transaction finishT);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 511e426cc681..722494c05e32 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -129,6 +129,7 @@ import com.android.internal.logging.InstanceId;
import com.android.internal.policy.FoldLockSettingsObserver;
import com.android.internal.protolog.ProtoLog;
import com.android.launcher3.icons.IconProvider;
+import com.android.wm.shell.Flags;
import com.android.wm.shell.R;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -3766,13 +3767,31 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mTaskOrganizer.applyTransaction(wct);
}
+ public void onRecentsInSplitAnimationFinishing(boolean returnToApp,
+ @NonNull WindowContainerTransaction finishWct,
+ @NonNull SurfaceControl.Transaction finishT) {
+ if (!Flags.enableRecentsBookendTransition()) {
+ // The non-bookend recents transition case will be handled by
+ // RecentsMixedTransition wrapping the finish callback and calling
+ // onRecentsInSplitAnimationFinish()
+ return;
+ }
+
+ onRecentsInSplitAnimationFinishInner(returnToApp, finishWct, finishT);
+ }
+
/** Call this when the recents animation during split-screen finishes. */
- public void onRecentsInSplitAnimationFinish(WindowContainerTransaction finishWct,
- SurfaceControl.Transaction finishT) {
- ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRecentsInSplitAnimationFinish");
- mPausingTasks.clear();
+ public void onRecentsInSplitAnimationFinish(@NonNull WindowContainerTransaction finishWct,
+ @NonNull SurfaceControl.Transaction finishT) {
+ if (Flags.enableRecentsBookendTransition()) {
+ // The bookend recents transition case will be handled by
+ // onRecentsInSplitAnimationFinishing above
+ return;
+ }
+
// Check if the recent transition is finished by returning to the current
// split, so we can restore the divider bar.
+ boolean returnToApp = false;
for (int i = 0; i < finishWct.getHierarchyOps().size(); ++i) {
final WindowContainerTransaction.HierarchyOp op =
finishWct.getHierarchyOps().get(i);
@@ -3787,13 +3806,26 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
if (op.getType() == HIERARCHY_OP_TYPE_REORDER && op.getToTop()
&& anyStageContainsContainer) {
- updateSurfaceBounds(mSplitLayout, finishT,
- false /* applyResizingOffset */);
- finishT.reparent(mSplitLayout.getDividerLeash(), mRootTaskLeash);
- setDividerVisibility(true, finishT);
- return;
+ returnToApp = true;
}
}
+ onRecentsInSplitAnimationFinishInner(returnToApp, finishWct, finishT);
+ }
+
+ /** Call this when the recents animation during split-screen finishes. */
+ public void onRecentsInSplitAnimationFinishInner(boolean returnToApp,
+ @NonNull WindowContainerTransaction finishWct,
+ @NonNull SurfaceControl.Transaction finishT) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRecentsInSplitAnimationFinish: returnToApp=%b",
+ returnToApp);
+ mPausingTasks.clear();
+ if (returnToApp) {
+ updateSurfaceBounds(mSplitLayout, finishT,
+ false /* applyResizingOffset */);
+ finishT.reparent(mSplitLayout.getDividerLeash(), mRootTaskLeash);
+ setDividerVisibility(true, finishT);
+ return;
+ }
setSplitsVisible(false);
finishWct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
index 0445add9cba9..13d87eda085b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
@@ -92,6 +92,10 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
getHolder().addCallback(this);
}
+ public TaskViewTaskController getController() {
+ return mTaskViewTaskController;
+ }
+
/**
* Launch a new activity.
*
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
index d19a7eac6ad2..aef75e2dc99e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
@@ -448,7 +448,7 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener {
* have the pending info, we'll do it when we receive it in
* {@link #onTaskAppeared(ActivityManager.RunningTaskInfo, SurfaceControl)}.
*/
- void setTaskNotFound() {
+ public void setTaskNotFound() {
mTaskNotFound = true;
if (mPendingInfo != null) {
cleanUpPendingTask();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
index 6c90a9060523..1eaae7ec83d9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
@@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -66,7 +67,7 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV
static final String TAG = "TaskViewTransitions";
/**
- * Map of {@link TaskViewTaskController} to {@link TaskViewRequestedState}.
+ * Map of {@link TaskViewTaskController} to {@link TaskViewRepository.TaskViewState}.
* <p>
* {@link TaskView} keeps a reference to the {@link TaskViewTaskController} instance and
* manages its lifecycle.
@@ -95,6 +96,7 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV
final @WindowManager.TransitionType int mType;
final WindowContainerTransaction mWct;
final @NonNull TaskViewTaskController mTaskView;
+ ExternalTransition mExternalTransition;
IBinder mClaimed;
/**
@@ -182,6 +184,32 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV
}
/**
+ * Starts or queues an "external" runnable into the pending queue. This means it will run
+ * in order relative to the local transitions.
+ *
+ * The external operation *must* call {@link #onExternalDone} once it has finished.
+ *
+ * In practice, the external is usually another transition on a different handler.
+ */
+ public void enqueueExternal(@NonNull TaskViewTaskController taskView, ExternalTransition ext) {
+ final PendingTransition pending = new PendingTransition(
+ TRANSIT_NONE, null /* wct */, taskView, null /* cookie */);
+ pending.mExternalTransition = ext;
+ mPending.add(pending);
+ startNextTransition();
+ }
+
+ /**
+ * An external transition run in this "queue" is required to call this once it becomes ready.
+ */
+ public void onExternalDone(IBinder key) {
+ final PendingTransition pending = findPending(key);
+ if (pending == null) return;
+ mPending.remove(pending);
+ startNextTransition();
+ }
+
+ /**
* Looks through the pending transitions for a opening transaction that matches the provided
* `taskView`.
*
@@ -191,6 +219,7 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV
PendingTransition findPendingOpeningTransition(TaskViewTaskController taskView) {
for (int i = mPending.size() - 1; i >= 0; --i) {
if (mPending.get(i).mTaskView != taskView) continue;
+ if (mPending.get(i).mExternalTransition != null) continue;
if (TransitionUtil.isOpeningType(mPending.get(i).mType)) {
return mPending.get(i);
}
@@ -207,6 +236,7 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV
PendingTransition findPending(TaskViewTaskController taskView, int type) {
for (int i = mPending.size() - 1; i >= 0; --i) {
if (mPending.get(i).mTaskView != taskView) continue;
+ if (mPending.get(i).mExternalTransition != null) continue;
if (mPending.get(i).mType == type) {
return mPending.get(i);
}
@@ -518,7 +548,11 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV
// Wait for this to start animating.
return;
}
- pending.mClaimed = mTransitions.startTransition(pending.mType, pending.mWct, this);
+ if (pending.mExternalTransition != null) {
+ pending.mClaimed = pending.mExternalTransition.start();
+ } else {
+ pending.mClaimed = mTransitions.startTransition(pending.mType, pending.mWct, this);
+ }
}
@Override
@@ -641,7 +675,7 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV
}
@VisibleForTesting
- void prepareOpenAnimation(TaskViewTaskController taskView,
+ public void prepareOpenAnimation(TaskViewTaskController taskView,
final boolean newTask,
SurfaceControl.Transaction startTransaction,
SurfaceControl.Transaction finishTransaction,
@@ -695,4 +729,10 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV
taskView.notifyAppeared(newTask);
}
+
+ /** Interface for running an external transition in this object's pending queue. */
+ public interface ExternalTransition {
+ /** Starts a transition and returns an identifying key for lookup. */
+ IBinder start();
+ }
}
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 2177986bccd5..d8e7c2ccb15f 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
@@ -367,7 +367,7 @@ public class DefaultMixedHandler implements MixedTransitionHandler,
}
@Override
- public Consumer<IBinder> handleRecentsRequest(WindowContainerTransaction outWCT) {
+ public Consumer<IBinder> handleRecentsRequest() {
if (mRecentsHandler != null) {
if (mSplitHandler.isSplitScreenVisible()) {
return this::setRecentsTransitionDuringSplit;
@@ -383,6 +383,21 @@ public class DefaultMixedHandler implements MixedTransitionHandler,
return null;
}
+ @Override
+ public void handleFinishRecents(boolean returnToApp,
+ @NonNull WindowContainerTransaction finishWct,
+ @NonNull SurfaceControl.Transaction finishT) {
+ if (mRecentsHandler != null) {
+ for (int i = mActiveTransitions.size() - 1; i >= 0; --i) {
+ final MixedTransition mixed = mActiveTransitions.get(i);
+ if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) {
+ ((RecentsMixedTransition) mixed).onAnimateRecentsDuringSplitFinishing(
+ returnToApp, finishWct, finishT);
+ }
+ }
+ }
+ }
+
private void setRecentsTransitionDuringSplit(IBinder transition) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while "
+ "Split-Screen is foreground, so treat it as Mixed.");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
index b0547a2a47b1..29a58d7f75dc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
@@ -407,8 +407,6 @@ class DefaultMixedTransition extends DefaultMixedHandler.MixedTransition {
mLeftoversHandler.mergeAnimation(
transition, info, t, mergeTarget, finishCallback);
}
- } else {
- mPipHandler.end();
}
return;
case TYPE_KEYGUARD:
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 36c3e9711f5c..ac6e4c5cd69e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -306,10 +306,10 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
}
// Early check if the transition doesn't warrant an animation.
- if (Transitions.isAllNoAnimation(info) || Transitions.isAllOrderOnly(info)
+ if (TransitionUtil.isAllNoAnimation(info) || TransitionUtil.isAllOrderOnly(info)
|| (info.getFlags() & WindowManager.TRANSIT_FLAG_INVISIBLE) != 0) {
startTransaction.apply();
- finishTransaction.apply();
+ // As a contract, finishTransaction should only be applied in Transitions#onFinish
finishCallback.onTransitionFinished(null /* wct */);
return true;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java
index 30ffdac5cbba..357861cc3ca7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java
@@ -161,13 +161,10 @@ public class MixedTransitionHelper {
pipHandler.startEnterAnimation(pipChange, startTransaction, finishTransaction,
finishCB);
}
- // make a new finishTransaction because pip's startEnterAnimation "consumes" it so
- // we need a separate one to send over to launcher.
- SurfaceControl.Transaction otherFinishT = new SurfaceControl.Transaction();
// Dispatch the rest of the transition normally. This will most-likely be taken by
// recents or default handler.
mixed.mLeftoversHandler = player.dispatchTransition(mixed.mTransition, everythingElse,
- otherStartT, otherFinishT, finishCB, mixedHandler);
+ otherStartT, finishTransaction, finishCB, mixedHandler);
} else {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Not leaving split, so just "
+ "forward animation to Pip-Handler.");
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 8cdbe26a2c76..1847af07f275 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
@@ -159,6 +159,8 @@ class RecentsMixedTransition extends DefaultMixedHandler.MixedTransition {
// If pair-to-pair switching, the post-recents clean-up isn't needed.
wct = wct != null ? wct : new WindowContainerTransaction();
if (mAnimType != ANIM_TYPE_PAIR_TO_PAIR) {
+ // TODO(b/346588978): Only called if !enableRecentsBookendTransition(), can remove
+ // once that rolls out
mSplitHandler.onRecentsInSplitAnimationFinish(wct, finishTransaction);
} else {
// notify pair-to-pair recents animation finish
@@ -177,6 +179,17 @@ class RecentsMixedTransition extends DefaultMixedHandler.MixedTransition {
return handled;
}
+ /**
+ * Called when the recents animation during split is about to finish.
+ */
+ void onAnimateRecentsDuringSplitFinishing(boolean returnToApp,
+ @NonNull WindowContainerTransaction finishWct,
+ @NonNull SurfaceControl.Transaction finishT) {
+ if (mAnimType != ANIM_TYPE_PAIR_TO_PAIR) {
+ mSplitHandler.onRecentsInSplitAnimationFinishing(returnToApp, finishWct, finishT);
+ }
+ }
+
@Override
void mergeAnimation(
@NonNull IBinder transition, @NonNull TransitionInfo info,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 8c9407b38d9e..b83b7e2f07a3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -672,46 +672,6 @@ public class Transitions implements RemoteCallable<Transitions>,
return -1;
}
- /**
- * Look through a transition and see if all non-closing changes are no-animation. If so, no
- * animation should play.
- */
- static boolean isAllNoAnimation(TransitionInfo info) {
- if (isClosingType(info.getType())) {
- // no-animation is only relevant for launching (open) activities.
- return false;
- }
- boolean hasNoAnimation = false;
- final int changeSize = info.getChanges().size();
- for (int i = changeSize - 1; i >= 0; --i) {
- final TransitionInfo.Change change = info.getChanges().get(i);
- if (isClosingType(change.getMode())) {
- // ignore closing apps since they are a side-effect of the transition and don't
- // animate.
- continue;
- }
- if (change.hasFlags(FLAG_NO_ANIMATION)) {
- hasNoAnimation = true;
- } else if (!TransitionUtil.isOrderOnly(change) && !change.hasFlags(FLAG_IS_OCCLUDED)) {
- // Ignore the order only or occluded changes since they shouldn't be visible during
- // animation. For anything else, we need to animate if at-least one relevant
- // participant *is* animated,
- return false;
- }
- }
- return hasNoAnimation;
- }
-
- /**
- * Check if all changes in this transition are only ordering changes. If so, we won't animate.
- */
- static boolean isAllOrderOnly(TransitionInfo info) {
- for (int i = info.getChanges().size() - 1; i >= 0; --i) {
- if (!TransitionUtil.isOrderOnly(info.getChanges().get(i))) return false;
- }
- return true;
- }
-
private Track getOrCreateTrack(int trackId) {
while (trackId >= mTracks.size()) {
mTracks.add(new Track());
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 fe9bdb027d18..dd7488e5f2f9 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
@@ -972,7 +972,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
final RelayoutParams.OccludingCaptionElement controlsElement =
new RelayoutParams.OccludingCaptionElement();
controlsElement.mWidthResId = R.dimen.desktop_mode_customizable_caption_margin_end;
- if (Flags.enableMinimizeButton()) {
+ if (DesktopModeFlags.ENABLE_MINIMIZE_BUTTON.isTrue()) {
controlsElement.mWidthResId =
R.dimen.desktop_mode_customizable_caption_with_minimize_button_margin_end;
}
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 79c5eff01b4c..9f8ca7740182 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
@@ -47,7 +47,6 @@ import com.android.internal.R.color.materialColorSurfaceContainerHigh
import com.android.internal.R.color.materialColorSurfaceContainerLow
import com.android.internal.R.color.materialColorSurfaceDim
import com.android.window.flags.Flags
-import com.android.window.flags.Flags.enableMinimizeButton
import com.android.wm.shell.R
import android.window.DesktopModeFlags
import com.android.wm.shell.windowdecor.MaximizeButtonView
@@ -226,7 +225,7 @@ class AppHeaderViewHolder(
minimizeWindowButton.background = getDrawable(1)
}
maximizeButtonView.setAnimationTints(isDarkMode())
- minimizeWindowButton.isGone = !enableMinimizeButton()
+ minimizeWindowButton.isGone = !DesktopModeFlags.ENABLE_MINIMIZE_BUTTON.isTrue()
}
private fun bindDataWithThemedHeaders(
@@ -276,7 +275,7 @@ class AppHeaderViewHolder(
drawableInsets = minimizeDrawableInsets
)
}
- minimizeWindowButton.isGone = !enableMinimizeButton()
+ minimizeWindowButton.isGone = !DesktopModeFlags.ENABLE_MINIMIZE_BUTTON.isTrue()
// Maximize button.
maximizeButtonView.apply {
setAnimationTints(
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromAllAppsLandscape.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromAllAppsLandscape.kt
new file mode 100644
index 000000000000..6b159a4152ac
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromAllAppsLandscape.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.wm.shell.flicker
+
+import android.tools.Rotation.ROTATION_90
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CASCADE_APP
+import com.android.wm.shell.scenarios.OpenAppFromAllApps
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class OpenAppFromAllAppsLandscape : OpenAppFromAllApps(rotation = ROTATION_90) {
+
+ @ExpectedScenarios(["CASCADE_APP"])
+ @Test
+ override fun openApp() = super.openApp()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(CASCADE_APP)
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromAllAppsPortrait.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromAllAppsPortrait.kt
new file mode 100644
index 000000000000..07b439284680
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromAllAppsPortrait.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.wm.shell.flicker
+
+import android.tools.Rotation.ROTATION_0
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CASCADE_APP
+import com.android.wm.shell.scenarios.OpenAppFromAllApps
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class OpenAppFromAllAppsPortrait : OpenAppFromAllApps(rotation = ROTATION_0) {
+
+ @ExpectedScenarios(["CASCADE_APP"])
+ @Test
+ override fun openApp() = super.openApp()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(CASCADE_APP)
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromTaskbarLandscape.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromTaskbarLandscape.kt
new file mode 100644
index 000000000000..caadd3be0b9c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromTaskbarLandscape.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.wm.shell.flicker
+
+import android.tools.Rotation.ROTATION_90
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CASCADE_APP
+import com.android.wm.shell.scenarios.OpenAppFromTaskbar
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class OpenAppFromTaskbarLandscape : OpenAppFromTaskbar(rotation = ROTATION_90) {
+
+ @ExpectedScenarios(["CASCADE_APP"])
+ @Test
+ override fun openApp() = super.openApp()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(CASCADE_APP)
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromTaskbarPortrait.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromTaskbarPortrait.kt
new file mode 100644
index 000000000000..77f5ab290e20
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromTaskbarPortrait.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.wm.shell.flicker
+
+import android.tools.Rotation.ROTATION_0
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CASCADE_APP
+import com.android.wm.shell.scenarios.OpenAppFromTaskbar
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class OpenAppFromTaskbarPortrait : OpenAppFromTaskbar(rotation = ROTATION_0) {
+
+ @ExpectedScenarios(["CASCADE_APP"])
+ @Test
+ override fun openApp() = super.openApp()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(CASCADE_APP)
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppFromAllApps.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppFromAllApps.kt
index 36cdd5b26992..348219631245 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppFromAllApps.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppFromAllApps.kt
@@ -20,12 +20,12 @@ import android.app.Instrumentation
import android.tools.NavBar
import android.tools.flicker.rules.ChangeDisplayOrientationRule
import android.tools.Rotation
+import android.tools.device.apphelpers.CalculatorAppHelper
import android.tools.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
-import com.android.server.wm.flicker.helpers.MailAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.window.flags.Flags
import com.android.wm.shell.Utils
@@ -44,7 +44,7 @@ abstract class OpenAppFromAllApps(val rotation: Rotation = Rotation.ROTATION_0)
private val wmHelper = WindowManagerStateHelper(instrumentation)
private val device = UiDevice.getInstance(instrumentation)
private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
- private val mailApp = MailAppHelper(instrumentation)
+ private val calculatorApp = CalculatorAppHelper(instrumentation)
@Rule
@JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
@@ -64,13 +64,13 @@ abstract class OpenAppFromAllApps(val rotation: Rotation = Rotation.ROTATION_0)
open fun openApp() {
tapl.launchedAppState.taskbar
.openAllApps()
- .getAppIcon(mailApp.appName)
- .launch(mailApp.packageName)
+ .getAppIcon(calculatorApp.appName)
+ .launch(calculatorApp.packageName)
}
@After
fun teardown() {
- mailApp.exit(wmHelper)
+ calculatorApp.exit(wmHelper)
testApp.exit(wmHelper)
}
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/CopyContentInSplit.kt
index 31d89f92f744..9d501d32fbc7 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/CopyContentInSplit.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/CopyContentInSplit.kt
@@ -23,8 +23,8 @@ import android.tools.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.RecentTasksUtils
import com.android.wm.shell.Utils
-import com.android.wm.shell.flicker.utils.RecentTasksUtils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByDivider.kt
index 1af6cac39085..f574f02ac3b3 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByDivider.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByDivider.kt
@@ -23,8 +23,8 @@ import android.tools.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.RecentTasksUtils
import com.android.wm.shell.Utils
-import com.android.wm.shell.flicker.utils.RecentTasksUtils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByGoHome.kt
index 8ad8c7bd7a7f..60fcce2fbf18 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByGoHome.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByGoHome.kt
@@ -23,8 +23,8 @@ import android.tools.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.RecentTasksUtils
import com.android.wm.shell.Utils
-import com.android.wm.shell.flicker.utils.RecentTasksUtils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DragDividerToResize.kt
index da0ace472153..e6a080b2d258 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DragDividerToResize.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DragDividerToResize.kt
@@ -23,8 +23,8 @@ import android.tools.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.RecentTasksUtils
import com.android.wm.shell.Utils
-import com.android.wm.shell.flicker.utils.RecentTasksUtils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenAutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenAutoEnterPipOnGoToHomeTest.kt
index f9c60ad14fae..aa893ed65e7c 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenAutoEnterPipOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenAutoEnterPipOnGoToHomeTest.kt
@@ -19,7 +19,6 @@ package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.platform.test.annotations.RequiresDevice
-import android.platform.test.annotations.RequiresFlagsDisabled
import android.tools.Rotation
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
@@ -29,7 +28,6 @@ import android.tools.helpers.WindowUtils
import android.tools.traces.parsers.toFlickerComponent
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.wm.shell.Flags
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.Assume
import org.junit.FixMethodOrder
@@ -64,7 +62,6 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
open class FromSplitScreenAutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) :
AutoEnterPipOnGoToHomeTest(flicker) {
private val portraitDisplayBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0)
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt
index 805f4c2fe7f8..8e7cb56c0f07 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt
@@ -19,7 +19,6 @@ package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.platform.test.annotations.RequiresDevice
-import android.platform.test.annotations.RequiresFlagsDisabled
import android.tools.Rotation
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
@@ -30,7 +29,6 @@ import android.tools.traces.parsers.toFlickerComponent
import com.android.server.wm.flicker.helpers.PipAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.wm.shell.Flags
import com.android.wm.shell.flicker.pip.common.EnterPipTransition
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.Assume
@@ -66,7 +64,6 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
@FlakyTest(bugId = 386333280)
open class FromSplitScreenEnterPipOnUserLeaveHintTest(flicker: LegacyFlickerTest) :
EnterPipTransition(flicker) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java
new file mode 100644
index 000000000000..b4f514acf2dd
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java
@@ -0,0 +1,182 @@
+/*
+ * 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.wm.shell.bubbles;
+
+import static android.view.WindowManager.TRANSIT_CHANGE;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.os.IBinder;
+import android.view.SurfaceControl;
+import android.view.ViewRootImpl;
+import android.window.IWindowContainerToken;
+import android.window.TransitionInfo;
+import android.window.WindowContainerToken;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.launcher3.icons.BubbleIconFactory;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.TestSyncExecutor;
+import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView;
+import com.android.wm.shell.bubbles.bar.BubbleBarLayerView;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.taskview.TaskView;
+import com.android.wm.shell.taskview.TaskViewRepository;
+import com.android.wm.shell.taskview.TaskViewTaskController;
+import com.android.wm.shell.taskview.TaskViewTransitions;
+import com.android.wm.shell.transition.Transitions;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests of {@link BubbleTransitions}.
+ */
+@SmallTest
+public class BubbleTransitionsTest extends ShellTestCase {
+ @Mock
+ private BubbleData mBubbleData;
+ @Mock
+ private Bubble mBubble;
+ @Mock
+ private Transitions mTransitions;
+ @Mock
+ private SyncTransactionQueue mSyncQueue;
+ @Mock
+ private BubbleExpandedViewManager mExpandedViewManager;
+ @Mock
+ private BubblePositioner mBubblePositioner;
+ @Mock
+ private BubbleLogger mBubbleLogger;
+ @Mock
+ private BubbleStackView mStackView;
+ @Mock
+ private BubbleBarLayerView mLayerView;
+ @Mock
+ private BubbleIconFactory mIconFactory;
+
+ @Mock private ShellTaskOrganizer mTaskOrganizer;
+ private TaskViewTransitions mTaskViewTransitions;
+ private TaskViewRepository mRepository;
+ private BubbleTransitions mBubbleTransitions;
+ private BubbleTaskViewFactory mTaskViewFactory;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mRepository = new TaskViewRepository();
+ ShellExecutor syncExecutor = new TestSyncExecutor();
+
+ when(mTransitions.getMainExecutor()).thenReturn(syncExecutor);
+ when(mTransitions.isRegistered()).thenReturn(true);
+ mTaskViewTransitions = new TaskViewTransitions(mTransitions, mRepository, mTaskOrganizer,
+ mSyncQueue);
+ mBubbleTransitions = new BubbleTransitions(mTransitions, mTaskOrganizer, mRepository,
+ mBubbleData, mTaskViewTransitions, mContext);
+ mTaskViewFactory = () -> {
+ TaskViewTaskController taskViewTaskController = new TaskViewTaskController(
+ mContext, mTaskOrganizer, mTaskViewTransitions, mSyncQueue);
+ TaskView taskView = new TaskView(mContext, mTaskViewTransitions,
+ taskViewTaskController);
+ return new BubbleTaskView(taskView, syncExecutor);
+ };
+ final BubbleBarExpandedView bbev = mock(BubbleBarExpandedView.class);
+ final ViewRootImpl vri = mock(ViewRootImpl.class);
+ when(bbev.getViewRootImpl()).thenReturn(vri);
+ when(mBubble.getBubbleBarExpandedView()).thenReturn(bbev);
+ }
+
+ private ActivityManager.RunningTaskInfo setupBubble() {
+ ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
+ final IWindowContainerToken itoken = mock(IWindowContainerToken.class);
+ final IBinder asBinder = mock(IBinder.class);
+ when(itoken.asBinder()).thenReturn(asBinder);
+ WindowContainerToken token = new WindowContainerToken(itoken);
+ taskInfo.token = token;
+ final TaskView tv = mock(TaskView.class);
+ final TaskViewTaskController tvtc = mock(TaskViewTaskController.class);
+ when(tvtc.getTaskInfo()).thenReturn(taskInfo);
+ when(tv.getController()).thenReturn(tvtc);
+ when(mBubble.getTaskView()).thenReturn(tv);
+ mRepository.add(tvtc);
+ return taskInfo;
+ }
+
+ @Test
+ public void testConvertToBubble() {
+ // Basic walk-through of convert-to-bubble transition stages
+ ActivityManager.RunningTaskInfo taskInfo = setupBubble();
+ final BubbleTransitions.BubbleTransition bt = mBubbleTransitions.startConvertToBubble(
+ mBubble, taskInfo, mExpandedViewManager, mTaskViewFactory, mBubblePositioner,
+ mBubbleLogger, mStackView, mLayerView, mIconFactory, false);
+ final BubbleTransitions.ConvertToBubble ctb = (BubbleTransitions.ConvertToBubble) bt;
+ ctb.onInflated(mBubble);
+ when(mLayerView.canExpandView(any())).thenReturn(true);
+ verify(mTransitions).startTransition(anyInt(), any(), eq(ctb));
+ verify(mBubble).setPreparingTransition(eq(bt));
+ // Ensure we are communicating with the taskviewtransitions queue
+ assertTrue(mTaskViewTransitions.hasPending());
+
+ final TransitionInfo info = new TransitionInfo(TRANSIT_CHANGE, 0);
+ final TransitionInfo.Change chg = new TransitionInfo.Change(taskInfo.token,
+ mock(SurfaceControl.class));
+ chg.setTaskInfo(taskInfo);
+ chg.setMode(TRANSIT_CHANGE);
+ info.addChange(chg);
+ info.addRoot(new TransitionInfo.Root(0, mock(SurfaceControl.class), 0, 0));
+ SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
+ SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
+ final boolean[] finishCalled = new boolean[]{false};
+ Transitions.TransitionFinishCallback finishCb = wct -> {
+ assertFalse(finishCalled[0]);
+ finishCalled[0] = true;
+ };
+ ctb.startAnimation(ctb.mTransition, info, startT, finishT, finishCb);
+ assertFalse(mTaskViewTransitions.hasPending());
+
+ verify(mBubbleData).notificationEntryUpdated(eq(mBubble), anyBoolean(), anyBoolean());
+ ctb.continueExpand();
+
+ clearInvocations(mBubble);
+ verify(mBubble, never()).setPreparingTransition(any());
+
+ ctb.surfaceCreated();
+ verify(mBubble).setPreparingTransition(isNull());
+ ArgumentCaptor<Runnable> animCb = ArgumentCaptor.forClass(Runnable.class);
+ verify(mLayerView).animateConvert(any(), any(), any(), any(), animCb.capture());
+ assertFalse(finishCalled[0]);
+ animCb.getValue().run();
+ assertTrue(finishCalled[0]);
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index 065fa219e8d0..542289db6cfc 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -211,6 +211,7 @@ public class RecentTasksControllerTest extends ShellTestCase {
@Test
public void testAddRemoveSplitNotifyChange() {
+ reset(mRecentTasksController);
RecentTaskInfo t1 = makeTaskInfo(1);
RecentTaskInfo t2 = makeTaskInfo(2);
setRawList(t1, t2);
@@ -225,6 +226,7 @@ public class RecentTasksControllerTest extends ShellTestCase {
@Test
public void testAddSameSplitBoundsInfoSkipNotifyChange() {
+ reset(mRecentTasksController);
RecentTaskInfo t1 = makeTaskInfo(1);
RecentTaskInfo t2 = makeTaskInfo(2);
setRawList(t1, t2);
@@ -535,6 +537,7 @@ public class RecentTasksControllerTest extends ShellTestCase {
@Test
public void testTaskWindowingModeChangedNotifiesChange() {
+ reset(mRecentTasksController);
RecentTaskInfo t1 = makeTaskInfo(1);
setRawList(t1);
@@ -551,7 +554,8 @@ public class RecentTasksControllerTest extends ShellTestCase {
WINDOWING_MODE_MULTI_WINDOW);
mShellTaskOrganizer.onTaskInfoChanged(rt2MultiWIndow);
- verify(mRecentTasksController).notifyRecentTasksChanged();
+ // One for onTaskAppeared and one for onTaskInfoChanged
+ verify(mRecentTasksController, times(2)).notifyRecentTasksChanged();
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index 4211e4682810..b9d6a454694d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -67,6 +67,7 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.launcher3.icons.IconProvider;
+import com.android.wm.shell.Flags;
import com.android.wm.shell.MockToken;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -355,8 +356,13 @@ public class SplitTransitionTests extends ShellTestCase {
// Make sure it cleans-up if recents doesn't restore
WindowContainerTransaction commitWCT = new WindowContainerTransaction();
- mStageCoordinator.onRecentsInSplitAnimationFinish(commitWCT,
- mock(SurfaceControl.Transaction.class));
+ if (Flags.enableRecentsBookendTransition()) {
+ mStageCoordinator.onRecentsInSplitAnimationFinishing(false /* returnToApp */, commitWCT,
+ mock(SurfaceControl.Transaction.class));
+ } else {
+ mStageCoordinator.onRecentsInSplitAnimationFinish(commitWCT,
+ mock(SurfaceControl.Transaction.class));
+ }
assertFalse(mStageCoordinator.isSplitScreenVisible());
}
@@ -420,8 +426,13 @@ public class SplitTransitionTests extends ShellTestCase {
// simulate the restoreWCT being applied:
mMainStage.onTaskAppeared(mMainChild, mock(SurfaceControl.class));
mSideStage.onTaskAppeared(mSideChild, mock(SurfaceControl.class));
- mStageCoordinator.onRecentsInSplitAnimationFinish(restoreWCT,
- mock(SurfaceControl.Transaction.class));
+ if (Flags.enableRecentsBookendTransition()) {
+ mStageCoordinator.onRecentsInSplitAnimationFinishing(true /* returnToApp */, restoreWCT,
+ mock(SurfaceControl.Transaction.class));
+ } else {
+ mStageCoordinator.onRecentsInSplitAnimationFinish(restoreWCT,
+ mock(SurfaceControl.Transaction.class));
+ }
assertTrue(mStageCoordinator.isSplitScreenVisible());
}
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 677fd86aca9c..53d3b77f1ba2 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -206,6 +206,9 @@ java_sdk_library {
visibility: [
"//frameworks/base", // Framework
],
+ impl_library_visibility: [
+ "//frameworks/base/ravenwood",
+ ],
srcs: [
":framework-graphics-srcs",
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 52eae43f7db9..aa3dbda374be 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -21,6 +21,7 @@ import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO;
import static android.content.Context.DEVICE_ID_DEFAULT;
import static android.media.audio.Flags.autoPublicVolumeApiHardening;
import static android.media.audio.Flags.automaticBtDeviceType;
+import static android.media.audio.Flags.cacheGetStreamMinMaxVolume;
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;
@@ -58,7 +59,6 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.media.AudioAttributes.AttributeSystemUsage;
-import android.media.AudioDeviceInfo;
import android.media.CallbackUtil.ListenerInfo;
import android.media.audiopolicy.AudioPolicy;
import android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener;
@@ -75,6 +75,7 @@ import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
+import android.os.IpcDataCache;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
@@ -1231,6 +1232,84 @@ public class AudioManager {
}
/**
+ * API string for caching the min volume for each stream
+ * @hide
+ **/
+ public static final String VOLUME_MIN_CACHING_API = "getStreamMinVolume";
+ /**
+ * API string for caching the max volume for each stream
+ * @hide
+ **/
+ public static final String VOLUME_MAX_CACHING_API = "getStreamMaxVolume";
+ private static final int VOLUME_MIN_MAX_CACHING_SIZE = 16;
+
+ private final IpcDataCache.QueryHandler<VolumeCacheQuery, Integer> mVolQuery =
+ new IpcDataCache.QueryHandler<>() {
+ @Override
+ public Integer apply(VolumeCacheQuery query) {
+ final IAudioService service = getService();
+ try {
+ return switch (query.queryCommand) {
+ case QUERY_VOL_MIN -> service.getStreamMinVolume(query.stream);
+ case QUERY_VOL_MAX -> service.getStreamMaxVolume(query.stream);
+ default -> {
+ Log.w(TAG, "Not a valid volume cache query: " + query);
+ yield null;
+ }
+ };
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ };
+
+ private final IpcDataCache<VolumeCacheQuery, Integer> mVolMinCache =
+ new IpcDataCache<>(VOLUME_MIN_MAX_CACHING_SIZE, IpcDataCache.MODULE_SYSTEM,
+ VOLUME_MIN_CACHING_API, VOLUME_MIN_CACHING_API, mVolQuery);
+
+ private final IpcDataCache<VolumeCacheQuery, Integer> mVolMaxCache =
+ new IpcDataCache<>(VOLUME_MIN_MAX_CACHING_SIZE, IpcDataCache.MODULE_SYSTEM,
+ VOLUME_MAX_CACHING_API, VOLUME_MAX_CACHING_API, mVolQuery);
+
+ /**
+ * Used to invalidate the cache for the given API
+ * @hide
+ **/
+ public static void clearVolumeCache(String api) {
+ if (cacheGetStreamMinMaxVolume()) {
+ IpcDataCache.invalidateCache(IpcDataCache.MODULE_SYSTEM, api);
+ }
+ }
+
+ private static final int QUERY_VOL_MIN = 1;
+ private static final int QUERY_VOL_MAX = 2;
+ /** @hide */
+ @IntDef(prefix = "QUERY_VOL", value = {
+ QUERY_VOL_MIN,
+ QUERY_VOL_MAX}
+ )
+ @Retention(RetentionPolicy.SOURCE)
+ private @interface QueryVolCommand {}
+
+ private record VolumeCacheQuery(int stream, @QueryVolCommand int queryCommand) {
+ private String queryVolCommandToString() {
+ return switch (queryCommand) {
+ case QUERY_VOL_MIN -> "getStreamMinVolume";
+ case QUERY_VOL_MAX -> "getStreamMaxVolume";
+ default -> "invalid command";
+ };
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return TextUtils.formatSimple("VolumeCacheQuery(stream=%d, queryCommand=%s)", stream,
+ queryVolCommandToString());
+ }
+ }
+
+ /**
* Returns the maximum volume index for a particular stream.
*
* @param streamType The stream type whose maximum volume index is returned.
@@ -1238,6 +1317,9 @@ public class AudioManager {
* @see #getStreamVolume(int)
*/
public int getStreamMaxVolume(int streamType) {
+ if (cacheGetStreamMinMaxVolume()) {
+ return mVolMaxCache.query(new VolumeCacheQuery(streamType, QUERY_VOL_MAX));
+ }
final IAudioService service = getService();
try {
return service.getStreamMaxVolume(streamType);
@@ -1271,6 +1353,9 @@ public class AudioManager {
*/
@TestApi
public int getStreamMinVolumeInt(int streamType) {
+ if (cacheGetStreamMinMaxVolume()) {
+ return mVolMinCache.query(new VolumeCacheQuery(streamType, QUERY_VOL_MIN));
+ }
final IAudioService service = getService();
try {
return service.getStreamMinVolume(streamType);
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index c9625c405faa..c4886836f451 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -20,6 +20,8 @@ import static android.media.codec.Flags.FLAG_CODEC_AVAILABILITY;
import static android.media.codec.Flags.FLAG_NULL_OUTPUT_SURFACE;
import static android.media.codec.Flags.FLAG_REGION_OF_INTEREST;
import static android.media.codec.Flags.FLAG_SUBSESSION_METRICS;
+import static android.media.tv.flags.Flags.applyPictureProfiles;
+import static android.media.tv.flags.Flags.mediaQualityFw;
import static com.android.media.codec.flags.Flags.FLAG_LARGE_AUDIO_FRAME;
@@ -37,6 +39,8 @@ import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.hardware.HardwareBuffer;
import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.quality.PictureProfile;
+import android.media.quality.PictureProfileHandle;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -5370,6 +5374,9 @@ final public class MediaCodec {
* @param params The bundle of parameters to set.
* @throws IllegalStateException if in the Released state.
*/
+
+ private static final String PARAMETER_KEY_PICTURE_PROFILE_HANDLE = "picture-profile-handle";
+
public final void setParameters(@Nullable Bundle params) {
if (params == null) {
return;
@@ -5383,19 +5390,41 @@ final public class MediaCodec {
if (key.equals(MediaFormat.KEY_AUDIO_SESSION_ID)) {
int sessionId = 0;
try {
- sessionId = (Integer)params.get(key);
+ sessionId = (Integer) params.get(key);
} catch (Exception e) {
throw new IllegalArgumentException("Wrong Session ID Parameter!");
}
keys[i] = "audio-hw-sync";
values[i] = AudioSystem.getAudioHwSyncForSession(sessionId);
+ } else if (applyPictureProfiles() && mediaQualityFw()
+ && key.equals(MediaFormat.KEY_PICTURE_PROFILE_INSTANCE)) {
+ PictureProfile pictureProfile = null;
+ try {
+ pictureProfile = (PictureProfile) params.get(key);
+ } catch (ClassCastException e) {
+ throw new IllegalArgumentException(
+ "Cannot cast the instance parameter to PictureProfile!");
+ } catch (Exception e) {
+ android.util.Log.getStackTraceString(e);
+ throw new IllegalArgumentException("Unexpected exception when casting the "
+ + "instance parameter to PictureProfile!");
+ }
+ if (pictureProfile == null) {
+ throw new IllegalArgumentException(
+ "Picture profile instance parameter is null!");
+ }
+ PictureProfileHandle handle = pictureProfile.getHandle();
+ if (handle != PictureProfileHandle.NONE) {
+ keys[i] = PARAMETER_KEY_PICTURE_PROFILE_HANDLE;
+ values[i] = Long.valueOf(handle.getId());
+ }
} else {
keys[i] = key;
Object value = params.get(key);
// Bundle's byte array is a byte[], JNI layer only takes ByteBuffer
if (value instanceof byte[]) {
- values[i] = ByteBuffer.wrap((byte[])value);
+ values[i] = ByteBuffer.wrap((byte[]) value);
} else {
values[i] = value;
}
diff --git a/media/java/android/media/quality/MediaQualityContract.java b/media/java/android/media/quality/MediaQualityContract.java
index d1f63404dbff..8ae72de3214a 100644
--- a/media/java/android/media/quality/MediaQualityContract.java
+++ b/media/java/android/media/quality/MediaQualityContract.java
@@ -385,6 +385,339 @@ public class MediaQualityContract {
public static final String PARAMETER_AUTO_SUPER_RESOLUTION_ENABLED =
"auto_super_resolution_enabled";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_LEVEL_RANGE = "level_range";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_GAMUT_MAPPING = "gamut_mapping";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_PC_MODE = "pc_mode";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_LOW_LATENCY = "low_latency";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_VRR = "vrr";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_CVRR = "cvrr";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_HDMI_RGB_RANGE = "hdmi_rgb_range";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_COLOR_SPACE = "color_space";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_PANEL_INIT_MAX_LUMINCE_NITS =
+ "panel_init_max_lumince_nits";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_PANEL_INIT_MAX_LUMINCE_VALID =
+ "panel_init_max_lumince_valid";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_GAMMA = "gamma";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_COLOR_TEMPERATURE_RED_OFFSET =
+ "color_temperature_red_offset";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_COLOR_TEMPERATURE_GREEN_OFFSET =
+ "color_temperature_green_offset";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_COLOR_TEMPERATURE_BLUE_OFFSET =
+ "color_temperature_blue_offset";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_ELEVEN_POINT_RED = "eleven_point_red";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_ELEVEN_POINT_GREEN = "eleven_point_green";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_ELEVEN_POINT_BLUE = "eleven_point_blue";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_LOW_BLUE_LIGHT = "low_blue_light";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_LD_MODE = "ld_mode";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_OSD_RED_GAIN = "osd_red_gain";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_OSD_GREEN_GAIN = "osd_green_gain";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_OSD_BLUE_GAIN = "osd_blue_gain";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_OSD_RED_OFFSET = "osd_red_offset";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_OSD_GREEN_OFFSET = "osd_green_offset";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_OSD_BLUE_OFFSET = "osd_blue_offset";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_OSD_HUE = "osd_hue";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_OSD_SATURATION = "osd_saturation";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_OSD_CONTRAST = "osd_contrast";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_COLOR_TUNER_SWITCH = "color_tuner_switch";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_COLOR_TUNER_HUE_RED = "color_tuner_hue_red";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_COLOR_TUNER_HUE_GREEN = "color_tuner_hue_green";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_COLOR_TUNER_HUE_BLUE = "color_tuner_hue_blue";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_COLOR_TUNER_HUE_CYAN = "color_tuner_hue_cyan";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_COLOR_TUNER_HUE_MAGENTA = "color_tuner_hue_magenta";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_COLOR_TUNER_HUE_YELLOW = "color_tuner_hue_yellow";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_COLOR_TUNER_HUE_FLESH = "color_tuner_hue_flesh";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_COLOR_TUNER_SATURATION_RED =
+ "color_tuner_saturation_red";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_COLOR_TUNER_SATURATION_GREEN =
+ "color_tuner_saturation_green";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_COLOR_TUNER_SATURATION_BLUE =
+ "color_tuner_saturation_blue";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_COLOR_TUNER_SATURATION_CYAN =
+ "color_tuner_saturation_cyan";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_COLOR_TUNER_SATURATION_MAGENTA =
+ "color_tuner_saturation_magenta";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_COLOR_TUNER_SATURATION_YELLOW =
+ "color_tuner_saturation_yellow";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_COLOR_TUNER_SATURATION_FLESH =
+ "color_tuner_saturation_flesh";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_COLOR_TUNER_LUMINANCE_RED =
+ "color_tuner_luminance_red";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_COLOR_TUNER_LUMINANCE_GREEN =
+ "color_tuner_luminance_green";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_COLOR_TUNER_LUMINANCE_BLUE =
+ "color_tuner_luminance_blue";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_COLOR_TUNER_LUMINANCE_CYAN =
+ "color_tuner_luminance_cyan";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_COLOR_TUNER_LUMINANCE_MAGENTA =
+ "color_tuner_luminance_magenta";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_COLOR_TUNER_LUMINANCE_YELLOW =
+ "color_tuner_luminance_yellow";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_COLOR_TUNER_LUMINANCE_FLESH =
+ "color_tuner_luminance_flesh";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_ACTIVE_PROFILE = "active_profile";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_PICTURE_QUALITY_EVENT_TYPE =
+ "picture_quality_event_type";
+
private PictureQuality() {
}
}
@@ -641,6 +974,17 @@ public class MediaQualityContract {
*/
public static final String PARAMETER_DIGITAL_OUTPUT_MODE = "digital_output_mode";
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_ACTIVE_PROFILE = "active_profile";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_SOUND_STYLE = "sound_style";
+
+
private SoundQuality() {
}
diff --git a/media/java/android/mtp/OWNERS b/media/java/android/mtp/OWNERS
index 77ed08b1f9a5..c57265a91eff 100644
--- a/media/java/android/mtp/OWNERS
+++ b/media/java/android/mtp/OWNERS
@@ -1,9 +1,9 @@
set noparent
-anothermark@google.com
+vmartensson@google.com
+nkapron@google.com
febinthattil@google.com
-aprasath@google.com
+shubhankarm@google.com
jsharkey@android.com
jameswei@google.com
rmojumder@google.com
-kumarashishg@google.com
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index a77bc9fe0570..a1ce495fe33d 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -910,7 +910,7 @@ MtpResponseCode MtpDatabase::getObjectInfo(MtpObjectHandle handle,
case MTP_FORMAT_TIFF:
case MTP_FORMAT_TIFF_EP:
case MTP_FORMAT_DEFINED: {
- String8 temp(path);
+ String8 temp {static_cast<std::string_view>(path)};
std::unique_ptr<FileStream> stream(new FileStream(temp));
piex::PreviewImageData image_data;
if (!GetExifFromRawImage(stream.get(), temp, image_data)) {
@@ -967,7 +967,7 @@ void* MtpDatabase::getThumbnail(MtpObjectHandle handle, size_t& outThumbSize) {
case MTP_FORMAT_TIFF:
case MTP_FORMAT_TIFF_EP:
case MTP_FORMAT_DEFINED: {
- String8 temp(path);
+ String8 temp {static_cast<std::string_view>(path)};
std::unique_ptr<FileStream> stream(new FileStream(temp));
piex::PreviewImageData image_data;
if (!GetExifFromRawImage(stream.get(), temp, image_data)) {
diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java
index e9a0d3eceba3..017a1029d35c 100644
--- a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java
+++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java
@@ -16,6 +16,15 @@
package com.android.audiopolicytest;
+import static android.media.AudioManager.STREAM_ACCESSIBILITY;
+import static android.media.AudioManager.STREAM_ALARM;
+import static android.media.AudioManager.STREAM_DTMF;
+import static android.media.AudioManager.STREAM_MUSIC;
+import static android.media.AudioManager.STREAM_NOTIFICATION;
+import static android.media.AudioManager.STREAM_RING;
+import static android.media.AudioManager.STREAM_SYSTEM;
+import static android.media.AudioManager.STREAM_VOICE_CALL;
+
import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
import static com.android.audiopolicytest.AudioVolumeTestUtil.DEFAULT_ATTRIBUTES;
@@ -28,11 +37,15 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
+import android.content.Context;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.AudioSystem;
+import android.media.IAudioService;
import android.media.audiopolicy.AudioProductStrategy;
import android.media.audiopolicy.AudioVolumeGroup;
+import android.os.IBinder;
+import android.os.ServiceManager;
import android.platform.test.annotations.Presubmit;
import android.util.Log;
@@ -207,6 +220,32 @@ public class AudioManagerTest {
}
//-----------------------------------------------------------------
+ // Test getStreamVolume consistency with AudioService
+ //-----------------------------------------------------------------
+ @Test
+ public void getStreamMinMaxVolume_consistentWithAs() throws Exception {
+ IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
+ IAudioService service = IAudioService.Stub.asInterface(b);
+ final int[] streamTypes = {
+ STREAM_VOICE_CALL,
+ STREAM_SYSTEM,
+ STREAM_RING,
+ STREAM_MUSIC,
+ STREAM_ALARM,
+ STREAM_NOTIFICATION,
+ STREAM_DTMF,
+ STREAM_ACCESSIBILITY,
+ };
+
+ for (int streamType : streamTypes) {
+ assertEquals(service.getStreamMinVolume(streamType),
+ mAudioManager.getStreamMinVolume(streamType));
+ assertEquals(service.getStreamMaxVolume(streamType),
+ mAudioManager.getStreamMaxVolume(streamType));
+ }
+ }
+
+ //-----------------------------------------------------------------
// Test Volume per Attributes setter/getters
//-----------------------------------------------------------------
@Test
diff --git a/media/tests/MtpTests/OWNERS b/media/tests/MtpTests/OWNERS
index bdb6cdbea332..c57265a91eff 100644
--- a/media/tests/MtpTests/OWNERS
+++ b/media/tests/MtpTests/OWNERS
@@ -1,9 +1,9 @@
set noparent
-anothermark@google.com
+vmartensson@google.com
+nkapron@google.com
febinthattil@google.com
-aprasath@google.com
+shubhankarm@google.com
jsharkey@android.com
jameswei@google.com
rmojumder@google.com
-kumarashishg@google.com \ No newline at end of file
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java b/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
index fe27cee7ba2d..acead8e2a0eb 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
@@ -510,7 +510,10 @@ public final class PageContentRepository {
protected Void doInBackground(Void... params) {
synchronized (mLock) {
try {
- if (mRenderer != null) {
+ // A page count < 0 indicates there was an error
+ // opening the document, in which case it doesn't
+ // need to be closed.
+ if (mRenderer != null && mPageCount >= 0) {
mRenderer.closeDocument();
}
} catch (RemoteException re) {
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
index b48c55ddfef0..a9d00e9a77eb 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
@@ -70,6 +70,7 @@ public final class RemotePrintDocument {
private static final int STATE_CANCELING = 6;
private static final int STATE_CANCELED = 7;
private static final int STATE_DESTROYED = 8;
+ private static final int STATE_INVALID = 9;
private final Context mContext;
@@ -287,7 +288,8 @@ public final class RemotePrintDocument {
}
if (mState != STATE_STARTED && mState != STATE_UPDATED
&& mState != STATE_FAILED && mState != STATE_CANCELING
- && mState != STATE_CANCELED && mState != STATE_DESTROYED) {
+ && mState != STATE_CANCELED && mState != STATE_DESTROYED
+ && mState != STATE_INVALID) {
throw new IllegalStateException("Cannot finish in state:"
+ stateToString(mState));
}
@@ -300,6 +302,16 @@ public final class RemotePrintDocument {
}
}
+ /**
+ * Mark this document as invalid.
+ */
+ public void invalid() {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "[CALLED] invalid()");
+ }
+ mState = STATE_INVALID;
+ }
+
public void cancel(boolean force) {
if (DEBUG) {
Log.i(LOG_TAG, "[CALLED] cancel(" + force + ")");
@@ -491,6 +503,9 @@ public final class RemotePrintDocument {
case STATE_DESTROYED: {
return "STATE_DESTROYED";
}
+ case STATE_INVALID: {
+ return "STATE_INVALID";
+ }
default: {
return "STATE_UNKNOWN";
}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index 4a3a6d248254..2e3234e6e622 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -167,6 +167,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
private static final int STATE_PRINTER_UNAVAILABLE = 6;
private static final int STATE_UPDATE_SLOW = 7;
private static final int STATE_PRINT_COMPLETED = 8;
+ private static final int STATE_FILE_INVALID = 9;
private static final int UI_STATE_PREVIEW = 0;
private static final int UI_STATE_ERROR = 1;
@@ -404,6 +405,11 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
public void onPause() {
PrintSpoolerService spooler = mSpoolerProvider.getSpooler();
+ if (isInvalid()) {
+ super.onPause();
+ return;
+ }
+
if (mState == STATE_INITIALIZING) {
if (isFinishing()) {
if (spooler != null) {
@@ -478,7 +484,8 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
}
if (mState == STATE_PRINT_CANCELED || mState == STATE_PRINT_CONFIRMED
- || mState == STATE_PRINT_COMPLETED) {
+ || mState == STATE_PRINT_COMPLETED
+ || mState == STATE_FILE_INVALID) {
return true;
}
@@ -509,23 +516,32 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
@Override
public void onMalformedPdfFile() {
onPrintDocumentError("Cannot print a malformed PDF file");
+ mPrintedDocument.invalid();
+ setState(STATE_FILE_INVALID);
}
@Override
public void onSecurePdfFile() {
onPrintDocumentError("Cannot print a password protected PDF file");
+ mPrintedDocument.invalid();
+ setState(STATE_FILE_INVALID);
}
private void onPrintDocumentError(String message) {
setState(mProgressMessageController.cancel());
- ensureErrorUiShown(null, PrintErrorFragment.ACTION_RETRY);
+ ensureErrorUiShown(
+ getString(R.string.print_cannot_load_page), PrintErrorFragment.ACTION_NONE);
setState(STATE_UPDATE_FAILED);
if (DEBUG) {
Log.i(LOG_TAG, "PrintJob state[" + PrintJobInfo.STATE_FAILED + "] reason: " + message);
}
PrintSpoolerService spooler = mSpoolerProvider.getSpooler();
- spooler.setPrintJobState(mPrintJob.getId(), PrintJobInfo.STATE_FAILED, message);
+ // Use a cancel state for the spooler. This will prevent the notification from getting
+ // displayed and will remove the job. The notification (which displays the cancel and
+ // restart options) doesn't make sense for an invalid document since it will just fail
+ // again.
+ spooler.setPrintJobState(mPrintJob.getId(), PrintJobInfo.STATE_CANCELED, message);
mPrintedDocument.finish();
}
@@ -995,6 +1011,9 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
}
private void setState(int state) {
+ if (isInvalid()) {
+ return;
+ }
if (isFinalState(mState)) {
if (isFinalState(state)) {
if (DEBUG) {
@@ -1015,7 +1034,12 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
private static boolean isFinalState(int state) {
return state == STATE_PRINT_CANCELED
|| state == STATE_PRINT_COMPLETED
- || state == STATE_CREATE_FILE_FAILED;
+ || state == STATE_CREATE_FILE_FAILED
+ || state == STATE_FILE_INVALID;
+ }
+
+ private boolean isInvalid() {
+ return mState == STATE_FILE_INVALID;
}
private void updateSelectedPagesFromPreview() {
@@ -1100,7 +1124,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
}
private void ensurePreviewUiShown() {
- if (isFinishing() || isDestroyed()) {
+ if (isFinishing() || isDestroyed() || isInvalid()) {
return;
}
if (mUiState != UI_STATE_PREVIEW) {
@@ -1257,6 +1281,9 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
}
private boolean updateDocument(boolean clearLastError) {
+ if (isInvalid()) {
+ return false;
+ }
if (!clearLastError && mPrintedDocument.hasUpdateError()) {
return false;
}
@@ -1676,7 +1703,8 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
|| mState == STATE_UPDATE_FAILED
|| mState == STATE_CREATE_FILE_FAILED
|| mState == STATE_PRINTER_UNAVAILABLE
- || mState == STATE_UPDATE_SLOW) {
+ || mState == STATE_UPDATE_SLOW
+ || mState == STATE_FILE_INVALID) {
disableOptionsUi(isFinalState(mState));
return;
}
@@ -2100,6 +2128,9 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
}
private boolean canUpdateDocument() {
+ if (isInvalid()) {
+ return false;
+ }
if (mPrintedDocument.isDestroyed()) {
return false;
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt
index 3309faaa8db2..3a6327962dc2 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt
@@ -32,6 +32,7 @@ import kotlinx.coroutines.flow.flowOn
data class EnhancedConfirmation(
val key: String,
val packageName: String,
+ val isRestrictedSettingAllowed: Boolean?
)
data class Restrictions(
val userId: Int = UserHandle.myUserId(),
@@ -92,6 +93,9 @@ internal class RestrictionsProviderImpl(
}
restrictions.enhancedConfirmation?.let { ec ->
+ if (ec.isRestrictedSettingAllowed == true) {
+ return NoRestricted
+ }
RestrictedLockUtilsInternal
.checkIfRequiresEnhancedConfirmation(context, ec.key,
ec.packageName)
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
index 7466f95e3fb8..5580d2e3211b 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
@@ -155,7 +155,7 @@ internal fun <T : AppRecord> TogglePermissionAppListModel<T>.TogglePermissionApp
}
RestrictedSwitchPreference(
model = switchModel,
- restrictions = getRestrictions(userId, packageName),
+ restrictions = getRestrictions(userId, packageName, isAllowed()),
ifBlockedByAdminOverrideCheckedValueTo = switchifBlockedByAdminOverrideCheckedValueTo,
restrictionsProviderFactory = restrictionsProviderFactory,
)
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt
index d2867af1eda6..771eb85ee21a 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt
@@ -116,12 +116,15 @@ fun <T : AppRecord> TogglePermissionAppListModel<T>.isChangeableWithSystemUidChe
fun <T : AppRecord> TogglePermissionAppListModel<T>.getRestrictions(
userId: Int,
packageName: String,
+ isRestrictedSettingAllowed: Boolean?
) =
Restrictions(
userId = userId,
keys = switchRestrictionKeys,
enhancedConfirmation =
- enhancedConfirmationKey?.let { key -> EnhancedConfirmation(key, packageName) },
+ enhancedConfirmationKey?.let {
+ key -> EnhancedConfirmation(key, packageName, isRestrictedSettingAllowed)
+ },
)
interface TogglePermissionAppListProvider {
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
index ec44d2af4ffa..bef2bdaaefaf 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
@@ -149,13 +149,14 @@ internal class TogglePermissionInternalAppListModel<T : AppRecord>(
@Composable
fun getSummary(record: T): () -> String {
+ val allowed = listModel.isAllowed(record)
val restrictions =
listModel.getRestrictions(
userId = record.app.userId,
packageName = record.app.packageName,
+ allowed()
)
val restrictedMode by restrictionsProviderFactory.rememberRestrictedMode(restrictions)
- val allowed = listModel.isAllowed(record)
return RestrictedSwitchPreferenceModel.getSummary(
context = context,
summaryIfNoRestricted = { getSummaryIfNoRestricted(allowed()) },
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index ec346c87cdf3..6491bf5c8f5b 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -553,7 +553,11 @@
<service android:name=".wallpapers.GradientColorWallpaper"
android:singleUser="true"
android:permission="android.permission.BIND_WALLPAPER"
- android:exported="true" />
+ android:exported="true">
+ <meta-data android:name="android.service.wallpaper"
+ android:resource="@xml/gradient_color_wallpaper">
+ </meta-data>
+ </service>
<activity android:name=".tuner.TunerActivity"
android:enabled="false"
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 01ee2cd5d22b..7cf83135c237 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -1852,6 +1852,13 @@ flag {
}
flag {
+ name: "double_tap_to_sleep"
+ namespace: "systemui"
+ description: "Enable Double Tap to Sleep"
+ bug: "385194612"
+}
+
+flag{
name: "gsf_bouncer"
namespace: "systemui"
description: "Applies GSF font styles to Bouncer surfaces."
@@ -1935,16 +1942,6 @@ flag {
}
flag {
- name: "stabilize_heads_up_group"
- namespace: "systemui"
- description: "Stabilize heads up groups in VisualStabilityCoordinator"
- bug: "381864715"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "magnetic_notification_horizontal_swipe"
namespace: "systemui"
description: "Add support for magnetic behavior on horizontal notification swipes."
@@ -1962,6 +1959,13 @@ flag {
}
flag {
+ name: "permission_helper_ui_rich_ongoing"
+ namespace: "systemui"
+ description: "[RONs] Guards inline permission helper for demoting RONs"
+ bug: "379186372"
+}
+
+flag {
name: "aod_ui_rich_ongoing"
namespace: "systemui"
description: "Show a rich ongoing notification on the always-on display (depends on ui_rich_ongoing)"
@@ -1976,4 +1980,14 @@ flag {
metadata {
purpose: PURPOSE_BUGFIX
}
-} \ No newline at end of file
+}
+
+flag {
+ name: "shade_launch_accessibility"
+ namespace: "systemui"
+ description: "Intercept accessibility focus events for the Shade during launch animations to avoid stray TalkBack events."
+ bug: "379222226"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
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 bdd0da9ce4a4..4e10ff689b19 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
@@ -67,11 +67,10 @@ import com.android.systemui.Flags
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.compose.modifiers.sysuiResTag
-import com.android.systemui.haptics.slider.SeekableSliderTrackerConfig
-import com.android.systemui.haptics.slider.SliderHapticFeedbackConfig
import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.res.R
+import com.android.systemui.volume.haptics.ui.VolumeHapticsConfigsProvider
import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.SliderState
import kotlin.math.round
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -103,6 +102,10 @@ fun VolumeSlider(
}
val value by valueState(state)
+ val interactionSource = remember { MutableInteractionSource() }
+ val hapticsViewModel: SliderHapticsViewModel? =
+ setUpHapticsViewModel(value, state.valueRange, interactionSource, hapticsViewModelFactory)
+
Column(modifier = modifier.animateContentSize(), verticalArrangement = Arrangement.Top) {
Row(
horizontalArrangement = Arrangement.spacedBy(12.dp),
@@ -127,8 +130,14 @@ fun VolumeSlider(
Slider(
value = value,
valueRange = state.valueRange,
- onValueChange = onValueChange,
- onValueChangeFinished = onValueChangeFinished,
+ onValueChange = { newValue ->
+ hapticsViewModel?.addVelocityDataPoint(newValue)
+ onValueChange(newValue)
+ },
+ onValueChangeFinished = {
+ hapticsViewModel?.onValueChangeEnded()
+ onValueChangeFinished?.invoke()
+ },
enabled = state.isEnabled,
modifier =
Modifier.height(40.dp)
@@ -210,41 +219,8 @@ private fun LegacyVolumeSlider(
) {
val value by valueState(state)
val interactionSource = remember { MutableInteractionSource() }
- val sliderStepSize = 1f / (state.valueRange.endInclusive - state.valueRange.start)
val hapticsViewModel: SliderHapticsViewModel? =
- hapticsViewModelFactory?.let {
- rememberViewModel(traceName = "SliderHapticsViewModel") {
- it.create(
- interactionSource,
- state.valueRange,
- Orientation.Horizontal,
- SliderHapticFeedbackConfig(
- lowerBookendScale = 0.2f,
- progressBasedDragMinScale = 0.2f,
- progressBasedDragMaxScale = 0.5f,
- deltaProgressForDragThreshold = 0f,
- additionalVelocityMaxBump = 0.2f,
- maxVelocityToScale = 0.1f, /* slider progress(from 0 to 1) per sec */
- sliderStepSize = sliderStepSize,
- ),
- SeekableSliderTrackerConfig(
- lowerBookendThreshold = 0f,
- upperBookendThreshold = 1f,
- ),
- )
- }
- }
- var lastDiscreteStep by remember { mutableFloatStateOf(round(value)) }
- LaunchedEffect(value) {
- snapshotFlow { value }
- .map { round(it) }
- .filter { it != lastDiscreteStep }
- .distinctUntilChanged()
- .collect { discreteStep ->
- lastDiscreteStep = discreteStep
- hapticsViewModel?.onValueChange(discreteStep)
- }
- }
+ setUpHapticsViewModel(value, state.valueRange, interactionSource, hapticsViewModelFactory)
PlatformSlider(
modifier =
@@ -357,3 +333,36 @@ private fun SliderIcon(
content = { Icon(modifier = Modifier.size(24.dp), icon = icon) },
)
}
+
+@Composable
+fun setUpHapticsViewModel(
+ value: Float,
+ valueRange: ClosedFloatingPointRange<Float>,
+ interactionSource: MutableInteractionSource,
+ hapticsViewModelFactory: SliderHapticsViewModel.Factory?,
+): SliderHapticsViewModel? {
+ return hapticsViewModelFactory?.let {
+ rememberViewModel(traceName = "SliderHapticsViewModel") {
+ it.create(
+ interactionSource,
+ valueRange,
+ Orientation.Horizontal,
+ VolumeHapticsConfigsProvider.sliderHapticFeedbackConfig(valueRange),
+ VolumeHapticsConfigsProvider.seekableSliderTrackerConfig,
+ )
+ }
+ .also { hapticsViewModel ->
+ var lastDiscreteStep by remember { mutableFloatStateOf(round(value)) }
+ LaunchedEffect(value) {
+ snapshotFlow { value }
+ .map { round(it) }
+ .filter { it != lastDiscreteStep }
+ .distinctUntilChanged()
+ .collect { discreteStep ->
+ lastDiscreteStep = discreteStep
+ hapticsViewModel.onValueChange(discreteStep)
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
index e69fa994931d..b2bc9ef4e363 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
@@ -124,7 +124,7 @@ class FlexClockController(private val clockCtx: ClockContext) : ClockController
ClockFontAxis(
key = "wght",
type = AxisType.Float,
- minValue = 1f,
+ minValue = 25f,
currentValue = 400f,
maxValue = 1000f,
name = "Weight",
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
index 827bd6898310..67cbf3082632 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
@@ -125,7 +125,19 @@ class FlexClockFaceController(clockCtx: ClockContext, private val isLargeClock:
layerController.faceEvents.onThemeChanged(theme)
}
- override fun onFontAxesChanged(axes: List<ClockFontAxisSetting>) {
+ override fun onFontAxesChanged(settings: List<ClockFontAxisSetting>) {
+ var axes = settings
+ if (!isLargeClock) {
+ axes =
+ axes.map { axis ->
+ if (axis.key == "wdth" && axis.value > SMALL_CLOCK_MAX_WDTH) {
+ axis.copy(value = SMALL_CLOCK_MAX_WDTH)
+ } else {
+ axis
+ }
+ }
+ }
+
layerController.events.onFontAxesChanged(axes)
}
@@ -236,6 +248,7 @@ class FlexClockFaceController(clockCtx: ClockContext, private val isLargeClock:
}
companion object {
+ val SMALL_CLOCK_MAX_WDTH = 120f
val SMALL_LAYER_CONFIG =
LayerConfig(
timespec = DigitalTimespec.TIME_FULL_FORMAT,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepositoryTest.kt
index 3bf59f34db76..cd05980385e0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepositoryTest.kt
@@ -50,6 +50,7 @@ import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCommand
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory
+import com.android.systemui.keyboard.shortcut.shortcutHelperAccessibilityShortcutsSource
import com.android.systemui.keyboard.shortcut.shortcutHelperAppCategoriesShortcutsSource
import com.android.systemui.keyboard.shortcut.shortcutHelperCurrentAppShortcutsSource
import com.android.systemui.keyboard.shortcut.shortcutHelperInputShortcutsSource
@@ -88,6 +89,7 @@ class DefaultShortcutCategoriesRepositoryTest : SysuiTestCase() {
it.shortcutHelperAppCategoriesShortcutsSource = fakeAppCategoriesSource
it.shortcutHelperInputShortcutsSource = FakeKeyboardShortcutGroupsSource()
it.shortcutHelperCurrentAppShortcutsSource = FakeKeyboardShortcutGroupsSource()
+ it.shortcutHelperAccessibilityShortcutsSource = FakeKeyboardShortcutGroupsSource()
}
private val repo = kosmos.defaultShortcutCategoriesRepository
@@ -284,14 +286,20 @@ class DefaultShortcutCategoriesRepositoryTest : SysuiTestCase() {
val categories by collectLastValue(repo.categories)
val cycleForwardThroughRecentAppsShortcut =
- categories?.first { it.type == ShortcutCategoryType.MultiTasking }
- ?.subCategories?.first { it.label == recentAppsGroup.label }
- ?.shortcuts?.first { it.label == CYCLE_FORWARD_THROUGH_RECENT_APPS_SHORTCUT_LABEL }
+ categories
+ ?.first { it.type == ShortcutCategoryType.MultiTasking }
+ ?.subCategories
+ ?.first { it.label == recentAppsGroup.label }
+ ?.shortcuts
+ ?.first { it.label == CYCLE_FORWARD_THROUGH_RECENT_APPS_SHORTCUT_LABEL }
val cycleBackThroughRecentAppsShortcut =
- categories?.first { it.type == ShortcutCategoryType.MultiTasking }
- ?.subCategories?.first { it.label == recentAppsGroup.label }
- ?.shortcuts?.first { it.label == CYCLE_BACK_THROUGH_RECENT_APPS_SHORTCUT_LABEL }
+ categories
+ ?.first { it.type == ShortcutCategoryType.MultiTasking }
+ ?.subCategories
+ ?.first { it.label == recentAppsGroup.label }
+ ?.shortcuts
+ ?.first { it.label == CYCLE_BACK_THROUGH_RECENT_APPS_SHORTCUT_LABEL }
assertThat(cycleForwardThroughRecentAppsShortcut?.isCustomizable).isFalse()
assertThat(cycleBackThroughRecentAppsShortcut?.isCustomizable).isFalse()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt
index 8f0bc640f0eb..61490986f4a9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt
@@ -38,6 +38,7 @@ import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.InputMethodEditor
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.MultiTasking
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.System
+import com.android.systemui.keyboard.shortcut.shortcutHelperAccessibilityShortcutsSource
import com.android.systemui.keyboard.shortcut.shortcutHelperAppCategoriesShortcutsSource
import com.android.systemui.keyboard.shortcut.shortcutHelperCategoriesInteractor
import com.android.systemui.keyboard.shortcut.shortcutHelperCurrentAppShortcutsSource
@@ -76,6 +77,7 @@ class ShortcutHelperCategoriesInteractorTest : SysuiTestCase() {
it.shortcutHelperMultiTaskingShortcutsSource = multitaskingShortcutsSource
it.shortcutHelperAppCategoriesShortcutsSource = FakeKeyboardShortcutGroupsSource()
it.shortcutHelperCurrentAppShortcutsSource = FakeKeyboardShortcutGroupsSource()
+ it.shortcutHelperAccessibilityShortcutsSource = FakeKeyboardShortcutGroupsSource()
it.userTracker = FakeUserTracker(onCreateCurrentUserContext = { mockUserContext })
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt
index 000024f9b814..7a343351ef64 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt
@@ -26,6 +26,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.keyboard.shortcut.data.source.FakeKeyboardShortcutGroupsSource
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts
import com.android.systemui.keyboard.shortcut.shortcutCustomizationDialogStarterFactory
+import com.android.systemui.keyboard.shortcut.shortcutHelperAccessibilityShortcutsSource
import com.android.systemui.keyboard.shortcut.shortcutHelperAppCategoriesShortcutsSource
import com.android.systemui.keyboard.shortcut.shortcutHelperCurrentAppShortcutsSource
import com.android.systemui.keyboard.shortcut.shortcutHelperInputShortcutsSource
@@ -66,6 +67,7 @@ class ShortcutHelperDialogStarterTest : SysuiTestCase() {
it.testDispatcher = UnconfinedTestDispatcher()
it.shortcutHelperSystemShortcutsSource = fakeSystemSource
it.shortcutHelperMultiTaskingShortcutsSource = fakeMultiTaskingSource
+ it.shortcutHelperAccessibilityShortcutsSource = FakeKeyboardShortcutGroupsSource()
it.shortcutHelperAppCategoriesShortcutsSource = FakeKeyboardShortcutGroupsSource()
it.shortcutHelperInputShortcutsSource = FakeKeyboardShortcutGroupsSource()
it.shortcutHelperCurrentAppShortcutsSource = FakeKeyboardShortcutGroupsSource()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
index 3fc46b973959..cf38072912e9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
@@ -45,6 +45,7 @@ import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.System
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory
import com.android.systemui.keyboard.shortcut.shared.model.shortcut
+import com.android.systemui.keyboard.shortcut.shortcutHelperAccessibilityShortcutsSource
import com.android.systemui.keyboard.shortcut.shortcutHelperAppCategoriesShortcutsSource
import com.android.systemui.keyboard.shortcut.shortcutHelperCurrentAppShortcutsSource
import com.android.systemui.keyboard.shortcut.shortcutHelperInputShortcutsSource
@@ -95,6 +96,7 @@ class ShortcutHelperViewModelTest : SysuiTestCase() {
it.shortcutHelperMultiTaskingShortcutsSource = fakeMultiTaskingSource
it.shortcutHelperAppCategoriesShortcutsSource = FakeKeyboardShortcutGroupsSource()
it.shortcutHelperInputShortcutsSource = FakeKeyboardShortcutGroupsSource()
+ it.shortcutHelperAccessibilityShortcutsSource = FakeKeyboardShortcutGroupsSource()
it.shortcutHelperCurrentAppShortcutsSource = fakeCurrentAppsSource
it.userTracker = FakeUserTracker(onCreateCurrentUserContext = { mockUserContext })
}
@@ -112,9 +114,12 @@ class ShortcutHelperViewModelTest : SysuiTestCase() {
fakeSystemSource.setGroups(TestShortcuts.systemGroups)
fakeMultiTaskingSource.setGroups(TestShortcuts.multitaskingGroups)
fakeCurrentAppsSource.setGroups(TestShortcuts.currentAppGroups)
- whenever(mockPackageManager.getApplicationInfo(anyString(), eq(0))).thenReturn(mockApplicationInfo)
- whenever(mockPackageManager.getApplicationLabel(mockApplicationInfo)).thenReturn("Current App")
- whenever(mockPackageManager.getApplicationIcon(anyString())).thenThrow(NameNotFoundException())
+ whenever(mockPackageManager.getApplicationInfo(anyString(), eq(0)))
+ .thenReturn(mockApplicationInfo)
+ whenever(mockPackageManager.getApplicationLabel(mockApplicationInfo))
+ .thenReturn("Current App")
+ whenever(mockPackageManager.getApplicationIcon(anyString()))
+ .thenThrow(NameNotFoundException())
whenever(mockUserContext.packageManager).thenReturn(mockPackageManager)
whenever(mockUserContext.getSystemService(INPUT_SERVICE)).thenReturn(inputManager)
}
@@ -278,11 +283,11 @@ class ShortcutHelperViewModelTest : SysuiTestCase() {
fun shortcutsUiState_currentAppIsLauncher_defaultSelectedCategoryIsSystem() =
testScope.runTest {
whenever(
- mockRoleManager.getRoleHoldersAsUser(
- RoleManager.ROLE_HOME,
- fakeUserTracker.userHandle,
+ mockRoleManager.getRoleHoldersAsUser(
+ RoleManager.ROLE_HOME,
+ fakeUserTracker.userHandle,
+ )
)
- )
.thenReturn(listOf(TestShortcuts.currentAppPackageName))
val uiState by collectLastValue(viewModel.shortcutsUiState)
@@ -318,23 +323,23 @@ class ShortcutHelperViewModelTest : SysuiTestCase() {
label = "System",
iconSource = IconSource(imageVector = Icons.Default.Tv),
shortcutCategory =
- ShortcutCategory(
- System,
- subCategoryWithShortcutLabels("first Foo shortcut1"),
- subCategoryWithShortcutLabels(
- "second foO shortcut2",
- subCategoryLabel = SECOND_SIMPLE_GROUP_LABEL,
+ ShortcutCategory(
+ System,
+ subCategoryWithShortcutLabels("first Foo shortcut1"),
+ subCategoryWithShortcutLabels(
+ "second foO shortcut2",
+ subCategoryLabel = SECOND_SIMPLE_GROUP_LABEL,
+ ),
),
- ),
),
ShortcutCategoryUi(
label = "Multitasking",
iconSource = IconSource(imageVector = Icons.Default.VerticalSplit),
shortcutCategory =
- ShortcutCategory(
- MultiTasking,
- subCategoryWithShortcutLabels("third FoO shortcut1"),
- ),
+ ShortcutCategory(
+ MultiTasking,
+ subCategoryWithShortcutLabels("third FoO shortcut1"),
+ ),
),
)
}
@@ -420,9 +425,8 @@ class ShortcutHelperViewModelTest : SysuiTestCase() {
@Test
fun shortcutsUiState_shouldShowResetButton_isTrueWhenThereAreCustomShortcuts() =
testScope.runTest {
- whenever(
- inputManager.getCustomInputGestures(/* filter= */ InputGestureData.Filter.KEY)
- ).thenReturn(listOf(allAppsInputGestureData))
+ whenever(inputManager.getCustomInputGestures(/* filter= */ InputGestureData.Filter.KEY))
+ .thenReturn(listOf(allAppsInputGestureData))
val uiState by collectLastValue(viewModel.shortcutsUiState)
testHelper.showFromActivity()
@@ -433,7 +437,7 @@ class ShortcutHelperViewModelTest : SysuiTestCase() {
@Test
fun shortcutsUiState_searchQuery_isResetAfterHelperIsClosedAndReOpened() =
- testScope.runTest{
+ testScope.runTest {
val uiState by collectLastValue(viewModel.shortcutsUiState)
openHelperAndSearchForFooString()
@@ -443,7 +447,7 @@ class ShortcutHelperViewModelTest : SysuiTestCase() {
assertThat((uiState as? ShortcutsUiState.Active)?.searchQuery).isEqualTo("")
}
- private fun openHelperAndSearchForFooString(){
+ private fun openHelperAndSearchForFooString() {
testHelper.showFromActivity()
viewModel.onSearchQueryChanged("foo")
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModelTest.kt
index 83b821619659..286f8bfd63a2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModelTest.kt
@@ -123,8 +123,8 @@ class AlternateBouncerToPrimaryBouncerTransitionViewModelTest : SysuiTestCase()
kosmos.keyguardWindowBlurTestUtil.assertTransitionToBlurRadius(
transitionProgress = listOf(0f, 0f, 0.1f, 0.2f, 0.3f, 1f),
- startValue = kosmos.blurConfig.maxBlurRadiusPx / 3.0f,
- endValue = kosmos.blurConfig.maxBlurRadiusPx / 3.0f,
+ startValue = kosmos.blurConfig.maxBlurRadiusPx,
+ endValue = kosmos.blurConfig.maxBlurRadiusPx,
transitionFactory = ::step,
actualValuesProvider = { values },
checkInterpolatedValues = false,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
index fdee8d0544f0..60a19a4c7d07 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
@@ -198,8 +198,8 @@ class LockscreenToPrimaryBouncerTransitionViewModelTest(flags: FlagsParameteriza
kosmos.keyguardWindowBlurTestUtil.assertTransitionToBlurRadius(
transitionProgress = listOf(0f, 0f, 0.1f, 0.2f, 0.3f, 1f),
- startValue = kosmos.blurConfig.maxBlurRadiusPx / 3.0f,
- endValue = kosmos.blurConfig.maxBlurRadiusPx / 3.0f,
+ startValue = kosmos.blurConfig.maxBlurRadiusPx,
+ endValue = kosmos.blurConfig.maxBlurRadiusPx,
transitionFactory = ::step,
actualValuesProvider = { values },
checkInterpolatedValues = false,
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 7f9313cbeb5b..83b68fed768e 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
@@ -18,7 +18,7 @@ package com.android.systemui.navigationbar.views;
import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
-import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN;
import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_ADJUST_NOTHING;
import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_DEFAULT;
import static android.inputmethodservice.InputMethodService.IME_VISIBLE;
@@ -32,7 +32,7 @@ import static com.android.systemui.navigationbar.views.NavigationBar.NavBarActio
import static com.android.systemui.navigationbar.views.buttons.KeyButtonView.NavBarButtonEvent.NAVBAR_IME_SWITCHER_BUTTON_LONGPRESS;
import static com.android.systemui.navigationbar.views.buttons.KeyButtonView.NavBarButtonEvent.NAVBAR_IME_SWITCHER_BUTTON_TAP;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_BUTTON_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
import static com.google.common.truth.Truth.assertThat;
@@ -501,7 +501,7 @@ public class NavigationBarTest extends SysuiTestCase {
mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, IME_VISIBLE,
BACK_DISPOSITION_DEFAULT, true /* showImeSwitcher */);
verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SHOWING), eq(true));
- verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SWITCHER_SHOWING), eq(true));
+ verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SWITCHER_BUTTON_SHOWING), eq(true));
}
/**
@@ -515,7 +515,7 @@ public class NavigationBarTest extends SysuiTestCase {
mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, IME_VISIBLE,
BACK_DISPOSITION_DEFAULT, false /* showImeSwitcher */);
verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SHOWING), eq(true));
- verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SWITCHER_SHOWING), eq(false));
+ verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SWITCHER_BUTTON_SHOWING), eq(false));
}
/**
@@ -532,7 +532,7 @@ public class NavigationBarTest extends SysuiTestCase {
mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, 0 /* vis */,
BACK_DISPOSITION_DEFAULT, true /* showImeSwitcher */);
verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SHOWING), eq(false));
- verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SWITCHER_SHOWING), eq(false));
+ verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SWITCHER_BUTTON_SHOWING), eq(false));
}
/**
@@ -546,7 +546,7 @@ public class NavigationBarTest extends SysuiTestCase {
mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, IME_VISIBLE,
BACK_DISPOSITION_ADJUST_NOTHING, true /* showImeSwitcher */);
verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SHOWING), eq(true));
- verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SWITCHER_SHOWING), eq(true));
+ verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SWITCHER_BUTTON_SHOWING), eq(true));
}
@Test
@@ -568,12 +568,12 @@ public class NavigationBarTest extends SysuiTestCase {
// Verify IME window state will be updated in default NavBar & external NavBar state reset.
assertEquals(NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN
- | NAVIGATION_HINT_IME_SWITCHER_SHOWN,
+ | NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN,
defaultNavBar.getNavigationIconHints());
assertFalse((externalNavBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
assertFalse((externalNavBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
- assertFalse((externalNavBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SWITCHER_SHOWN)
- != 0);
+ assertFalse((externalNavBar.getNavigationIconHints()
+ & NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN) != 0);
externalNavBar.setImeWindowStatus(EXTERNAL_DISPLAY_ID, IME_VISIBLE,
BACK_DISPOSITION_DEFAULT, true);
@@ -581,12 +581,12 @@ public class NavigationBarTest extends SysuiTestCase {
BACK_DISPOSITION_DEFAULT, false);
// Verify IME window state will be updated in external NavBar & default NavBar state reset.
assertEquals(NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN
- | NAVIGATION_HINT_IME_SWITCHER_SHOWN,
+ | NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN,
externalNavBar.getNavigationIconHints());
assertFalse((defaultNavBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
assertFalse((defaultNavBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
- assertFalse((defaultNavBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SWITCHER_SHOWN)
- != 0);
+ assertFalse((defaultNavBar.getNavigationIconHints()
+ & NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN) != 0);
}
@Test
@@ -604,8 +604,8 @@ public class NavigationBarTest extends SysuiTestCase {
BACK_DISPOSITION_DEFAULT, true);
assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
- assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SWITCHER_SHOWN)
- != 0);
+ assertTrue((mNavigationBar.getNavigationIconHints()
+ & NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN) != 0);
// Verify navbar didn't alter and showing back icon when the keyguard is showing without
// requesting IME insets visible.
@@ -614,8 +614,8 @@ public class NavigationBarTest extends SysuiTestCase {
BACK_DISPOSITION_DEFAULT, true);
assertFalse((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
assertFalse((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
- assertFalse((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SWITCHER_SHOWN)
- != 0);
+ assertFalse((mNavigationBar.getNavigationIconHints()
+ & NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN) != 0);
// Verify navbar altered and showing back icon when the keyguard is showing and
// requesting IME insets visible.
@@ -625,8 +625,8 @@ public class NavigationBarTest extends SysuiTestCase {
BACK_DISPOSITION_DEFAULT, true);
assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
- assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SWITCHER_SHOWN)
- != 0);
+ assertTrue((mNavigationBar.getNavigationIconHints()
+ & NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN) != 0);
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index ee9cb141a700..555c717e1e65 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -20,7 +20,7 @@ import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.testing.TestableLooper.RunWithLooper
import android.view.Choreographer
-import android.view.MotionEvent
+import android.view.accessibility.AccessibilityEvent
import android.widget.FrameLayout
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -45,7 +45,10 @@ import com.android.systemui.res.R
import com.android.systemui.settings.brightness.data.repository.BrightnessMirrorShowingRepository
import com.android.systemui.settings.brightness.domain.interactor.BrightnessMirrorShowingInteractor
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
+import com.android.systemui.shade.data.repository.ShadeAnimationRepository
+import com.android.systemui.shade.data.repository.ShadeRepositoryImpl
import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
+import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractorLegacyImpl
import com.android.systemui.statusbar.BlurUtils
import com.android.systemui.statusbar.DragDownHelper
import com.android.systemui.statusbar.LockscreenShadeTransitionController
@@ -185,6 +188,10 @@ class NotificationShadeWindowViewTest : SysuiTestCase() {
notificationShadeDepthController,
underTest,
shadeViewController,
+ ShadeAnimationInteractorLegacyImpl(
+ ShadeAnimationRepository(),
+ ShadeRepositoryImpl(testScope),
+ ),
panelExpansionInteractor,
ShadeExpansionStateManager(),
notificationStackScrollLayoutController,
@@ -259,6 +266,20 @@ class NotificationShadeWindowViewTest : SysuiTestCase() {
verify(configurationForwarder).dispatchOnMovedToDisplay(eq(1), eq(config))
}
+ @Test
+ @EnableFlags(AConfigFlags.FLAG_SHADE_LAUNCH_ACCESSIBILITY)
+ fun requestSendAccessibilityEvent_duringLaunchAnimation_blocksFocusEvent() {
+ underTest.setAnimatingContentLaunch(true)
+
+ assertThat(
+ underTest.requestSendAccessibilityEvent(
+ underTest.getChildAt(0),
+ AccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED),
+ )
+ )
+ .isFalse()
+ }
+
private fun captureInteractionEventHandler() {
verify(underTest).setInteractionEventHandler(interactionEventHandlerCaptor.capture())
interactionEventHandler = interactionEventHandlerCaptor.value
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt
index ab5fa8ef43fb..5566c10dc9e9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt
@@ -38,7 +38,7 @@ class QsBatteryModeControllerTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val insetsProviderStore = kosmos.fakeStatusBarContentInsetsProviderStore
- private val insetsProvider = insetsProviderStore.defaultDisplay
+ private val insetsProvider = insetsProviderStore.forDisplay(context.displayId)
@JvmField @Rule val mockitoRule = MockitoJUnit.rule()!!
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorImplTest.kt
index a47db2ec728b..668f568d7f46 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorImplTest.kt
@@ -16,15 +16,11 @@
package com.android.systemui.shade.domain.interactor
-import android.platform.test.annotations.DisableFlags
-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.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
-import com.android.systemui.shade.data.repository.shadeRepository
-import com.android.systemui.shade.shared.flag.DualShade
import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -48,82 +44,79 @@ class ShadeModeInteractorImplTest : SysuiTestCase() {
}
@Test
- @DisableFlags(DualShade.FLAG_NAME)
fun legacyShadeMode_narrowScreen_singleShade() =
testScope.runTest {
val shadeMode by collectLastValue(underTest.shadeMode)
- kosmos.shadeRepository.setShadeLayoutWide(false)
+ kosmos.enableSingleShade()
assertThat(shadeMode).isEqualTo(ShadeMode.Single)
}
@Test
- @DisableFlags(DualShade.FLAG_NAME)
fun legacyShadeMode_wideScreen_splitShade() =
testScope.runTest {
val shadeMode by collectLastValue(underTest.shadeMode)
- kosmos.shadeRepository.setShadeLayoutWide(true)
+ kosmos.enableSplitShade()
assertThat(shadeMode).isEqualTo(ShadeMode.Split)
}
@Test
- @EnableFlags(DualShade.FLAG_NAME)
fun shadeMode_wideScreen_isDual() =
testScope.runTest {
val shadeMode by collectLastValue(underTest.shadeMode)
- kosmos.shadeRepository.setShadeLayoutWide(true)
+ kosmos.enableDualShade(wideLayout = true)
assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
}
@Test
- @EnableFlags(DualShade.FLAG_NAME)
fun shadeMode_narrowScreen_isDual() =
testScope.runTest {
val shadeMode by collectLastValue(underTest.shadeMode)
- kosmos.shadeRepository.setShadeLayoutWide(false)
+ kosmos.enableDualShade(wideLayout = false)
assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
}
@Test
- @EnableFlags(DualShade.FLAG_NAME)
- fun isDualShade_flagEnabled_true() =
+ fun isDualShade_settingEnabled_returnsTrue() =
testScope.runTest {
- // Initiate collection.
+ // TODO(b/391578667): Add a test case for user switching once the bug is fixed.
val shadeMode by collectLastValue(underTest.shadeMode)
+ kosmos.enableDualShade()
+ assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
assertThat(underTest.isDualShade).isTrue()
}
@Test
- @DisableFlags(DualShade.FLAG_NAME)
- fun isDualShade_flagDisabled_false() =
+ fun isDualShade_settingDisabled_returnsFalse() =
testScope.runTest {
- // Initiate collection.
val shadeMode by collectLastValue(underTest.shadeMode)
+ kosmos.disableDualShade()
+ assertThat(shadeMode).isNotEqualTo(ShadeMode.Dual)
assertThat(underTest.isDualShade).isFalse()
}
@Test
fun getTopEdgeSplitFraction_narrowScreen_splitInHalf() =
testScope.runTest {
- // Ensure isShadeLayoutWide is collected.
- val isShadeLayoutWide by collectLastValue(underTest.isShadeLayoutWide)
- kosmos.shadeRepository.setShadeLayoutWide(false)
+ val shadeMode by collectLastValue(underTest.shadeMode)
+ kosmos.enableDualShade(wideLayout = false)
+ assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
assertThat(underTest.getTopEdgeSplitFraction()).isEqualTo(0.5f)
}
@Test
fun getTopEdgeSplitFraction_wideScreen_splitInHalf() =
testScope.runTest {
- // Ensure isShadeLayoutWide is collected.
- val isShadeLayoutWide by collectLastValue(underTest.isShadeLayoutWide)
- kosmos.shadeRepository.setShadeLayoutWide(true)
+ val shadeMode by collectLastValue(underTest.shadeMode)
+ kosmos.enableDualShade(wideLayout = true)
+ assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
assertThat(underTest.getTopEdgeSplitFraction()).isEqualTo(0.5f)
}
}
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 eec23d3ffb1a..55f3717535b7 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
@@ -609,7 +609,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
listOf(
activeNotificationModel(
key = "notif",
- statusBarChipIcon = mock<StatusBarIconView>(),
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
promotedContent = promotedContentBuilder.build(),
)
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolverTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolverTest.kt
index 22906b8724b5..c515d940d2aa 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolverTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolverTest.kt
@@ -144,7 +144,8 @@ class TargetSdkResolverTest : SysuiTestCase() {
/* rankingAdjustment = */ 0,
/* isBubble = */ false,
/* proposedImportance = */ 0,
- /* sensitiveContent = */ false
+ /* sensitiveContent = */ false,
+ /* summarization = */ null
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
index 7b120947b1d6..2aa1efaa429f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
@@ -107,7 +107,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
@Parameters(name = "{0}")
public static List<FlagsParameterization> getParams() {
return SceneContainerFlagParameterizationKt
- .andSceneContainer(allCombinationsOf(Flags.FLAG_STABILIZE_HEADS_UP_GROUP));
+ .andSceneContainer(allCombinationsOf(Flags.FLAG_STABILIZE_HEADS_UP_GROUP_V2));
}
private VisualStabilityCoordinator mCoordinator;
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/BundleNotificationInfoTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/BundleNotificationInfoTest.java
index b2962eeb9001..66277e2d13a6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/BundleNotificationInfoTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/BundleNotificationInfoTest.java
@@ -198,7 +198,8 @@ public class BundleNotificationInfoTest extends SysuiTestCase {
false,
true,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
// and the feedback button is clicked,
final View feedbackButton = mInfo.findViewById(R.id.notification_guts_bundle_feedback);
feedbackButton.performClick();
@@ -253,7 +254,8 @@ public class BundleNotificationInfoTest extends SysuiTestCase {
false,
true,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
final View feedbackButton = mInfo.findViewById(R.id.notification_guts_bundle_feedback);
feedbackButton.performClick();
@@ -294,7 +296,8 @@ public class BundleNotificationInfoTest extends SysuiTestCase {
false,
true,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
final View feedbackButton = mInfo.findViewById(R.id.notification_guts_bundle_feedback);
feedbackButton.performClick();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
index 6a0a5bb3b191..39c42f183481 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
@@ -544,6 +544,7 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
/* wasShownHighPriority = */ eq(true),
eq(assistantFeedbackController),
eq(metricsLogger),
+ any<View.OnClickListener>(),
)
}
@@ -580,6 +581,7 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
/* wasShownHighPriority = */ eq(false),
eq(assistantFeedbackController),
eq(metricsLogger),
+ any<View.OnClickListener>(),
)
}
@@ -614,6 +616,7 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
/* wasShownHighPriority = */ eq(false),
eq(assistantFeedbackController),
eq(metricsLogger),
+ any<View.OnClickListener>(),
)
}
@@ -651,6 +654,7 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
/* wasShownHighPriority = */ eq(false),
eq(assistantFeedbackController),
eq(metricsLogger),
+ any<View.OnClickListener>(),
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index 245a6a0b130c..fdba7ba34855 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -187,7 +187,7 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
true,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger, null);
final TextView textView = mNotificationInfo.findViewById(R.id.pkg_name);
assertTrue(textView.getText().toString().contains("App Name"));
assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
@@ -213,7 +213,7 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
true,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger, null);
final ImageView iconView = mNotificationInfo.findViewById(R.id.pkg_icon);
assertEquals(iconDrawable, iconView.getDrawable());
}
@@ -235,7 +235,7 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
true,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger, null);
final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
assertEquals(GONE, nameView.getVisibility());
}
@@ -266,7 +266,7 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
true,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger, null);
final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
assertEquals(VISIBLE, nameView.getVisibility());
assertTrue(nameView.getText().toString().contains("Proxied"));
@@ -289,7 +289,7 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
true,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger, null);
final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
assertEquals(GONE, groupNameView.getVisibility());
}
@@ -317,7 +317,7 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
true,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger, null);
final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
assertEquals(View.VISIBLE, groupNameView.getVisibility());
assertEquals("Test Group Name", groupNameView.getText());
@@ -340,7 +340,7 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
true,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger, null);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(TEST_CHANNEL_NAME, textView.getText());
}
@@ -362,7 +362,7 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
true,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger, null);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(GONE, textView.getVisibility());
}
@@ -388,7 +388,8 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
true,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(VISIBLE, textView.getVisibility());
}
@@ -410,7 +411,8 @@ public class NotificationInfoTest extends SysuiTestCase {
true,
true,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(VISIBLE, textView.getVisibility());
}
@@ -436,7 +438,8 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
true,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
settingsButton.performClick();
@@ -461,7 +464,8 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
true,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
}
@@ -486,7 +490,8 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
true,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
}
@@ -508,7 +513,8 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
true,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
mNotificationInfo.bindNotification(
mMockPackageManager,
mMockINotificationManager,
@@ -524,7 +530,8 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
true,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertEquals(View.VISIBLE, settingsButton.getVisibility());
}
@@ -546,7 +553,8 @@ public class NotificationInfoTest extends SysuiTestCase {
true,
true,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
final TextView view = mNotificationInfo.findViewById(R.id.non_configurable_text);
assertEquals(View.VISIBLE, view.getVisibility());
assertEquals(mContext.getString(R.string.notification_unblockable_desc),
@@ -589,7 +597,8 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
true,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
final TextView view = mNotificationInfo.findViewById(R.id.non_configurable_call_text);
assertEquals(View.VISIBLE, view.getVisibility());
assertEquals(mContext.getString(R.string.notification_unblockable_call_desc),
@@ -632,7 +641,8 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
true,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
assertEquals(GONE,
mNotificationInfo.findViewById(R.id.non_configurable_call_text).getVisibility());
assertEquals(VISIBLE,
@@ -659,7 +669,8 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
true,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.automatic).getVisibility());
assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.automatic_summary).getVisibility());
}
@@ -681,7 +692,8 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
true,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
assertEquals(GONE, mNotificationInfo.findViewById(R.id.automatic).getVisibility());
assertEquals(GONE, mNotificationInfo.findViewById(R.id.automatic_summary).getVisibility());
}
@@ -705,7 +717,8 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
true,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
assertTrue(mNotificationInfo.findViewById(R.id.automatic).isSelected());
}
@@ -726,7 +739,8 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
true,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
assertTrue(mNotificationInfo.findViewById(R.id.alert).isSelected());
}
@@ -747,7 +761,8 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
false,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
assertTrue(mNotificationInfo.findViewById(R.id.silence).isSelected());
}
@@ -768,7 +783,8 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
true,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
mTestableLooper.processAllMessages();
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
anyString(), eq(TEST_UID), any());
@@ -791,7 +807,8 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
true,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
assertEquals(1, mUiEventLogger.numLogs());
assertEquals(NotificationControlsEvent.NOTIFICATION_CONTROLS_OPEN.getId(),
mUiEventLogger.eventId(0));
@@ -815,7 +832,8 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
false,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
mNotificationInfo.findViewById(R.id.alert).performClick();
mTestableLooper.processAllMessages();
@@ -842,7 +860,8 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
true,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
mNotificationInfo.findViewById(R.id.silence).performClick();
mTestableLooper.processAllMessages();
@@ -869,7 +888,8 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
true,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
mNotificationInfo.findViewById(R.id.automatic).performClick();
mTestableLooper.processAllMessages();
@@ -897,7 +917,8 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
true,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
mNotificationInfo.handleCloseControls(true, false);
mTestableLooper.processAllMessages();
@@ -924,7 +945,8 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
true,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
mNotificationInfo.handleCloseControls(true, false);
mTestableLooper.processAllMessages();
@@ -959,7 +981,8 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
true,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
mNotificationInfo.handleCloseControls(true, false);
@@ -987,7 +1010,8 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
true,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
mNotificationInfo.findViewById(R.id.silence).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1028,7 +1052,8 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
false,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
mNotificationInfo.findViewById(R.id.alert).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1065,7 +1090,8 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
true,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
mNotificationInfo.findViewById(R.id.automatic).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1097,7 +1123,8 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
true,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
mNotificationInfo.findViewById(R.id.silence).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1133,7 +1160,8 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
false,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
assertEquals(mContext.getString(R.string.inline_done_button),
((TextView) mNotificationInfo.findViewById(R.id.done)).getText());
@@ -1171,7 +1199,8 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
false,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
mNotificationInfo.findViewById(R.id.silence).performClick();
mNotificationInfo.handleCloseControls(false, false);
@@ -1202,7 +1231,8 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
false,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
assertEquals(mContext.getString(R.string.inline_done_button),
((TextView) mNotificationInfo.findViewById(R.id.done)).getText());
@@ -1240,7 +1270,8 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
true,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
mNotificationInfo.findViewById(R.id.silence).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1269,7 +1300,8 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
false,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
assertEquals(mContext.getString(R.string.inline_done_button),
((TextView) mNotificationInfo.findViewById(R.id.done)).getText());
@@ -1300,7 +1332,8 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
false,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
mNotificationInfo.findViewById(R.id.alert).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1335,7 +1368,8 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
false,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
mNotificationInfo.findViewById(R.id.alert).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1368,7 +1402,8 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
false,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
mNotificationInfo.findViewById(R.id.alert).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1401,7 +1436,8 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
false,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
mNotificationInfo.findViewById(R.id.alert).performClick();
@@ -1427,7 +1463,8 @@ public class NotificationInfoTest extends SysuiTestCase {
false,
false,
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ null);
assertFalse(mNotificationInfo.willBeRemoved());
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfoTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfoTest.java
new file mode 100644
index 000000000000..b33f93d5b523
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfoTest.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row;
+
+import static android.app.Notification.EXTRA_BUILDER_APPLICATION_INFO;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.INotificationManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.UserHandle;
+import android.platform.test.annotations.EnableFlags;
+import android.service.notification.StatusBarNotification;
+import android.telecom.TelecomManager;
+import android.testing.TestableLooper;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.testing.UiEventLoggerFake;
+import com.android.systemui.Dependency;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.res.R;
+import com.android.systemui.statusbar.notification.AssistantFeedbackController;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.concurrent.CountDownLatch;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@TestableLooper.RunWithLooper
+public class PromotedNotificationInfoTest extends SysuiTestCase {
+ private static final String TEST_PACKAGE_NAME = "test_package";
+ private static final int TEST_UID = 1;
+ private static final String TEST_CHANNEL = "test_channel";
+ private static final String TEST_CHANNEL_NAME = "TEST CHANNEL NAME";
+
+ private TestableLooper mTestableLooper;
+ private PromotedNotificationInfo mInfo;
+ private NotificationChannel mNotificationChannel;
+ private StatusBarNotification mSbn;
+ private NotificationEntry mEntry;
+ private UiEventLoggerFake mUiEventLogger = new UiEventLoggerFake();
+
+ @Rule
+ public MockitoRule mockito = MockitoJUnit.rule();
+ @Mock
+ private MetricsLogger mMetricsLogger;
+ @Mock
+ private INotificationManager mMockINotificationManager;
+ @Mock
+ private PackageManager mMockPackageManager;
+ @Mock
+ private OnUserInteractionCallback mOnUserInteractionCallback;
+ @Mock
+ private ChannelEditorDialogController mChannelEditorDialogController;
+ @Mock
+ private AssistantFeedbackController mAssistantFeedbackController;
+ @Mock
+ private TelecomManager mTelecomManager;
+
+ @Before
+ public void setUp() throws Exception {
+ final ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.uid = TEST_UID; // non-zero
+
+ mNotificationChannel = new NotificationChannel(
+ TEST_CHANNEL, TEST_CHANNEL_NAME, IMPORTANCE_LOW);
+ Notification notification = new Notification();
+ notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, applicationInfo);
+ mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0,
+ notification, UserHandle.getUserHandleForUid(TEST_UID), null, 0);
+ mEntry = new NotificationEntryBuilder().setSbn(mSbn).build();
+ when(mAssistantFeedbackController.isFeedbackEnabled()).thenReturn(false);
+
+ mTestableLooper = TestableLooper.get(this);
+
+ mContext.addMockSystemService(TelecomManager.class, mTelecomManager);
+
+ mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
+ // Inflate the layout
+ final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
+ mInfo = (PromotedNotificationInfo) layoutInflater.inflate(
+ R.layout.promoted_notification_info, null);
+ mInfo.setGutsParent(mock(NotificationGuts.class));
+ // Our view is never attached to a window so the View#post methods in
+ // BundleNotificationInfo never get called. Setting this will skip the post and do the
+ // action immediately.
+ mInfo.mSkipPost = true;
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
+ public void testBindNotification_setsOnClickListenerForFeedback() throws Exception {
+
+ // Bind the notification to the Info object
+ final CountDownLatch latch = new CountDownLatch(1);
+ mInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mOnUserInteractionCallback,
+ mChannelEditorDialogController,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mEntry,
+ null,
+ null,
+ mUiEventLogger,
+ true,
+ false,
+ true,
+ mAssistantFeedbackController,
+ mMetricsLogger,
+ null);
+ // Click demote button
+ final View demoteButton = mInfo.findViewById(R.id.promoted_demote);
+ demoteButton.performClick();
+ // verify that notiManager tried to demote
+ verify(mMockINotificationManager, atLeastOnce()).setCanBePromoted(TEST_PACKAGE_NAME,
+ mSbn.getUid(), false, true);
+
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt
index 43ad042ecf78..57b7df7a8d31 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt
@@ -162,7 +162,7 @@ class KeyguardStatusBarViewControllerTest : SysuiTestCase() {
Mockito.`when`(iconManagerFactory.create(ArgumentMatchers.any(), ArgumentMatchers.any()))
.thenReturn(iconManager)
- Mockito.`when`(statusBarContentInsetsProviderStore.defaultDisplay)
+ Mockito.`when`(statusBarContentInsetsProviderStore.forDisplay(context.displayId))
.thenReturn(kosmos.mockStatusBarContentInsetsProvider)
allowTestableLooperAsMainThread()
looper.runWithLooper {
@@ -180,6 +180,7 @@ class KeyguardStatusBarViewControllerTest : SysuiTestCase() {
private fun createController(): KeyguardStatusBarViewController {
return KeyguardStatusBarViewController(
kosmos.testDispatcher,
+ context,
keyguardStatusBarView,
carrierTextController,
configurationController,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractorTest.kt
index fec186e862be..b837253f44a4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractorTest.kt
@@ -16,12 +16,16 @@
package com.android.systemui.volume.dialog.domain.interactor
+import android.media.AudioManager.RINGER_MODE_NORMAL
+import android.media.AudioManager.RINGER_MODE_SILENT
+import android.media.AudioManager.RINGER_MODE_VIBRATE
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.plugins.fakeVolumeDialogController
import com.android.systemui.testKosmos
import com.android.systemui.volume.dialog.domain.model.VolumeDialogEventModel
import com.google.common.truth.Truth.assertThat
@@ -44,4 +48,30 @@ class VolumeDialogCallbacksInteractorTest : SysuiTestCase() {
val event by collectLastValue(underTest.event)
assertThat(event).isInstanceOf(VolumeDialogEventModel.SubscribedToEvents::class.java)
}
+
+ @Test
+ fun showSilentHint_setsRingerModeToNormal() =
+ kosmos.runTest {
+ fakeVolumeDialogController.setRingerMode(RINGER_MODE_VIBRATE, false)
+
+ underTest // It should eagerly collect the values and update the controller
+ fakeVolumeDialogController.onShowSilentHint()
+ fakeVolumeDialogController.getState()
+
+ assertThat(fakeVolumeDialogController.state.ringerModeInternal)
+ .isEqualTo(RINGER_MODE_NORMAL)
+ }
+
+ @Test
+ fun showVibrateHint_setsRingerModeToSilent() =
+ kosmos.runTest {
+ fakeVolumeDialogController.setRingerMode(RINGER_MODE_VIBRATE, false)
+
+ underTest // It should eagerly collect the values and update the controller
+ fakeVolumeDialogController.onShowVibrateHint()
+ fakeVolumeDialogController.getState()
+
+ assertThat(fakeVolumeDialogController.state.ringerModeInternal)
+ .isEqualTo(RINGER_MODE_SILENT)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractorTest.kt
index 7d5559933cd8..12885a83a70b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractorTest.kt
@@ -25,14 +25,13 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.volume.shared.model.RingerMode
import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.plugins.fakeVolumeDialogController
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -43,8 +42,7 @@ import org.junit.runner.RunWith
@TestableLooper.RunWithLooper
class VolumeDialogRingerInteractorTest : SysuiTestCase() {
- private val kosmos = testKosmos()
- private val testScope = kosmos.testScope
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
private val controller = kosmos.fakeVolumeDialogController
private lateinit var underTest: VolumeDialogRingerInteractor
@@ -57,13 +55,11 @@ class VolumeDialogRingerInteractorTest : SysuiTestCase() {
@Test
fun setRingerMode_normal() =
- testScope.runTest {
- runCurrent()
+ kosmos.runTest {
val ringerModel by collectLastValue(underTest.ringerModel)
underTest.setRingerMode(RingerMode(RINGER_MODE_NORMAL))
controller.getState()
- runCurrent()
assertThat(ringerModel).isNotNull()
assertThat(ringerModel?.currentRingerMode).isEqualTo(RingerMode(RINGER_MODE_NORMAL))
@@ -71,13 +67,11 @@ class VolumeDialogRingerInteractorTest : SysuiTestCase() {
@Test
fun setRingerMode_silent() =
- testScope.runTest {
- runCurrent()
+ kosmos.runTest {
val ringerModel by collectLastValue(underTest.ringerModel)
underTest.setRingerMode(RingerMode(RINGER_MODE_SILENT))
controller.getState()
- runCurrent()
assertThat(ringerModel).isNotNull()
assertThat(ringerModel?.currentRingerMode).isEqualTo(RingerMode(RINGER_MODE_SILENT))
@@ -85,13 +79,11 @@ class VolumeDialogRingerInteractorTest : SysuiTestCase() {
@Test
fun setRingerMode_vibrate() =
- testScope.runTest {
- runCurrent()
+ kosmos.runTest {
val ringerModel by collectLastValue(underTest.ringerModel)
underTest.setRingerMode(RingerMode(RINGER_MODE_VIBRATE))
controller.getState()
- runCurrent()
assertThat(ringerModel).isNotNull()
assertThat(ringerModel?.currentRingerMode).isEqualTo(RingerMode(RINGER_MODE_VIBRATE))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInputEventsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInputEventsInteractorTest.kt
index 799ca4a49038..0a50722d8fed 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInputEventsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInputEventsInteractorTest.kt
@@ -18,7 +18,6 @@ package com.android.systemui.volume.dialog.sliders.domain.interactor
import android.app.ActivityManager
import android.testing.TestableLooper
-import android.view.MotionEvent
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -29,6 +28,7 @@ import com.android.systemui.testKosmos
import com.android.systemui.volume.Events
import com.android.systemui.volume.dialog.domain.interactor.volumeDialogVisibilityInteractor
import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel
+import com.android.systemui.volume.dialog.sliders.shared.model.SliderInputEvent
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import kotlin.time.Duration.Companion.seconds
@@ -73,16 +73,7 @@ class VolumeDialogSliderInputEventsInteractorTest : SysuiTestCase() {
assertThat(dialogVisibility)
.isInstanceOf(VolumeDialogVisibilityModel.Visible::class.java)
- underTest.onTouchEvent(
- MotionEvent.obtain(
- /* downTime = */ 0,
- /* eventTime = */ 0,
- /* action = */ 0,
- /* x = */ 0f,
- /* y = */ 0f,
- /* metaState = */ 0,
- )
- )
+ underTest.onTouchEvent(SliderInputEvent.Touch.Start(0f, 0f))
advanceTimeBy(volumeDialogTimeout / 2)
assertThat(dialogVisibility)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt
index 2985053f56d5..d6343c840d9b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt
@@ -26,4 +26,5 @@ class FakeWallpaperRepository : WallpaperRepository {
override val wallpaperInfo = MutableStateFlow<WallpaperInfo?>(null)
override val wallpaperSupportsAmbientMode = flowOf(false)
override var rootView: View? = null
+ override val shouldSendFocalArea = MutableStateFlow(false)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt
index 03753d9aa884..115edd0d3bb0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt
@@ -67,7 +67,6 @@ class WallpaperRepositoryImplTest : SysuiTestCase() {
fakeBroadcastDispatcher,
userRepository,
keyguardRepository,
- keyguardClockRepository,
wallpaperManager,
context,
)
@@ -252,7 +251,7 @@ class WallpaperRepositoryImplTest : SysuiTestCase() {
@EnableFlags(Flags.FLAG_MAGIC_PORTRAIT_WALLPAPERS)
fun shouldSendNotificationLayout_setMagicPortraitWallpaper_launchSendLayoutJob() =
testScope.runTest {
- val latest by collectLastValue(underTest.shouldSendNotificationLayout)
+ val latest by collectLastValue(underTest.shouldSendFocalArea)
val magicPortraitWallpaper =
mock<WallpaperInfo>().apply {
whenever(this.component)
@@ -273,7 +272,7 @@ class WallpaperRepositoryImplTest : SysuiTestCase() {
@EnableFlags(Flags.FLAG_MAGIC_PORTRAIT_WALLPAPERS)
fun shouldSendNotificationLayout_setNotMagicPortraitWallpaper_cancelSendLayoutJob() =
testScope.runTest {
- val latest by collectLastValue(underTest.shouldSendNotificationLayout)
+ val latest by collectLastValue(underTest.shouldSendFocalArea)
val magicPortraitWallpaper = MAGIC_PORTRAIT_WP
whenever(wallpaperManager.getWallpaperInfoForUser(any()))
.thenReturn(magicPortraitWallpaper)
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index 433c0a71008d..e7d6b2fe08f4 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -159,6 +159,17 @@
<item name="android:shadowRadius">?attr/shadowRadius</item>
</style>
+ <style name="TextAppearance.Keyguard.BottomArea.DoubleShadow">
+ <item name="keyShadowBlur">0.5dp</item>
+ <item name="keyShadowOffsetX">0.5dp</item>
+ <item name="keyShadowOffsetY">0.5dp</item>
+ <item name="keyShadowAlpha">0.8</item>
+ <item name="ambientShadowBlur">0.5dp</item>
+ <item name="ambientShadowOffsetX">0.5dp</item>
+ <item name="ambientShadowOffsetY">0.5dp</item>
+ <item name="ambientShadowAlpha">0.6</item>
+ </style>
+
<style name="TextAppearance.Keyguard.BottomArea.Button">
<item name="android:shadowRadius">0</item>
</style>
diff --git a/packages/SystemUI/res/layout/promoted_notification_info.xml b/packages/SystemUI/res/layout/promoted_notification_info.xml
new file mode 100644
index 000000000000..5d170a98a806
--- /dev/null
+++ b/packages/SystemUI/res/layout/promoted_notification_info.xml
@@ -0,0 +1,387 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2024, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<com.android.systemui.statusbar.notification.row.PromotedNotificationInfo
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:id="@+id/notification_guts"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:focusable="true"
+ android:clipChildren="false"
+ android:clipToPadding="true"
+ android:orientation="vertical"
+ android:paddingStart="@dimen/notification_shade_content_margin_horizontal">
+
+ <!-- Package Info -->
+ <LinearLayout
+ android:id="@+id/header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:clipChildren="false"
+ android:paddingTop="@dimen/notification_guts_header_top_padding"
+ android:clipToPadding="true">
+ <ImageView
+ android:id="@+id/pkg_icon"
+ android:layout_width="@dimen/notification_guts_conversation_icon_size"
+ android:layout_height="@dimen/notification_guts_conversation_icon_size"
+ android:layout_centerVertical="true"
+ android:layout_alignParentStart="true"
+ android:layout_marginEnd="15dp" />
+ <LinearLayout
+ android:id="@+id/names"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:orientation="vertical"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/notification_guts_conversation_icon_size"
+ android:layout_centerVertical="true"
+ android:gravity="center_vertical"
+ android:layout_alignEnd="@id/pkg_icon"
+ android:layout_toEndOf="@id/pkg_icon">
+ <TextView
+ android:id="@+id/channel_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textDirection="locale"
+ style="@style/TextAppearance.NotificationImportanceChannel"/>
+ <TextView
+ android:id="@+id/group_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textDirection="locale"
+ android:ellipsize="end"
+ style="@style/TextAppearance.NotificationImportanceChannelGroup"/>
+ <TextView
+ android:id="@+id/pkg_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/TextAppearance.NotificationImportanceApp"
+ android:ellipsize="end"
+ android:textDirection="locale"
+ android:maxLines="1"/>
+ <TextView
+ android:id="@+id/delegate_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ style="@style/TextAppearance.NotificationImportanceHeader"
+ android:layout_marginStart="2dp"
+ android:layout_marginEnd="2dp"
+ android:ellipsize="end"
+ android:textDirection="locale"
+ android:text="@string/notification_delegate_header"
+ android:maxLines="1" />
+
+ </LinearLayout>
+
+ <!-- end aligned fields -->
+ <!-- Optional link to app. Only appears if the channel is not disabled and the app
+asked for it -->
+ <ImageButton
+ android:id="@+id/app_settings"
+ android:layout_width="@dimen/notification_importance_toggle_size"
+ android:layout_height="@dimen/notification_importance_toggle_size"
+ android:layout_centerVertical="true"
+ android:visibility="gone"
+ android:background="@drawable/ripple_drawable"
+ android:contentDescription="@string/notification_app_settings"
+ android:src="@drawable/ic_info"
+ android:layout_toStartOf="@id/info"
+ android:tint="@androidprv:color/materialColorPrimary"/>
+ <ImageButton
+ android:id="@+id/info"
+ android:layout_width="@dimen/notification_importance_toggle_size"
+ android:layout_height="@dimen/notification_importance_toggle_size"
+ android:layout_centerVertical="true"
+ android:contentDescription="@string/notification_more_settings"
+ android:background="@drawable/ripple_drawable_20dp"
+ android:src="@drawable/ic_settings"
+ android:tint="@androidprv:color/materialColorPrimary"
+ android:layout_alignParentEnd="true" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/inline_controls"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingEnd="@dimen/notification_shade_content_margin_horizontal"
+ android:layout_marginTop="@dimen/notification_guts_option_vertical_padding"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:orientation="vertical">
+
+ <!-- Non configurable app/channel text. appears instead of @+id/interruptiveness_settings-->
+ <TextView
+ android:id="@+id/non_configurable_text"
+ android:text="@string/notification_unblockable_desc"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@*android:style/TextAppearance.DeviceDefault.Notification" />
+
+ <!-- Non configurable app/channel text. appears instead of @+id/interruptiveness_settings-->
+ <TextView
+ android:id="@+id/non_configurable_call_text"
+ android:text="@string/notification_unblockable_call_desc"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@*android:style/TextAppearance.DeviceDefault.Notification" />
+
+ <!-- Non configurable multichannel text. appears instead of @+id/interruptiveness_settings-->
+ <TextView
+ android:id="@+id/non_configurable_multichannel_text"
+ android:text="@string/notification_multichannel_desc"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@*android:style/TextAppearance.DeviceDefault.Notification" />
+
+ <LinearLayout
+ android:id="@+id/interruptiveness_settings"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:orientation="vertical">
+ <com.android.systemui.statusbar.notification.row.ButtonLinearLayout
+ android:id="@+id/automatic"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/notification_importance_button_separation"
+ android:padding="@dimen/notification_importance_button_padding"
+ android:clickable="true"
+ android:focusable="true"
+ android:background="@drawable/notification_guts_priority_button_bg"
+ android:orientation="vertical"
+ android:visibility="gone">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:gravity="center"
+ >
+ <ImageView
+ android:id="@+id/automatic_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_notifications_automatic"
+ android:background="@android:color/transparent"
+ android:tint="@color/notification_guts_priority_contents"
+ android:clickable="false"
+ android:focusable="false"/>
+ <TextView
+ android:id="@+id/automatic_label"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/notification_importance_drawable_padding"
+ android:layout_weight="1"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:clickable="false"
+ android:focusable="false"
+ android:textAppearance="@style/TextAppearance.NotificationImportanceButton"
+ android:text="@string/notification_automatic_title"/>
+ </LinearLayout>
+ <TextView
+ android:id="@+id/automatic_summary"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/notification_importance_button_description_top_margin"
+ android:visibility="gone"
+ android:text="@string/notification_channel_summary_automatic"
+ android:clickable="false"
+ android:focusable="false"
+ android:ellipsize="end"
+ android:maxLines="2"
+ android:textAppearance="@style/TextAppearance.NotificationImportanceDetail"/>
+ </com.android.systemui.statusbar.notification.row.ButtonLinearLayout>
+
+ <com.android.systemui.statusbar.notification.row.ButtonLinearLayout
+ android:id="@+id/alert"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="@dimen/notification_importance_button_padding"
+ android:clickable="true"
+ android:focusable="true"
+ android:background="@drawable/notification_guts_priority_button_bg"
+ android:orientation="vertical">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:gravity="center"
+ >
+ <ImageView
+ android:id="@+id/alert_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_notifications_alert"
+ android:background="@android:color/transparent"
+ android:tint="@color/notification_guts_priority_contents"
+ android:clickable="false"
+ android:focusable="false"/>
+ <TextView
+ android:id="@+id/alert_label"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/notification_importance_drawable_padding"
+ android:layout_weight="1"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:clickable="false"
+ android:focusable="false"
+ android:textAppearance="@style/TextAppearance.NotificationImportanceButton"
+ android:text="@string/notification_alert_title"/>
+ </LinearLayout>
+ <TextView
+ android:id="@+id/alert_summary"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/notification_importance_button_description_top_margin"
+ android:visibility="gone"
+ android:text="@string/notification_channel_summary_default"
+ android:clickable="false"
+ android:focusable="false"
+ android:ellipsize="end"
+ android:maxLines="2"
+ android:textAppearance="@style/TextAppearance.NotificationImportanceDetail"/>
+ </com.android.systemui.statusbar.notification.row.ButtonLinearLayout>
+
+ <com.android.systemui.statusbar.notification.row.ButtonLinearLayout
+ android:id="@+id/silence"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/notification_importance_button_separation"
+ android:padding="@dimen/notification_importance_button_padding"
+ android:clickable="true"
+ android:focusable="true"
+ android:background="@drawable/notification_guts_priority_button_bg"
+ android:orientation="vertical">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:gravity="center"
+ >
+ <ImageView
+ android:id="@+id/silence_icon"
+ android:src="@drawable/ic_notifications_silence"
+ android:background="@android:color/transparent"
+ android:tint="@color/notification_guts_priority_contents"
+ android:layout_gravity="center"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:clickable="false"
+ android:focusable="false"/>
+ <TextView
+ android:id="@+id/silence_label"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:clickable="false"
+ android:focusable="false"
+ android:layout_toEndOf="@id/silence_icon"
+ android:layout_marginStart="@dimen/notification_importance_drawable_padding"
+ android:textAppearance="@style/TextAppearance.NotificationImportanceButton"
+ android:text="@string/notification_silence_title"/>
+ </LinearLayout>
+ <TextView
+ android:id="@+id/silence_summary"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/notification_importance_button_description_top_margin"
+ android:visibility="gone"
+ android:text="@string/notification_channel_summary_low"
+ android:clickable="false"
+ android:focusable="false"
+ android:ellipsize="end"
+ android:maxLines="2"
+ android:textAppearance="@style/TextAppearance.NotificationImportanceDetail"/>
+ </com.android.systemui.statusbar.notification.row.ButtonLinearLayout>
+
+ </LinearLayout>
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="60dp"
+ android:gravity="center_vertical"
+ android:paddingStart="4dp"
+ android:paddingEnd="4dp"
+ >
+ <TextView
+ android:id="@+id/promoted_demote"
+ android:text="@string/notification_inline_disable_promotion"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:gravity="start|center_vertical"
+ android:minWidth="@dimen/notification_importance_toggle_size"
+ android:minHeight="@dimen/notification_importance_toggle_size"
+ android:maxWidth="200dp"
+ style="@style/TextAppearance.NotificationInfo.Button"/>
+ <TextView
+ android:id="@+id/promoted_dismiss"
+ android:text="@string/notification_inline_dismiss"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:gravity="end|center_vertical"
+ android:minWidth="@dimen/notification_importance_toggle_size"
+ android:minHeight="@dimen/notification_importance_toggle_size"
+ android:maxWidth="125dp"
+ style="@style/TextAppearance.NotificationInfo.Button"/>
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:id="@+id/bottom_buttons"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="60dp"
+ android:gravity="center_vertical"
+ android:paddingStart="4dp"
+ android:paddingEnd="4dp"
+ >
+ <TextView
+ android:id="@+id/turn_off_notifications"
+ android:text="@string/inline_turn_off_notifications"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:gravity="start|center_vertical"
+ android:minWidth="@dimen/notification_importance_toggle_size"
+ android:minHeight="@dimen/notification_importance_toggle_size"
+ android:maxWidth="200dp"
+ style="@style/TextAppearance.NotificationInfo.Button"/>
+ <TextView
+ android:id="@+id/done"
+ android:text="@string/inline_ok_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:gravity="end|center_vertical"
+ android:minWidth="@dimen/notification_importance_toggle_size"
+ android:minHeight="@dimen/notification_importance_toggle_size"
+ android:maxWidth="125dp"
+ style="@style/TextAppearance.NotificationInfo.Button"/>
+ </RelativeLayout>
+ </LinearLayout>
+</com.android.systemui.statusbar.notification.row.PromotedNotificationInfo>
diff --git a/packages/SystemUI/res/layout/volume_dialog_slider.xml b/packages/SystemUI/res/layout/volume_dialog_slider.xml
index c5f468e731f5..2628f4991b49 100644
--- a/packages/SystemUI/res/layout/volume_dialog_slider.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_slider.xml
@@ -18,14 +18,10 @@
android:layout_height="match_parent"
android:maxHeight="@dimen/volume_dialog_slider_height">
- <com.google.android.material.slider.Slider
+ <androidx.compose.ui.platform.ComposeView
android:id="@+id/volume_dialog_slider"
- style="@style/SystemUI.Material3.Slider.Volume"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
- android:layout_marginTop="-20dp"
- android:layout_marginBottom="-20dp"
- android:orientation="vertical"
- android:theme="@style/Theme.Material3.DayNight" />
+ android:orientation="vertical" />
</FrameLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/values-xlarge-land/config.xml b/packages/SystemUI/res/values-xlarge-land/config.xml
index 5e4304e1c13a..6d8b64ade259 100644
--- a/packages/SystemUI/res/values-xlarge-land/config.xml
+++ b/packages/SystemUI/res/values-xlarge-land/config.xml
@@ -16,4 +16,5 @@
<resources>
<item name="shortcut_helper_screen_width_fraction" format="float" type="dimen">0.8</item>
+ <bool name="center_align_magic_portrait_shape">true</bool>
</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 940e87d3d163..68e33f27aefa 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -1104,7 +1104,10 @@
-->
<bool name="config_userSwitchingMustGoThroughLoginScreen">false</bool>
-
<!-- The dream component used when the device is low light environment. -->
<string translatable="false" name="config_lowLightDreamComponent"/>
+
+ <!--Whether we should position magic portrait shape effects in the center of lockscreen
+ it's false by default, and only be true in tablet landscape -->
+ <bool name="center_align_magic_portrait_shape">false</bool>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 1e52e9f135cb..64367ef79856 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2083,6 +2083,12 @@
<!-- [CHAR LIMIT=80] Text shown in feedback button in notification guts for a bundled notification -->
<string name="notification_guts_bundle_feedback" translatable="false">Provide Bundle Feedback</string>
+ <!-- [CHAR LIMIT=30] Text shown in button used to dismiss this single notification. -->
+ <string name="notification_inline_dismiss">Dismiss</string>
+
+ <!-- [CHAR LIMIT=30] Text shown in button used to prevent app from showing Live Updates, for this notification and all future ones -->
+ <string name="notification_inline_disable_promotion">Don\'t show again</string>
+
<!-- Notification: Control panel: Label that displays when the app's notifications cannot be blocked. -->
<string name="notification_unblockable_desc">These notifications can\'t be modified.</string>
@@ -2310,9 +2316,6 @@
<string name="group_system_lock_screen">Lock screen</string>
<!-- User visible title for the keyboard shortcut that pulls up Notes app for quick memo. [CHAR LIMIT=70] -->
<string name="group_system_quick_memo">Take a note</string>
- <!-- TODO(b/383734125): make it translatable once string is finalized by UXW.-->
- <!-- User visible title for the keyboard shortcut that toggles Voice Access. [CHAR LIMIT=70] -->
- <string name="group_system_toggle_voice_access" translatable="false">Toggle Voice Access</string>
<!-- User visible title for the multitasking keyboard shortcuts list. [CHAR LIMIT=70] -->
<string name="keyboard_shortcut_group_system_multitasking">Multitasking</string>
@@ -2371,6 +2374,17 @@
<!-- User visible title for the keyboard shortcut that takes the user to the maps app. [CHAR LIMIT=70] -->
<string name="keyboard_shortcut_group_applications_maps">Maps</string>
+ <!-- User visible title for the keyboard shortcut that toggles bounce keys. [CHAR LIMIT=70]-->
+ <string name="group_accessibility_toggle_bounce_keys">Toggle bounce keys</string>
+ <!-- User visible title for the keyboard shortcut that toggles mouse keys. [CHAR LIMIT=70]-->
+ <string name="group_accessibility_toggle_mouse_keys">Toggle mouse keys</string>
+ <!-- User visible title for the keyboard shortcut that toggles sticky keys. [CHAR LIMIT=70]-->
+ <string name="group_accessibility_toggle_sticky_keys">Toggle sticky keys</string>
+ <!-- User visible title for the keyboard shortcut that toggles slow keys. [CHAR LIMIT=70]-->
+ <string name="group_accessibility_toggle_slow_keys">Toggle slow keys</string>
+ <!-- User visible title for the keyboard shortcut that toggles Voice Access. [CHAR LIMIT=70] -->
+ <string name="group_accessibility_toggle_voice_access">Toggle Voice Access</string>
+
<!-- SysUI Tuner: Label for screen about do not disturb settings [CHAR LIMIT=60] -->
<string name="volume_and_do_not_disturb">Do Not Disturb</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index b0d9bed05e27..c95c6dd89353 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -565,16 +565,6 @@
<item name="android:windowNoTitle">true</item>
</style>
- <style name="SystemUI.Material3.Slider.Volume">
- <item name="trackHeight">40dp</item>
- <item name="thumbHeight">52dp</item>
- <item name="trackCornerSize">12dp</item>
- <item name="trackInsideCornerSize">2dp</item>
- <item name="trackStopIndicatorSize">6dp</item>
- <item name="trackIconSize">20dp</item>
- <item name="labelBehavior">gone</item>
- </style>
-
<style name="SystemUI.Material3.Slider" parent="@style/Widget.Material3.Slider">
<item name="labelStyle">@style/Widget.Material3.Slider.Label</item>
<item name="thumbColor">@color/thumb_color</item>
diff --git a/packages/SystemUI/res/xml/gradient_color_wallpaper.xml b/packages/SystemUI/res/xml/gradient_color_wallpaper.xml
new file mode 100644
index 000000000000..f453a4450750
--- /dev/null
+++ b/packages/SystemUI/res/xml/gradient_color_wallpaper.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<wallpaper
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:showMetadataInPreview="false"
+ android:supportsMultipleDisplays="true" /> \ No newline at end of file
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 41ad4373455e..818e39800b0c 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
@@ -18,9 +18,10 @@ package com.android.systemui.shared.recents.utilities;
import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
-import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN;
import android.annotation.TargetApi;
+import android.app.StatusBarManager.NavigationHint;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Color;
@@ -103,10 +104,15 @@ public class Utilities {
}
/**
- * @return updated set of flags from InputMethodService based off {@param oldHints}
- * Leaves original hints unmodified
+ * Gets the updated navigation icon hints, based on the current ones and the given IME state.
+ *
+ * @param oldHints current navigation icon hints.
+ * @param backDisposition the IME back disposition mode.
+ * @param imeShown whether the IME is currently visible.
+ * @param showImeSwitcher whether the IME Switcher button should be shown.
*/
- public static int calculateBackDispositionHints(int oldHints,
+ @NavigationHint
+ public static int calculateNavigationIconHints(@NavigationHint int oldHints,
@BackDispositionMode int backDisposition, boolean imeShown, boolean showImeSwitcher) {
int hints = oldHints;
switch (backDisposition) {
@@ -129,9 +135,9 @@ public class Utilities {
hints &= ~NAVIGATION_HINT_IME_SHOWN;
}
if (showImeSwitcher) {
- hints |= NAVIGATION_HINT_IME_SWITCHER_SHOWN;
+ hints |= NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN;
} else {
- hints &= ~NAVIGATION_HINT_IME_SWITCHER_SHOWN;
+ hints &= ~NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN;
}
return hints;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextView.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextView.kt
index bd20777c7102..e928525afc14 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextView.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextView.kt
@@ -16,6 +16,7 @@
package com.android.systemui.shared.shadow
import android.content.Context
+import android.content.res.TypedArray
import android.graphics.Canvas
import android.graphics.drawable.Drawable
import android.util.AttributeSet
@@ -31,19 +32,23 @@ constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
- defStyleRes: Int = 0
+ defStyleRes: Int = 0,
) : TextView(context, attrs, defStyleAttr, defStyleRes) {
- private val mKeyShadowInfo: ShadowInfo
- private val mAmbientShadowInfo: ShadowInfo
+ private lateinit var mKeyShadowInfo: ShadowInfo
+ private lateinit var mAmbientShadowInfo: ShadowInfo
init {
- val attributes =
+ updateShadowDrawables(
context.obtainStyledAttributes(
attrs,
R.styleable.DoubleShadowTextView,
defStyleAttr,
- defStyleRes
+ defStyleRes,
)
+ )
+ }
+
+ private fun updateShadowDrawables(attributes: TypedArray) {
val drawableSize: Int
val drawableInsetSize: Int
try {
@@ -70,17 +75,17 @@ constructor(
ambientShadowBlur,
ambientShadowOffsetX,
ambientShadowOffsetY,
- ambientShadowAlpha
+ ambientShadowAlpha,
)
drawableSize =
attributes.getDimensionPixelSize(
R.styleable.DoubleShadowTextView_drawableIconSize,
- 0
+ 0,
)
drawableInsetSize =
attributes.getDimensionPixelSize(
R.styleable.DoubleShadowTextView_drawableIconInsetSize,
- 0
+ 0,
)
} finally {
attributes.recycle()
@@ -95,12 +100,19 @@ constructor(
mAmbientShadowInfo,
drawable,
drawableSize,
- drawableInsetSize
+ drawableInsetSize,
)
}
setCompoundDrawablesRelative(drawables[0], drawables[1], drawables[2], drawables[3])
}
+ override fun setTextAppearance(resId: Int) {
+ super.setTextAppearance(resId)
+ updateShadowDrawables(
+ context.obtainStyledAttributes(resId, R.styleable.DoubleShadowTextView)
+ )
+ }
+
public override fun onDraw(canvas: Canvas) {
applyShadows(mKeyShadowInfo, mAmbientShadowInfo, this, canvas) { super.onDraw(canvas) }
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 6f13d637d5c5..f87d9b05d795 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -98,12 +98,12 @@ public class QuickStepContract {
public static final long SYSUI_STATE_ONE_HANDED_ACTIVE = 1L << 16;
// Allow system gesture no matter the system bar(s) is visible or not
public static final long SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY = 1L << 17;
- // The IME is showing
+ // The IME is visible.
public static final long SYSUI_STATE_IME_SHOWING = 1L << 18;
// The window magnification is overlapped with system gesture insets at the bottom.
public static final long SYSUI_STATE_MAGNIFICATION_OVERLAP = 1L << 19;
- // ImeSwitcher is showing
- public static final long SYSUI_STATE_IME_SWITCHER_SHOWING = 1L << 20;
+ // The IME Switcher button is visible.
+ public static final long SYSUI_STATE_IME_SWITCHER_BUTTON_SHOWING = 1L << 20;
// Device dozing/AOD state
public static final long SYSUI_STATE_DEVICE_DOZING = 1L << 21;
// The home feature is disabled (either by SUW/SysUI/device policy)
@@ -170,7 +170,7 @@ public class QuickStepContract {
SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY,
SYSUI_STATE_IME_SHOWING,
SYSUI_STATE_MAGNIFICATION_OVERLAP,
- SYSUI_STATE_IME_SWITCHER_SHOWING,
+ SYSUI_STATE_IME_SWITCHER_BUTTON_SHOWING,
SYSUI_STATE_DEVICE_DOZING,
SYSUI_STATE_BACK_DISABLED,
SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED,
@@ -250,8 +250,8 @@ public class QuickStepContract {
if ((flags & SYSUI_STATE_MAGNIFICATION_OVERLAP) != 0) {
str.add("magnification_overlap");
}
- if ((flags & SYSUI_STATE_IME_SWITCHER_SHOWING) != 0) {
- str.add("ime_switcher_showing");
+ if ((flags & SYSUI_STATE_IME_SWITCHER_BUTTON_SHOWING) != 0) {
+ str.add("ime_switcher_button_visible");
}
if ((flags & SYSUI_STATE_DEVICE_DOZING) != 0) {
str.add("device_dozing");
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt
index 6a42cdc876ca..8e4c9349c604 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt
@@ -39,10 +39,14 @@ import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFO
import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT
import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT
import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS
import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS
import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS
import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS
-import com.android.hardware.input.Flags.enableVoiceAccessKeyGestures
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.Accessibility
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.AppCategories
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.MultiTasking
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.System
@@ -65,7 +69,6 @@ class InputGestureMaps @Inject constructor(private val context: Context) {
KEY_GESTURE_TYPE_LAUNCH_ASSISTANT to System,
KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT to System,
KEY_GESTURE_TYPE_ALL_APPS to System,
- KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS to System,
// Multitasking Category
KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER to MultiTasking,
@@ -82,6 +85,13 @@ class InputGestureMaps @Inject constructor(private val context: Context) {
// App Category
KEY_GESTURE_TYPE_LAUNCH_APPLICATION to AppCategories,
+
+ // Accessibility Category
+ KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS to Accessibility,
+ KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS to Accessibility,
+ KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS to Accessibility,
+ KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS to Accessibility,
+ KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS to Accessibility,
)
val gestureToInternalKeyboardShortcutGroupLabelResIdMap =
@@ -103,7 +113,6 @@ class InputGestureMaps @Inject constructor(private val context: Context) {
KEY_GESTURE_TYPE_LAUNCH_ASSISTANT to R.string.shortcut_helper_category_system_apps,
KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT to
R.string.shortcut_helper_category_system_apps,
- KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS to R.string.shortcut_helper_category_system_apps,
// Multitasking Category
KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT to
@@ -128,6 +137,13 @@ class InputGestureMaps @Inject constructor(private val context: Context) {
// App Category
KEY_GESTURE_TYPE_LAUNCH_APPLICATION to R.string.keyboard_shortcut_group_applications,
+
+ // Accessibility Category
+ KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS to R.string.shortcutHelper_category_accessibility,
+ KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS to R.string.shortcutHelper_category_accessibility,
+ KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS to R.string.shortcutHelper_category_accessibility,
+ KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS to R.string.shortcutHelper_category_accessibility,
+ KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS to R.string.shortcutHelper_category_accessibility,
)
/**
@@ -152,7 +168,6 @@ class InputGestureMaps @Inject constructor(private val context: Context) {
KEY_GESTURE_TYPE_LAUNCH_ASSISTANT to R.string.group_system_access_google_assistant,
KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT to
R.string.group_system_access_google_assistant,
- KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS to R.string.group_system_toggle_voice_access,
// Multitasking Category
KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER to R.string.group_system_cycle_forward,
@@ -169,6 +184,14 @@ class InputGestureMaps @Inject constructor(private val context: Context) {
R.string.system_desktop_mode_toggle_maximize_window,
KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY to
R.string.system_multitasking_move_to_next_display,
+
+ // Accessibility Category
+ KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS to R.string.group_accessibility_toggle_bounce_keys,
+ KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS to R.string.group_accessibility_toggle_mouse_keys,
+ KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS to R.string.group_accessibility_toggle_sticky_keys,
+ KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS to R.string.group_accessibility_toggle_slow_keys,
+ KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS to
+ R.string.group_accessibility_toggle_voice_access,
)
val shortcutLabelToKeyGestureTypeMap: Map<String, Int>
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/AccessibilityShortcutsSource.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/AccessibilityShortcutsSource.kt
index d92c45591522..0c98f81e7cef 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/AccessibilityShortcutsSource.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/AccessibilityShortcutsSource.kt
@@ -17,9 +17,20 @@
package com.android.systemui.keyboard.shortcut.data.source
import android.content.res.Resources
+import android.hardware.input.InputSettings
+import android.view.KeyEvent.KEYCODE_3
+import android.view.KeyEvent.KEYCODE_4
+import android.view.KeyEvent.KEYCODE_5
+import android.view.KeyEvent.KEYCODE_6
+import android.view.KeyEvent.KEYCODE_V
+import android.view.KeyEvent.META_ALT_ON
+import android.view.KeyEvent.META_META_ON
import android.view.KeyboardShortcutGroup
import android.view.KeyboardShortcutInfo
+import com.android.hardware.input.Flags.enableVoiceAccessKeyGestures
+import com.android.hardware.input.Flags.keyboardA11yShortcutControl
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyboard.shortcut.data.model.shortcutInfo
import com.android.systemui.res.R
import javax.inject.Inject
@@ -33,5 +44,68 @@ class AccessibilityShortcutsSource @Inject constructor(@Main private val resourc
)
)
- private fun accessibilityShortcuts() = listOf<KeyboardShortcutInfo>()
+ private fun accessibilityShortcuts(): List<KeyboardShortcutInfo> {
+ val shortcuts = mutableListOf<KeyboardShortcutInfo>()
+
+ if (keyboardA11yShortcutControl()) {
+ if (InputSettings.isAccessibilityBounceKeysFeatureEnabled()) {
+ shortcuts.add(
+ // Toggle bounce keys:
+ // - Meta + Alt + 3
+ shortcutInfo(
+ resources.getString(R.string.group_accessibility_toggle_bounce_keys)
+ ) {
+ command(META_META_ON or META_ALT_ON, KEYCODE_3)
+ }
+ )
+ }
+ if (InputSettings.isAccessibilityMouseKeysFeatureFlagEnabled()) {
+ shortcuts.add(
+ // Toggle mouse keys:
+ // - Meta + Alt + 4
+ shortcutInfo(
+ resources.getString(R.string.group_accessibility_toggle_mouse_keys)
+ ) {
+ command(META_META_ON or META_ALT_ON, KEYCODE_4)
+ }
+ )
+ }
+ if (InputSettings.isAccessibilityStickyKeysFeatureEnabled()) {
+ shortcuts.add(
+ // Toggle sticky keys:
+ // - Meta + Alt + 5
+ shortcutInfo(
+ resources.getString(R.string.group_accessibility_toggle_sticky_keys)
+ ) {
+ command(META_META_ON or META_ALT_ON, KEYCODE_5)
+ }
+ )
+ }
+ if (InputSettings.isAccessibilitySlowKeysFeatureFlagEnabled()) {
+ shortcuts.add(
+ // Toggle slow keys:
+ // - Meta + Alt + 6
+ shortcutInfo(
+ resources.getString(R.string.group_accessibility_toggle_slow_keys)
+ ) {
+ command(META_META_ON or META_ALT_ON, KEYCODE_6)
+ }
+ )
+ }
+ }
+
+ if (enableVoiceAccessKeyGestures()) {
+ shortcuts.add(
+ // Toggle voice access:
+ // - Meta + Alt + V
+ shortcutInfo(
+ resources.getString(R.string.group_accessibility_toggle_voice_access)
+ ) {
+ command(META_META_ON or META_ALT_ON, KEYCODE_V)
+ }
+ )
+ }
+
+ return shortcuts
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt
index c3c9df97a682..5060abdda247 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt
@@ -32,14 +32,12 @@ import android.view.KeyEvent.KEYCODE_RECENT_APPS
import android.view.KeyEvent.KEYCODE_S
import android.view.KeyEvent.KEYCODE_SLASH
import android.view.KeyEvent.KEYCODE_TAB
-import android.view.KeyEvent.KEYCODE_V
import android.view.KeyEvent.META_ALT_ON
import android.view.KeyEvent.META_CTRL_ON
import android.view.KeyEvent.META_META_ON
import android.view.KeyEvent.META_SHIFT_ON
import android.view.KeyboardShortcutGroup
import android.view.KeyboardShortcutInfo
-import com.android.hardware.input.Flags.enableVoiceAccessKeyGestures
import com.android.systemui.Flags.shortcutHelperKeyGlyph
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyboard.shortcut.data.model.shortcutInfo
@@ -120,8 +118,8 @@ constructor(@Main private val resources: Resources, private val inputManager: In
return shortcuts
}
- private fun systemControlsShortcuts(): List<KeyboardShortcutInfo> {
- val shortcuts = mutableListOf(
+ private fun systemControlsShortcuts() =
+ listOf(
// Access list of all apps and search (i.e. Search/Launcher):
// - Meta
shortcutInfo(resources.getString(R.string.group_system_access_all_apps_search)) {
@@ -178,19 +176,6 @@ constructor(@Main private val resources: Resources, private val inputManager: In
},
)
- if (enableVoiceAccessKeyGestures()) {
- shortcuts.add(
- // Toggle voice access:
- // - Meta + Alt + V
- shortcutInfo(resources.getString(R.string.group_system_toggle_voice_access)) {
- command(META_META_ON or META_ALT_ON, KEYCODE_V)
- }
- )
- }
-
- return shortcuts
- }
-
private fun systemAppsShortcuts() =
listOf(
// Pull up Notes app for quick memo:
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt
index f1945e657d52..bd3d46d09f5e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt
@@ -108,22 +108,33 @@ constructor(
private fun setDialogProperties(dialog: SystemUIDialog, uiState: ShortcutCustomizationUiState) {
dialog.setOnDismissListener { viewModel.onDialogDismissed() }
- dialog.setTitle(
- resources.getString(
- when (uiState) {
- is AddShortcutDialog ->
- R.string.shortcut_customize_mode_add_shortcut_description
- is DeleteShortcutDialog ->
- R.string.shortcut_customize_mode_remove_shortcut_description
- else -> R.string.shortcut_customize_mode_reset_shortcut_description
- }
- )
- )
+ dialog.setTitle("${getDialogTitle(uiState)}. ${getDialogDescription(uiState)}")
// By default, apps cannot intercept action key. The system always handles it. This
// flag is needed to enable customisation dialog window to intercept action key
dialog.window?.addPrivateFlags(PRIVATE_FLAG_ALLOW_ACTION_KEY_EVENTS)
}
+ private fun getDialogTitle(uiState: ShortcutCustomizationUiState): String {
+ return when (uiState) {
+ is AddShortcutDialog -> uiState.shortcutLabel
+ is DeleteShortcutDialog ->
+ resources.getString(R.string.shortcut_customize_mode_remove_shortcut_dialog_title)
+ else ->
+ resources.getString(R.string.shortcut_customize_mode_reset_shortcut_dialog_title)
+ }
+ }
+
+ private fun getDialogDescription(uiState: ShortcutCustomizationUiState): String {
+ return resources.getString(
+ when (uiState) {
+ is AddShortcutDialog -> R.string.shortcut_customize_mode_add_shortcut_description
+ is DeleteShortcutDialog ->
+ R.string.shortcut_customize_mode_remove_shortcut_description
+ else -> R.string.shortcut_customize_mode_reset_shortcut_description
+ }
+ )
+ }
+
@AssistedFactory
interface Factory {
fun create(): ShortcutCustomizationDialogStarter
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index 5e0768a2fd24..f37e7685f21c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -27,6 +27,7 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
+import com.android.systemui.keyguard.domain.interactor.WallpaperFocalAreaInteractor
import com.android.systemui.keyguard.ui.binder.KeyguardBlueprintViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder
import com.android.systemui.keyguard.ui.binder.LightRevealScrimViewBinder
@@ -79,6 +80,7 @@ constructor(
private val keyguardClockViewModel: KeyguardClockViewModel,
private val smartspaceViewModel: KeyguardSmartspaceViewModel,
private val clockInteractor: KeyguardClockInteractor,
+ private val wallpaperFocalAreaInteractor: WallpaperFocalAreaInteractor,
private val keyguardViewMediator: KeyguardViewMediator,
private val deviceEntryUnlockTrackerViewBinder: Optional<DeviceEntryUnlockTrackerViewBinder>,
private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
@@ -139,6 +141,7 @@ constructor(
screenOffAnimationController,
shadeInteractor,
clockInteractor,
+ wallpaperFocalAreaInteractor,
keyguardClockViewModel,
interactionJankMonitor,
deviceEntryHapticsInteractor,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index a39982dd31e7..11477fb6cad1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.data.repository
import android.graphics.Point
+import android.graphics.RectF
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.internal.widget.LockPatternUtils
import com.android.keyguard.KeyguardUpdateMonitor
@@ -258,6 +259,8 @@ interface KeyguardRepository {
val notificationStackAbsoluteBottom: StateFlow<Float>
+ val wallpaperFocalAreaBounds: StateFlow<RectF>
+
/**
* Returns `true` if the keyguard is showing; `false` otherwise.
*
@@ -329,6 +332,8 @@ interface KeyguardRepository {
* this value
*/
fun setNotificationStackAbsoluteBottom(bottom: Float)
+
+ fun setWallpaperFocalAreaBounds(bounds: RectF)
}
/** Encapsulates application state for the keyguard. */
@@ -380,7 +385,6 @@ constructor(
override val onCameraLaunchDetected = MutableStateFlow(CameraLaunchSourceModel())
override val panelAlpha: MutableStateFlow<Float> = MutableStateFlow(1f)
-
override val topClippingBounds = MutableStateFlow<Int?>(null)
override val isKeyguardShowing: MutableStateFlow<Boolean> =
@@ -622,6 +626,10 @@ constructor(
private val _notificationStackAbsoluteBottom = MutableStateFlow(0F)
override val notificationStackAbsoluteBottom = _notificationStackAbsoluteBottom.asStateFlow()
+ private val _wallpaperFocalAreaBounds = MutableStateFlow(RectF(0F, 0F, 0F, 0F))
+ override val wallpaperFocalAreaBounds: StateFlow<RectF> =
+ _wallpaperFocalAreaBounds.asStateFlow()
+
init {
val callback =
object : KeyguardStateController.Callback {
@@ -700,6 +708,10 @@ constructor(
_notificationStackAbsoluteBottom.value = bottom
}
+ override fun setWallpaperFocalAreaBounds(bounds: RectF) {
+ _wallpaperFocalAreaBounds.value = bounds
+ }
+
private fun dozeMachineStateToModel(state: DozeMachine.State): DozeStateModel {
return when (state) {
DozeMachine.State.UNINITIALIZED -> DozeStateModel.UNINITIALIZED
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WallpaperFocalAreaInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WallpaperFocalAreaInteractor.kt
new file mode 100644
index 000000000000..934afe248a36
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WallpaperFocalAreaInteractor.kt
@@ -0,0 +1,176 @@
+/*
+ * 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.keyguard.domain.interactor
+
+import android.content.Context
+import android.content.res.Resources
+import android.graphics.RectF
+import android.util.TypedValue
+import com.android.app.animation.MathUtils.max
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.data.repository.KeyguardClockRepository
+import com.android.systemui.keyguard.data.repository.KeyguardRepository
+import com.android.systemui.res.R
+import com.android.systemui.shade.data.repository.ShadeRepository
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
+import com.android.systemui.wallpapers.data.repository.WallpaperRepository
+import javax.inject.Inject
+import kotlin.math.min
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.stateIn
+
+@SysUISingleton
+class WallpaperFocalAreaInteractor
+@Inject
+constructor(
+ @Application private val applicationScope: CoroutineScope,
+ context: Context,
+ private val keyguardRepository: KeyguardRepository,
+ shadeRepository: ShadeRepository,
+ activeNotificationsInteractor: ActiveNotificationsInteractor,
+ keyguardClockRepository: KeyguardClockRepository,
+ wallpaperRepository: WallpaperRepository,
+) {
+ // When there's notifications in splitshade, magic portrait shape effects should be left
+ // aligned in foldable
+ private val notificationInShadeWideLayout: Flow<Boolean> =
+ combine(
+ shadeRepository.isShadeLayoutWide,
+ activeNotificationsInteractor.areAnyNotificationsPresent,
+ ) { isShadeLayoutWide, areAnyNotificationsPresent: Boolean ->
+ when {
+ !isShadeLayoutWide -> false
+ !areAnyNotificationsPresent -> false
+ else -> true
+ }
+ }
+
+ val shouldSendFocalArea = wallpaperRepository.shouldSendFocalArea
+ val wallpaperFocalAreaBounds: StateFlow<RectF?> =
+ combine(
+ shadeRepository.isShadeLayoutWide,
+ notificationInShadeWideLayout,
+ keyguardRepository.notificationStackAbsoluteBottom,
+ keyguardRepository.shortcutAbsoluteTop,
+ keyguardClockRepository.notificationDefaultTop,
+ ) {
+ isShadeLayoutWide,
+ notificationInShadeWideLayout,
+ notificationStackAbsoluteBottom,
+ shortcutAbsoluteTop,
+ notificationDefaultTop ->
+ // Wallpaper will be zoomed in with config_wallpaperMaxScale in lockscreen
+ // so we need to give a bounds taking this scale in consideration
+ val wallpaperZoomedInScale = getSystemWallpaperMaximumScale(context)
+ val screenBounds =
+ RectF(
+ 0F,
+ 0F,
+ context.resources.displayMetrics.widthPixels.toFloat(),
+ context.resources.displayMetrics.heightPixels.toFloat(),
+ )
+ val scaledBounds =
+ RectF(
+ screenBounds.centerX() - screenBounds.width() / 2F / wallpaperZoomedInScale,
+ screenBounds.centerY() -
+ screenBounds.height() / 2F / wallpaperZoomedInScale,
+ screenBounds.centerX() + screenBounds.width() / 2F / wallpaperZoomedInScale,
+ screenBounds.centerY() + screenBounds.height() / 2F / wallpaperZoomedInScale,
+ )
+ val maxFocalAreaWidth =
+ TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP,
+ FOCAL_AREA_MAX_WIDTH_DP.toFloat(),
+ context.resources.displayMetrics,
+ )
+ val (left, right) =
+ // tablet landscape
+ if (context.resources.getBoolean(R.bool.center_align_magic_portrait_shape)) {
+ Pair(
+ scaledBounds.centerX() - maxFocalAreaWidth / 2F,
+ scaledBounds.centerX() + maxFocalAreaWidth / 2F,
+ )
+ // unfold foldable landscape
+ } else if (isShadeLayoutWide) {
+ if (notificationInShadeWideLayout) {
+ Pair(scaledBounds.left, scaledBounds.centerX())
+ } else {
+ Pair(scaledBounds.centerX(), scaledBounds.right)
+ }
+ // handheld / portrait
+ } else {
+ val focalAreaWidth = min(scaledBounds.width(), maxFocalAreaWidth)
+ Pair(
+ scaledBounds.centerX() - focalAreaWidth / 2F,
+ scaledBounds.centerX() + focalAreaWidth / 2F,
+ )
+ }
+ val scaledBottomMargin =
+ (context.resources.displayMetrics.heightPixels - shortcutAbsoluteTop) /
+ wallpaperZoomedInScale
+ val top =
+ // tablet landscape
+ if (context.resources.getBoolean(R.bool.center_align_magic_portrait_shape)) {
+ // no strict constraints for top, use bottom margin to make it symmetric
+ // vertically
+ scaledBounds.top + scaledBottomMargin
+ }
+ // unfold foldable landscape
+ else if (isShadeLayoutWide) {
+ // For all landscape, we should use bottom of smartspace to constrain
+ scaledBounds.top + notificationDefaultTop / wallpaperZoomedInScale
+ // handheld / portrait
+ } else {
+ scaledBounds.top +
+ max(notificationDefaultTop, notificationStackAbsoluteBottom) /
+ wallpaperZoomedInScale
+ }
+ val bottom = scaledBounds.bottom - scaledBottomMargin
+ RectF(left, top, right, bottom)
+ }
+ .stateIn(
+ applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = null,
+ )
+
+ fun setWallpaperFocalAreaBounds(bounds: RectF) {
+ keyguardRepository.setWallpaperFocalAreaBounds(bounds)
+ }
+
+ companion object {
+ fun getSystemWallpaperMaximumScale(context: Context): Float {
+ return context.resources.getFloat(
+ Resources.getSystem()
+ .getIdentifier(
+ /* name= */ "config_wallpaperMaxScale",
+ /* defType= */ "dimen",
+ /* defPackage= */ "android",
+ )
+ )
+ }
+
+ // A max width for magic portrait shape effects bounds, to avoid it going too large
+ // in large screen portrait mode
+ const val FOCAL_AREA_MAX_WIDTH_DP = 500
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
index 92b49ed6156c..21c9b0b82b2d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
@@ -23,6 +23,7 @@ import android.widget.TextView
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.systemui.Flags
import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
@@ -73,7 +74,6 @@ object KeyguardIndicationAreaBinder {
disposables +=
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
-
launch("$TAG#viewModel.indicationAreaTranslationX") {
viewModel.indicationAreaTranslationX.collect { translationX ->
view.translationX = translationX
@@ -119,6 +119,9 @@ object KeyguardIndicationAreaBinder {
launch("$TAG#viewModel.configurationChange") {
viewModel.configurationChange.collect {
configurationBasedDimensions.value = loadFromResources(view)
+ if (Flags.indicationTextA11yFix()) {
+ indicationController.onConfigurationChanged()
+ }
}
}
@@ -140,7 +143,7 @@ object KeyguardIndicationAreaBinder {
view.resources.getDimensionPixelOffset(R.dimen.keyguard_indication_area_padding),
indicationTextSizePx =
view.resources.getDimensionPixelSize(
- com.android.internal.R.dimen.text_size_small_material,
+ com.android.internal.R.dimen.text_size_small_material
),
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index 6d270b219c81..d8bd4452f2a6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -54,6 +54,7 @@ import com.android.systemui.customization.R as customR
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
import com.android.systemui.keyguard.KeyguardViewMediator
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
+import com.android.systemui.keyguard.domain.interactor.WallpaperFocalAreaInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
@@ -87,6 +88,7 @@ import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
@@ -105,6 +107,7 @@ object KeyguardRootViewBinder {
screenOffAnimationController: ScreenOffAnimationController,
shadeInteractor: ShadeInteractor,
clockInteractor: KeyguardClockInteractor,
+ wallpaperFocalAreaInteractor: WallpaperFocalAreaInteractor,
clockViewModel: KeyguardClockViewModel,
interactionJankMonitor: InteractionJankMonitor?,
deviceEntryHapticsInteractor: DeviceEntryHapticsInteractor?,
@@ -309,12 +312,15 @@ object KeyguardRootViewBinder {
.setTag(clockId)
jankMonitor.begin(builder)
}
+
TransitionState.CANCELED ->
jankMonitor.cancel(CUJ_SCREEN_OFF_SHOW_AOD)
+
TransitionState.FINISHED -> {
keyguardViewMediator?.maybeHandlePendingLock()
jankMonitor.end(CUJ_SCREEN_OFF_SHOW_AOD)
}
+
TransitionState.RUNNING -> Unit
}
}
@@ -378,6 +384,21 @@ object KeyguardRootViewBinder {
}
disposables +=
+ view.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ if (viewModel.shouldSendFocalArea.value) {
+ launch {
+ wallpaperFocalAreaInteractor.wallpaperFocalAreaBounds
+ .filterNotNull()
+ .collect {
+ wallpaperFocalAreaInteractor.setWallpaperFocalAreaBounds(it)
+ }
+ }
+ }
+ }
+ }
+
+ disposables +=
view.onLayoutChanged(
OnLayoutChange(
viewModel,
@@ -523,6 +544,7 @@ object KeyguardRootViewBinder {
View.INVISIBLE
}
}
+
else -> {
if (isVisible.value) {
CrossFadeHelper.fadeIn(this, animatorListener)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index a2107871a585..7605bdd3d7b5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -55,6 +55,7 @@ import com.android.systemui.customization.R as customR
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.keyguard.domain.interactor.WallpaperFocalAreaInteractor
import com.android.systemui.keyguard.shared.model.ClockSizeSetting
import com.android.systemui.keyguard.ui.binder.KeyguardPreviewClockViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardPreviewSmartspaceViewBinder
@@ -117,6 +118,7 @@ constructor(
private val secureSettings: SecureSettings,
private val defaultShortcutsSection: DefaultShortcutsSection,
private val keyguardQuickAffordanceViewBinder: KeyguardQuickAffordanceViewBinder,
+ private val wallpaperFocalAreaInteractor: WallpaperFocalAreaInteractor,
) {
val hostToken: IBinder? = bundle.getBinder(KEY_HOST_TOKEN)
private val width: Int = bundle.getInt(KEY_VIEW_WIDTH)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/BlurConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/BlurConfig.kt
index 3eb8522e0338..542fb9b46bef 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/BlurConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/BlurConfig.kt
@@ -23,10 +23,4 @@ data class BlurConfig(val minBlurRadiusPx: Float, val maxBlurRadiusPx: Float) {
// No-op config that will be used by dagger of other SysUI variants which don't blur the
// background surface.
@Inject constructor() : this(0.0f, 0.0f)
-
- companion object {
- // Blur the shade much lesser than the background surface so that the surface is
- // distinguishable from the background.
- @JvmStatic fun Float.maxBlurRadiusToNotificationPanelBlurRadius(): Float = this / 3.0f
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt
index 733d7d71061e..b531c7fa49ec 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt
@@ -24,7 +24,6 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCE
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.BlurConfig
-import com.android.systemui.keyguard.ui.transitions.BlurConfig.Companion.maxBlurRadiusToNotificationPanelBlurRadius
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
import com.android.systemui.scene.shared.flag.SceneContainerFlag
@@ -89,9 +88,7 @@ constructor(
shadeDependentFlows.transitionFlow(
flowWhenShadeIsNotExpanded = emptyFlow(),
flowWhenShadeIsExpanded =
- transitionAnimation.immediatelyTransitionTo(
- blurConfig.maxBlurRadiusPx.maxBlurRadiusToNotificationPanelBlurRadius()
- ),
+ transitionAnimation.immediatelyTransitionTo(blurConfig.maxBlurRadiusPx),
)
} else {
emptyFlow<Float>()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index e51e05b8ab61..aa4293a201ac 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -28,6 +28,7 @@ import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.PulseExpansionInteractor
+import com.android.systemui.keyguard.domain.interactor.WallpaperFocalAreaInteractor
import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
@@ -133,6 +134,7 @@ constructor(
private val screenOffAnimationController: ScreenOffAnimationController,
private val aodBurnInViewModel: AodBurnInViewModel,
private val shadeInteractor: ShadeInteractor,
+ wallpaperFocalAreaInteractor: WallpaperFocalAreaInteractor,
) {
val burnInLayerVisibility: Flow<Int> =
keyguardTransitionInteractor.startedKeyguardTransitionStep
@@ -362,6 +364,8 @@ constructor(
initialValue = AnimatedValue.NotAnimating(false),
)
+ val shouldSendFocalArea = wallpaperFocalAreaInteractor.shouldSendFocalArea
+
fun onNotificationContainerBoundsChanged(top: Float, bottom: Float, animate: Boolean = false) {
keyguardInteractor.setNotificationContainerBounds(
NotificationContainerBounds(top = top, bottom = bottom, isAnimated = animate)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
index 44c4c8723dcb..89dcbf6aa52b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
@@ -24,7 +24,6 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.BlurConfig
-import com.android.systemui.keyguard.ui.transitions.BlurConfig.Companion.maxBlurRadiusToNotificationPanelBlurRadius
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
import com.android.systemui.scene.shared.flag.SceneContainerFlag
@@ -88,9 +87,7 @@ constructor(
shadeDependentFlows.transitionFlow(
flowWhenShadeIsNotExpanded = emptyFlow(),
flowWhenShadeIsExpanded =
- transitionAnimation.immediatelyTransitionTo(
- blurConfig.maxBlurRadiusPx.maxBlurRadiusToNotificationPanelBlurRadius()
- ),
+ transitionAnimation.immediatelyTransitionTo(blurConfig.maxBlurRadiusPx),
)
} else {
emptyFlow()
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
index 173a964cc5d3..40197b2daf49 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -536,8 +536,10 @@ public final class NavBarHelper implements
}
/**
- * @return Whether the IME is shown on top of the screen given the {@code vis} flag of
- * {@link InputMethodService} and the keyguard states.
+ * Checks whether the IME is shown on top of the screen, based on the given IME window
+ * visibility flags, and the current keyguard state.
+ *
+ * @param vis the IME window visibility.
*/
public boolean isImeShown(@ImeWindowVisibility int vis) {
View shadeWindowView = mNotificationShadeWindowController.getWindowRootView();
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 05d8bff2ceb6..ee24b3120966 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -18,7 +18,7 @@ package com.android.systemui.navigationbar;
import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
-import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
@@ -31,12 +31,13 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BACK_DISABLED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_BUTTON_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
import android.app.StatusBarManager;
+import android.app.StatusBarManager.NavigationHint;
import android.app.StatusBarManager.WindowVisibleState;
import android.content.Context;
import android.graphics.Rect;
@@ -111,6 +112,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
private TaskStackChangeListeners mTaskStackChangeListeners;
private Optional<Pip> mPipOptional;
private int mDefaultDisplayId;
+ @NavigationHint
private int mNavigationIconHints;
private final NavBarHelper.NavbarTaskbarStateUpdater mNavbarTaskbarStateUpdater =
new NavBarHelper.NavbarTaskbarStateUpdater() {
@@ -362,8 +364,8 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
.setFlag(SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE, longClickable)
.setFlag(SYSUI_STATE_IME_SHOWING,
(mNavigationIconHints & NAVIGATION_HINT_IME_SHOWN) != 0)
- .setFlag(SYSUI_STATE_IME_SWITCHER_SHOWING,
- (mNavigationIconHints & NAVIGATION_HINT_IME_SWITCHER_SHOWN) != 0)
+ .setFlag(SYSUI_STATE_IME_SWITCHER_BUTTON_SHOWING,
+ (mNavigationIconHints & NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN) != 0)
.setFlag(SYSUI_STATE_OVERVIEW_DISABLED,
(mDisabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0)
.setFlag(SYSUI_STATE_HOME_DISABLED,
@@ -489,12 +491,14 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
imeShown = (vis & InputMethodService.IME_VISIBLE_IMPERCEPTIBLE) != 0;
}
showImeSwitcher = imeShown && showImeSwitcher;
- int hints = Utilities.calculateBackDispositionHints(mNavigationIconHints, backDisposition,
+ int hints = Utilities.calculateNavigationIconHints(mNavigationIconHints, backDisposition,
imeShown, showImeSwitcher);
- if (hints != mNavigationIconHints) {
- mNavigationIconHints = hints;
- updateSysuiFlags();
+ if (hints == mNavigationIconHints) {
+ return;
}
+
+ mNavigationIconHints = hints;
+ updateSysuiFlags();
}
@Override
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 c78750718cf0..6842dbc2b12d 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
@@ -18,11 +18,12 @@ package com.android.systemui.navigationbar.views;
import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
-import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN;
import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.app.StatusBarManager.WindowType;
import static android.app.StatusBarManager.WindowVisibleState;
+import static android.app.StatusBarManager.navigationHintsToString;
import static android.app.StatusBarManager.windowStateToString;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.view.InsetsSource.FLAG_SUPPRESS_SCRIM;
@@ -43,7 +44,7 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_BUTTON_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
import static com.android.systemui.shared.system.QuickStepContract.isGesturalMode;
@@ -56,6 +57,7 @@ import android.annotation.NonNull;
import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager;
import android.app.StatusBarManager;
+import android.app.StatusBarManager.NavigationHint;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Insets;
@@ -233,6 +235,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
private @WindowVisibleState int mNavigationBarWindowState = WINDOW_STATE_SHOWING;
+ @NavigationHint
private int mNavigationIconHints = 0;
private @TransitionMode int mTransitionMode;
private boolean mLongPressHomeEnabled;
@@ -817,7 +820,6 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
if (mSavedState != null) {
getBarTransitions().getLightTransitionsController().restoreState(mSavedState);
}
- setNavigationIconHints(mNavigationIconHints);
setWindowVisible(isNavBarWindowVisible());
mView.setBehavior(mBehavior);
setNavBarMode(mNavBarMode);
@@ -1111,6 +1113,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
pw.println(" mLongPressHomeEnabled=" + mLongPressHomeEnabled);
pw.println(" mNavigationBarWindowState="
+ windowStateToString(mNavigationBarWindowState));
+ pw.println(" mNavigationIconHints=" + navigationHintsToString(mNavigationIconHints));
pw.println(" mTransitionMode="
+ BarTransitions.modeToString(mTransitionMode));
pw.println(" mTransientShown=" + mTransientShown);
@@ -1137,9 +1140,11 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
}
boolean imeShown = mNavBarHelper.isImeShown(vis);
showImeSwitcher = imeShown && showImeSwitcher;
- int hints = Utilities.calculateBackDispositionHints(mNavigationIconHints, backDisposition,
+ int hints = Utilities.calculateNavigationIconHints(mNavigationIconHints, backDisposition,
imeShown, showImeSwitcher);
- if (hints == mNavigationIconHints) return;
+ if (hints == mNavigationIconHints) {
+ return;
+ }
setNavigationIconHints(hints);
checkBarModes();
@@ -1682,8 +1687,8 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
.setFlag(SYSUI_STATE_NAV_BAR_HIDDEN, !isNavBarWindowVisible())
.setFlag(SYSUI_STATE_IME_SHOWING,
(mNavigationIconHints & NAVIGATION_HINT_IME_SHOWN) != 0)
- .setFlag(SYSUI_STATE_IME_SWITCHER_SHOWING,
- (mNavigationIconHints & NAVIGATION_HINT_IME_SWITCHER_SHOWN) != 0)
+ .setFlag(SYSUI_STATE_IME_SWITCHER_BUTTON_SHOWING,
+ (mNavigationIconHints & NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN) != 0)
.setFlag(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY,
allowSystemGestureIgnoringBarVisibility())
.commitUpdate(mDisplayId);
@@ -1926,12 +1931,20 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
};
@VisibleForTesting
+ @NavigationHint
int getNavigationIconHints() {
return mNavigationIconHints;
}
- private void setNavigationIconHints(int hints) {
- if (hints == mNavigationIconHints) return;
+ /**
+ * Updates the navigation icons based on {@code hints}.
+ *
+ * @param hints bit flags defined in {@link StatusBarManager}.
+ */
+ private void setNavigationIconHints(@NavigationHint int hints) {
+ if (hints == mNavigationIconHints) {
+ return;
+ }
if (!isLargeScreen(mContext)) {
// All IME functions handled by launcher via Sysui flags for large screen
final boolean newBackAlt = (hints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
@@ -1945,9 +1958,8 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
mView.setNavigationIconHints(hints);
}
if (DEBUG) {
- android.widget.Toast.makeText(mContext,
- "Navigation icon hints = " + hints,
- 500).show();
+ android.widget.Toast.makeText(mContext, "Navigation icon hints = " + hints, 500)
+ .show();
}
mNavigationIconHints = hints;
}
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 d5ae72165c4a..ed8e538cc895 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java
@@ -16,6 +16,9 @@
package com.android.systemui.navigationbar.views;
+import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN;
import static android.inputmethodservice.InputMethodService.canImeRenderGesturalNavButtons;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
@@ -31,7 +34,7 @@ import android.animation.PropertyValuesHolder;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.annotation.DrawableRes;
-import android.app.StatusBarManager;
+import android.app.StatusBarManager.NavigationHint;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Canvas;
@@ -113,6 +116,7 @@ public class NavigationBarView extends FrameLayout {
boolean mLongClickableAccessibilityButton;
int mDisabledFlags = 0;
+ @NavigationHint
int mNavigationIconHints = 0;
private int mNavBarMode;
private boolean mImeDrawsImeNavBar;
@@ -499,8 +503,7 @@ public class NavigationBarView extends FrameLayout {
}
private void orientBackButton(KeyButtonDrawable drawable) {
- final boolean useAltBack =
- (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
+ final boolean useAltBack = (mNavigationIconHints & NAVIGATION_HINT_BACK_ALT) != 0;
final boolean isRtl = mConfiguration.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
float degrees = useAltBack ? (isRtl ? 90 : -90) : 0;
if (drawable.getRotation() == degrees) {
@@ -555,8 +558,10 @@ public class NavigationBarView extends FrameLayout {
super.setLayoutDirection(layoutDirection);
}
- void setNavigationIconHints(int hints) {
- if (hints == mNavigationIconHints) return;
+ void setNavigationIconHints(@NavigationHint int hints) {
+ if (hints == mNavigationIconHints) {
+ return;
+ }
mNavigationIconHints = hints;
updateNavButtonIcons();
}
@@ -594,8 +599,7 @@ public class NavigationBarView extends FrameLayout {
// We have to replace or restore the back and home button icons when exiting or entering
// carmode, respectively. Recents are not available in CarMode in nav bar so change
// to recent icon is not required.
- final boolean useAltBack =
- (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
+ final boolean useAltBack = (mNavigationIconHints & NAVIGATION_HINT_BACK_ALT) != 0;
KeyButtonDrawable backIcon = mBackIcon;
orientBackButton(backIcon);
KeyButtonDrawable homeIcon = mHomeDefaultIcon;
@@ -607,11 +611,12 @@ public class NavigationBarView extends FrameLayout {
updateRecentsIcon();
- // Update IME button visibility, a11y and rotate button always overrides the appearance
- boolean disableImeSwitcher =
- (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN) == 0
- || isImeRenderingNavButtons();
- mContextualButtonGroup.setButtonVisibility(R.id.ime_switcher, !disableImeSwitcher);
+ // Update IME switcher button visibility, a11y and rotate button always overrides
+ // the appearance
+ boolean isImeSwitcherButtonVisible =
+ (mNavigationIconHints & NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN) != 0
+ && !isImeRenderingNavButtons();
+ mContextualButtonGroup.setButtonVisibility(R.id.ime_switcher, isImeSwitcherButtonVisible);
mBarTransitions.reapplyDarkIntensity();
@@ -665,7 +670,7 @@ public class NavigationBarView extends FrameLayout {
boolean isImeRenderingNavButtons() {
return mImeDrawsImeNavBar
&& mImeCanRenderGesturalNavButtons
- && (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0;
+ && (mNavigationIconHints & NAVIGATION_HINT_IME_SHOWN) != 0;
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
index d2ee126ace91..2cccaddc02a8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
@@ -498,11 +498,9 @@ fun gridHeight(rows: Int, tileHeight: Dp, tilePadding: Dp, gridPadding: Dp): Dp
return ((tileHeight + tilePadding) * rows) + gridPadding * 2
}
-private fun GridCell.key(index: Int, dragAndDropState: DragAndDropState): Any {
+private fun GridCell.key(index: Int): Any {
return when (this) {
- is TileGridCell -> {
- if (dragAndDropState.isMoving(tile.tileSpec)) index else key
- }
+ is TileGridCell -> key
is SpacerGridCell -> index
}
}
@@ -510,10 +508,13 @@ private fun GridCell.key(index: Int, dragAndDropState: DragAndDropState): Any {
/**
* Adds a list of [GridCell] to the lazy grid
*
- * @param cells the pairs of [GridCell] to [AnimatableTileViewModel]
+ * @param cells the pairs of [GridCell] to [BounceableTileViewModel]
+ * @param columns the number of columns of this tile grid
* @param dragAndDropState the [DragAndDropState] for this grid
* @param selectionState the [MutableSelectionState] for this grid
- * @param onToggleSize the callback when a tile's size is toggled
+ * @param coroutineScope the [CoroutineScope] to be used for the tiles
+ * @param largeTilesSpan the width used for large tiles
+ * @param onResize the callback when a tile has a new [ResizeOperation]
*/
fun LazyGridScope.EditTiles(
cells: List<Pair<GridCell, BounceableTileViewModel>>,
@@ -526,7 +527,7 @@ fun LazyGridScope.EditTiles(
) {
items(
count = cells.size,
- key = { cells[it].first.key(it, dragAndDropState) },
+ key = { cells[it].first.key(it) },
span = { cells[it].first.span },
contentType = { TileType },
) { index ->
@@ -536,13 +537,12 @@ fun LazyGridScope.EditTiles(
// If the tile is being moved, replace it with a visible spacer
SpacerGridCell(
Modifier.background(
- color =
- MaterialTheme.colorScheme.secondary.copy(
- alpha = EditModeTileDefaults.PLACEHOLDER_ALPHA
- ),
- shape = RoundedCornerShape(InactiveCornerRadius),
- )
- .animateItem()
+ color =
+ MaterialTheme.colorScheme.secondary.copy(
+ alpha = EditModeTileDefaults.PLACEHOLDER_ALPHA
+ ),
+ shape = RoundedCornerShape(InactiveCornerRadius),
+ )
)
} else {
TileGridCell(
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index ecd002705c60..63c10c9b971a 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -945,10 +945,6 @@ constructor(
override fun onTransitionAnimationEnd() {
sceneInteractor.onTransitionAnimationEnd()
}
-
- override fun onTransitionAnimationCancelled() {
- sceneInteractor.onTransitionAnimationCancelled()
- }
}
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt
index 08214c456897..f5c605211520 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt
@@ -35,7 +35,6 @@ import android.util.Log
import android.view.Display
import android.view.ScrollCaptureResponse
import android.view.ViewRootImpl.ActivityConfigCallback
-import android.view.WindowManager
import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
import android.widget.Toast
import android.window.WindowContext
@@ -218,9 +217,7 @@ internal constructor(
window.setFocusable(true)
viewProxy.requestFocus()
- if (screenshot.type != WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE) {
- enqueueScrollCaptureRequest(requestId, screenshot.userHandle)
- }
+ enqueueScrollCaptureRequest(requestId, screenshot.userHandle)
window.attachWindow()
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotCrossProfileService.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotCrossProfileService.kt
index 2e6c7567259f..d82a8bd0ddcd 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotCrossProfileService.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotCrossProfileService.kt
@@ -31,7 +31,7 @@ class ScreenshotCrossProfileService : Service() {
private val mBinder: IBinder =
object : ICrossProfileService.Stub() {
- override fun launchIntent(intent: Intent, bundle: Bundle) {
+ override fun launchIntent(intent: Intent, bundle: Bundle?) {
startActivity(intent, bundle)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
index 49f3cfc4ceaf..917869a66ca4 100644
--- a/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
+++ b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
@@ -27,6 +27,8 @@ import android.graphics.PorterDuff;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
+import android.graphics.RenderEffect;
+import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.os.Looper;
import android.util.AttributeSet;
@@ -373,4 +375,18 @@ public class ScrimView extends View {
((ScrimDrawable) mDrawable).setRoundedCorners(radius);
}
}
+
+ /**
+ * Blur the view with the specific blur radius or clear any blurs if the radius is 0
+ */
+ public void setBlurRadius(float blurRadius) {
+ if (blurRadius > 0) {
+ setRenderEffect(RenderEffect.createBlurEffect(
+ blurRadius,
+ blurRadius,
+ Shader.TileMode.CLAMP));
+ } else {
+ setRenderEffect(null);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 19bf4c0bab81..a1b4def09ba9 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -109,7 +109,6 @@ import com.android.systemui.keyguard.shared.model.Edge;
import com.android.systemui.keyguard.shared.model.TransitionState;
import com.android.systemui.keyguard.shared.model.TransitionStep;
import com.android.systemui.keyguard.ui.binder.KeyguardLongPressViewBinder;
-import com.android.systemui.keyguard.ui.transitions.BlurConfig;
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.KeyguardTouchHandlingViewModel;
import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
@@ -915,8 +914,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
if (!com.android.systemui.Flags.bouncerUiRevamp()) return;
if (isBouncerShowing && isExpanded()) {
- float shadeBlurEffect = BlurConfig.maxBlurRadiusToNotificationPanelBlurRadius(
- mDepthController.getMaxBlurRadiusPx());
+ float shadeBlurEffect = mDepthController.getMaxBlurRadiusPx();
mView.setRenderEffect(RenderEffect.createBlurEffect(
shadeBlurEffect,
shadeBlurEffect,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
index 48bbb0407ee3..48e374746bf5 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
@@ -17,6 +17,7 @@
package com.android.systemui.shade;
import static android.os.Trace.TRACE_TAG_APP;
+import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED;
import static com.android.systemui.Flags.enableViewCaptureTracing;
import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG;
@@ -49,10 +50,12 @@ import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.WindowInsetsController;
+import android.view.accessibility.AccessibilityEvent;
import com.android.app.viewcapture.ViewCaptureFactory;
import com.android.internal.view.FloatingActionMode;
import com.android.internal.widget.floatingtoolbar.FloatingToolbar;
+import com.android.systemui.Flags;
import com.android.systemui.scene.ui.view.WindowRootView;
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround;
import com.android.systemui.statusbar.phone.ConfigurationForwarder;
@@ -77,6 +80,8 @@ public class NotificationShadeWindowView extends WindowRootView {
private SafeCloseable mViewCaptureCloseable;
+ private boolean mAnimatingContentLaunch = false;
+
public NotificationShadeWindowView(Context context, AttributeSet attrs) {
super(context, attrs);
setMotionEventSplittingEnabled(false);
@@ -188,6 +193,22 @@ public class NotificationShadeWindowView extends WindowRootView {
}
}
+ @Override
+ public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
+ if (Flags.shadeLaunchAccessibility() && mAnimatingContentLaunch
+ && event.getEventType() == TYPE_VIEW_ACCESSIBILITY_FOCUSED) {
+ // Block accessibility focus events during launch animations to avoid stray TalkBack
+ // announcements.
+ return false;
+ }
+
+ return super.requestSendAccessibilityEvent(child, event);
+ }
+
+ public void setAnimatingContentLaunch(boolean animating) {
+ mAnimatingContentLaunch = animating;
+ }
+
public void setConfigurationForwarder(ConfigurationForwarder configurationForwarder) {
ShadeWindowGoesAround.isUnexpectedlyInLegacyMode();
mConfigurationForwarder = configurationForwarder;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index e5dcd2338b9d..ffec8f284c48 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -16,10 +16,12 @@
package com.android.systemui.shade;
+import static com.android.systemui.Flags.shadeLaunchAccessibility;
import static com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING;
import static com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
+import static com.android.systemui.util.kotlin.JavaAdapterKt.combineFlows;
import android.app.StatusBarManager;
import android.util.Log;
@@ -59,6 +61,7 @@ import com.android.systemui.res.R;
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.settings.brightness.domain.interactor.BrightnessMirrorShowingInteractor;
import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
+import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor;
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround;
import com.android.systemui.shared.animation.DisableSubpixelTextTransitionListener;
import com.android.systemui.statusbar.BlurUtils;
@@ -85,6 +88,7 @@ import com.android.systemui.window.ui.WindowRootViewBinder;
import com.android.systemui.window.ui.viewmodel.WindowRootViewModel;
import kotlinx.coroutines.ExperimentalCoroutinesApi;
+import kotlinx.coroutines.flow.Flow;
import java.io.PrintWriter;
import java.util.Optional;
@@ -174,6 +178,7 @@ public class NotificationShadeWindowViewController implements Dumpable {
NotificationShadeDepthController depthController,
NotificationShadeWindowView notificationShadeWindowView,
ShadeViewController shadeViewController,
+ ShadeAnimationInteractor shadeAnimationInteractor,
PanelExpansionInteractor panelExpansionInteractor,
ShadeExpansionStateManager shadeExpansionStateManager,
NotificationStackScrollLayoutController notificationStackScrollLayoutController,
@@ -238,9 +243,17 @@ public class NotificationShadeWindowViewController implements Dumpable {
collectFlow(mView, keyguardTransitionInteractor.transition(
Edge.create(LOCKSCREEN, DREAMING)),
mLockscreenToDreamingTransition);
+ Flow<Boolean> isLaunchAnimationRunning =
+ shadeLaunchAccessibility()
+ ? combineFlows(
+ notificationLaunchAnimationInteractor.isLaunchAnimationRunning(),
+ shadeAnimationInteractor.isLaunchingActivity(),
+ (notificationLaunching, shadeLaunching) ->
+ notificationLaunching || shadeLaunching)
+ : notificationLaunchAnimationInteractor.isLaunchAnimationRunning();
collectFlow(
mView,
- notificationLaunchAnimationInteractor.isLaunchAnimationRunning(),
+ isLaunchAnimationRunning,
this::setExpandAnimationRunning);
if (QSComposeFragment.isEnabled()) {
collectFlow(mView,
@@ -726,9 +739,17 @@ public class NotificationShadeWindowViewController implements Dumpable {
if (ActivityTransitionAnimator.DEBUG_TRANSITION_ANIMATION) {
Log.d(TAG, "Setting mExpandAnimationRunning=" + running);
}
+
if (running) {
mLaunchAnimationTimeout = mClock.uptimeMillis() + 5000;
}
+
+ if (shadeLaunchAccessibility()) {
+ // The view needs to know when an animation is ongoing so it can intercept
+ // unnecessary accessibility events.
+ mView.setAnimatingContentLaunch(running);
+ }
+
mExpandAnimationRunning = running;
mNotificationShadeWindowController.setLaunchingActivity(mExpandAnimationRunning);
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt b/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt
index 7a70966c2b12..b15615a83698 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt
@@ -5,6 +5,7 @@ import android.view.DisplayCutout
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.res.R
import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStore
+import com.android.systemui.statusbar.layout.StatusBarContentInsetsProvider
import javax.inject.Inject
/**
@@ -15,11 +16,9 @@ class QsBatteryModeController
@Inject
constructor(
@ShadeDisplayAware private val context: Context,
- insetsProviderStore: StatusBarContentInsetsProviderStore,
+ private val insetsProviderStore: StatusBarContentInsetsProviderStore,
) {
- private val insetsProvider = insetsProviderStore.defaultDisplay
-
private companion object {
// MotionLayout frames are in [0, 100]. Where 0 and 100 are reserved for start and end
// frames.
@@ -43,17 +42,19 @@ constructor(
* animation.
*/
@BatteryMeterView.BatteryPercentMode
- fun getBatteryMode(cutout: DisplayCutout?, qsExpandedFraction: Float): Int? =
- when {
+ fun getBatteryMode(cutout: DisplayCutout?, qsExpandedFraction: Float): Int? {
+ val insetsProvider = insetsProviderStore.forDisplay(context.displayId)
+ return when {
qsExpandedFraction > fadeInStartFraction -> BatteryMeterView.MODE_ESTIMATE
- qsExpandedFraction < fadeOutCompleteFraction ->
- if (hasCenterCutout(cutout)) {
+ insetsProvider != null && qsExpandedFraction < fadeOutCompleteFraction ->
+ if (hasCenterCutout(cutout, insetsProvider)) {
BatteryMeterView.MODE_ON
} else {
BatteryMeterView.MODE_ESTIMATE
}
else -> null
}
+ }
fun updateResources() {
fadeInStartFraction =
@@ -64,7 +65,10 @@ constructor(
MOTION_LAYOUT_MAX_FRAME.toFloat()
}
- private fun hasCenterCutout(cutout: DisplayCutout?): Boolean =
+ private fun hasCenterCutout(
+ cutout: DisplayCutout?,
+ insetsProvider: StatusBarContentInsetsProvider,
+ ): Boolean =
cutout?.let {
!insetsProvider.currentRotationHasCornerCutout() && !it.boundingRectTop.isEmpty
} ?: false
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
index e8a792c30aa2..d82f8e722744 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
@@ -57,6 +57,7 @@ import com.android.systemui.shade.ShadeHeaderController.Companion.QS_HEADER_CONS
import com.android.systemui.shade.ShadeViewProviderModule.Companion.SHADE_HEADER
import com.android.systemui.shade.carrier.ShadeCarrierGroup
import com.android.systemui.shade.carrier.ShadeCarrierGroupController
+import com.android.systemui.shade.data.repository.ShadeDisplaysRepository
import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStore
import com.android.systemui.statusbar.phone.StatusBarLocation
import com.android.systemui.statusbar.phone.StatusIconContainer
@@ -90,8 +91,9 @@ constructor(
private val statusBarIconController: StatusBarIconController,
private val tintedIconManagerFactory: TintedIconManager.Factory,
private val privacyIconsController: HeaderPrivacyIconsController,
- private val insetsProviderStore: StatusBarContentInsetsProviderStore,
+ private val statusBarContentInsetsProviderStore: StatusBarContentInsetsProviderStore,
@ShadeDisplayAware private val configurationController: ConfigurationController,
+ private val shadeDisplaysRepository: ShadeDisplaysRepository,
private val variableDateViewControllerFactory: VariableDateViewController.Factory,
@Named(SHADE_HEADER) private val batteryMeterViewController: BatteryMeterViewController,
private val dumpManager: DumpManager,
@@ -104,7 +106,9 @@ constructor(
private val statusOverlayHoverListenerFactory: StatusOverlayHoverListenerFactory,
) : ViewController<View>(header), Dumpable {
- private val insetsProvider = insetsProviderStore.defaultDisplay
+ private val statusBarContentInsetsProvider
+ get() =
+ statusBarContentInsetsProviderStore.forDisplay(shadeDisplaysRepository.displayId.value)
companion object {
/** IDs for transitions and constraints for the [MotionLayout]. */
@@ -222,10 +226,14 @@ constructor(
private val insetListener =
View.OnApplyWindowInsetsListener { view, insets ->
- updateConstraintsForInsets(view as MotionLayout, insets)
- lastInsets = WindowInsets(insets)
-
- view.onApplyWindowInsets(insets)
+ val windowInsets = WindowInsets(insets)
+ if (windowInsets != lastInsets) {
+ updateConstraintsForInsets(view as MotionLayout, insets)
+ lastInsets = windowInsets
+ view.onApplyWindowInsets(insets)
+ } else {
+ insets
+ }
}
private var singleCarrier = false
@@ -414,6 +422,7 @@ constructor(
}
private fun updateConstraintsForInsets(view: MotionLayout, insets: WindowInsets) {
+ val insetsProvider = statusBarContentInsetsProvider ?: return
val cutout = insets.displayCutout.also { this.cutout = it }
val sbInsets: Insets = insetsProvider.getStatusBarContentInsetsForCurrentRotation()
@@ -508,6 +517,9 @@ constructor(
systemIconsHoverContainer.setOnClickListener(null)
systemIconsHoverContainer.isClickable = false
}
+
+ lastInsets?.let { updateConstraintsForInsets(header, it) }
+
header.jumpToState(header.startState)
updatePosition()
updateScrollY()
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt
index edf503d03f3e..59d812403777 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt
@@ -16,17 +16,20 @@
package com.android.systemui.shade.domain.interactor
+import android.provider.Settings
import androidx.annotation.FloatRange
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.shade.shared.flag.DualShade
import com.android.systemui.shade.shared.model.ShadeMode
+import com.android.systemui.shared.settings.data.repository.SecureSettingsRepository
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
/**
@@ -76,29 +79,53 @@ interface ShadeModeInteractor {
class ShadeModeInteractorImpl
@Inject
-constructor(@Application applicationScope: CoroutineScope, repository: ShadeRepository) :
- ShadeModeInteractor {
+constructor(
+ @Application applicationScope: CoroutineScope,
+ repository: ShadeRepository,
+ secureSettingsRepository: SecureSettingsRepository,
+) : ShadeModeInteractor {
+
+ private val isDualShadeEnabled: Flow<Boolean> =
+ secureSettingsRepository.boolSetting(
+ Settings.Secure.DUAL_SHADE,
+ defaultValue = DUAL_SHADE_ENABLED_DEFAULT,
+ )
override val isShadeLayoutWide: StateFlow<Boolean> = repository.isShadeLayoutWide
override val shadeMode: StateFlow<ShadeMode> =
- isShadeLayoutWide
- .map(this::determineShadeMode)
+ combine(isDualShadeEnabled, repository.isShadeLayoutWide, ::determineShadeMode)
.stateIn(
applicationScope,
SharingStarted.Eagerly,
- initialValue = determineShadeMode(isShadeLayoutWide.value),
+ initialValue =
+ determineShadeMode(
+ isDualShadeEnabled = DUAL_SHADE_ENABLED_DEFAULT,
+ isShadeLayoutWide = repository.isShadeLayoutWide.value,
+ ),
)
@FloatRange(from = 0.0, to = 1.0) override fun getTopEdgeSplitFraction(): Float = 0.5f
- private fun determineShadeMode(isShadeLayoutWide: Boolean): ShadeMode {
+ private fun determineShadeMode(
+ isDualShadeEnabled: Boolean,
+ isShadeLayoutWide: Boolean,
+ ): ShadeMode {
return when {
- DualShade.isEnabled -> ShadeMode.Dual
+ isDualShadeEnabled ||
+ // TODO(b/388793191): This ensures that the dual_shade aconfig flag can also enable
+ // Dual Shade, to avoid breaking unit tests. Remove this once all references to the
+ // flag are removed.
+ DualShade.isEnabled -> ShadeMode.Dual
isShadeLayoutWide -> ShadeMode.Split
else -> ShadeMode.Single
}
}
+
+ companion object {
+ /* Whether the Dual Shade setting is enabled by default. */
+ private const val DUAL_SHADE_ENABLED_DEFAULT = false
+ }
}
class ShadeModeInteractorEmptyImpl @Inject constructor() : ShadeModeInteractor {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 7fc1510f1136..dcea8d85e10d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -273,12 +273,12 @@ public class CommandQueue extends IStatusBar.Stub implements
default void toggleQuickSettingsPanel() { }
/**
- * Called to notify IME window status changes.
+ * Sets the new IME window status.
*
- * @param displayId The id of the display to notify.
- * @param vis IME visibility.
- * @param backDisposition Disposition mode of back button.
- * @param showImeSwitcher {@code true} to show IME switch button.
+ * @param displayId The id of the display to which the IME is bound.
+ * @param vis The IME window visibility.
+ * @param backDisposition The IME back disposition mode.
+ * @param showImeSwitcher Whether the IME Switcher button should be shown.
*/
default void setImeWindowStatus(int displayId, @ImeWindowVisibility int vis,
@BackDispositionMode int backDisposition, boolean showImeSwitcher) { }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index a5595edcbb95..4269f60e1c2a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -88,6 +88,7 @@ import com.android.keyguard.TrustGrantFlags;
import com.android.keyguard.logging.KeyguardLogger;
import com.android.settingslib.Utils;
import com.android.settingslib.fuelgauge.BatteryStatus;
+import com.android.systemui.Flags;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.FaceHelpMessageDeferral;
import com.android.systemui.biometrics.FaceHelpMessageDeferralFactory;
@@ -199,7 +200,7 @@ public class KeyguardIndicationController {
private CharSequence mBiometricMessage;
private CharSequence mBiometricMessageFollowUp;
private BiometricSourceType mBiometricMessageSource;
- protected ColorStateList mInitialTextColorState;
+ private ColorStateList mInitialTextColorState;
private boolean mVisible;
private boolean mOrganizationOwnedDevice;
@@ -393,13 +394,27 @@ public class KeyguardIndicationController {
return mIndicationArea;
}
+ /**
+ * Notify controller about configuration changes.
+ */
+ public void onConfigurationChanged() {
+ // Get new text color in case theme has changed
+ if (Flags.indicationTextA11yFix()) {
+ setIndicationColorToThemeColor();
+ }
+ }
+
public void setIndicationArea(ViewGroup indicationArea) {
mIndicationArea = indicationArea;
mTopIndicationView = indicationArea.findViewById(R.id.keyguard_indication_text);
mLockScreenIndicationView = indicationArea.findViewById(
R.id.keyguard_indication_text_bottom);
- mInitialTextColorState = mTopIndicationView != null
- ? mTopIndicationView.getTextColors() : ColorStateList.valueOf(Color.WHITE);
+ if (Flags.indicationTextA11yFix()) {
+ setIndicationColorToThemeColor();
+ } else {
+ setIndicationTextColor(mTopIndicationView != null
+ ? mTopIndicationView.getTextColors() : ColorStateList.valueOf(Color.WHITE));
+ }
if (mRotateTextViewController != null) {
mRotateTextViewController.destroy();
}
@@ -436,6 +451,12 @@ public class KeyguardIndicationController {
mIsLogoutEnabledCallback);
}
+ @NonNull
+ private ColorStateList wallpaperTextColor() {
+ return ColorStateList.valueOf(
+ Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor));
+ }
+
/**
* Cleanup
*/
@@ -513,7 +534,7 @@ public class KeyguardIndicationController {
.setMessage(mContext.getResources().getString(
com.android.systemui.res.R.string.dismissible_keyguard_swipe)
)
- .setTextColor(mInitialTextColorState)
+ .setTextColor(getInitialTextColorState())
.build(),
/* updateImmediately */ true);
} else {
@@ -533,7 +554,7 @@ public class KeyguardIndicationController {
INDICATION_TYPE_DISCLOSURE,
new KeyguardIndication.Builder()
.setMessage(disclosure)
- .setTextColor(mInitialTextColorState)
+ .setTextColor(getInitialTextColorState())
.build(),
/* updateImmediately */ false);
}
@@ -602,7 +623,7 @@ public class KeyguardIndicationController {
INDICATION_TYPE_OWNER_INFO,
new KeyguardIndication.Builder()
.setMessage(finalInfo)
- .setTextColor(mInitialTextColorState)
+ .setTextColor(getInitialTextColorState())
.build(),
false);
} else {
@@ -624,7 +645,7 @@ public class KeyguardIndicationController {
INDICATION_TYPE_BATTERY,
new KeyguardIndication.Builder()
.setMessage(powerIndication)
- .setTextColor(mInitialTextColorState)
+ .setTextColor(getInitialTextColorState())
.build(),
animate);
} else {
@@ -645,7 +666,7 @@ public class KeyguardIndicationController {
new KeyguardIndication.Builder()
.setMessage(mContext.getResources().getText(
com.android.internal.R.string.lockscreen_storage_locked))
- .setTextColor(mInitialTextColorState)
+ .setTextColor(getInitialTextColorState())
.build(),
false);
} else {
@@ -666,7 +687,7 @@ public class KeyguardIndicationController {
.setMessage(mBiometricMessage)
.setForceAccessibilityLiveRegionAssertive()
.setMinVisibilityMillis(IMPORTANT_MSG_MIN_DURATION)
- .setTextColor(mInitialTextColorState)
+ .setTextColor(getInitialTextColorState())
.build(),
true
);
@@ -680,7 +701,7 @@ public class KeyguardIndicationController {
new KeyguardIndication.Builder()
.setMessage(mBiometricMessageFollowUp)
.setMinVisibilityMillis(IMPORTANT_MSG_MIN_DURATION)
- .setTextColor(mInitialTextColorState)
+ .setTextColor(getInitialTextColorState())
.build(),
true
);
@@ -711,7 +732,7 @@ public class KeyguardIndicationController {
INDICATION_TYPE_TRUST,
new KeyguardIndication.Builder()
.setMessage(trustGrantedIndication)
- .setTextColor(mInitialTextColorState)
+ .setTextColor(getInitialTextColorState())
.build(),
true);
hideBiometricMessage();
@@ -722,7 +743,7 @@ public class KeyguardIndicationController {
INDICATION_TYPE_TRUST,
new KeyguardIndication.Builder()
.setMessage(trustManagedIndication)
- .setTextColor(mInitialTextColorState)
+ .setTextColor(getInitialTextColorState())
.build(),
false);
} else {
@@ -751,7 +772,7 @@ public class KeyguardIndicationController {
INDICATION_TYPE_PERSISTENT_UNLOCK_MESSAGE,
new KeyguardIndication.Builder()
.setMessage(mPersistentUnlockMessage)
- .setTextColor(mInitialTextColorState)
+ .setTextColor(getInitialTextColorState())
.build(),
true);
} else {
@@ -792,7 +813,7 @@ public class KeyguardIndicationController {
new KeyguardIndication.Builder()
.setMessage(mContext.getString(
R.string.keyguard_indication_after_adaptive_auth_lock))
- .setTextColor(mInitialTextColorState)
+ .setTextColor(getInitialTextColorState())
.build(),
true);
} else {
@@ -1179,7 +1200,8 @@ public class KeyguardIndicationController {
} else {
message = mContext.getString(R.string.keyguard_retry);
}
- mStatusBarKeyguardViewManager.setKeyguardMessage(message, mInitialTextColorState,
+ mStatusBarKeyguardViewManager.setKeyguardMessage(message,
+ getInitialTextColorState(),
null);
}
} else {
@@ -1232,7 +1254,7 @@ public class KeyguardIndicationController {
public void dump(PrintWriter pw, String[] args) {
pw.println("KeyguardIndicationController:");
- pw.println(" mInitialTextColorState: " + mInitialTextColorState);
+ pw.println(" mInitialTextColorState: " + getInitialTextColorState());
pw.println(" mPowerPluggedInWired: " + mPowerPluggedInWired);
pw.println(" mPowerPluggedIn: " + mPowerPluggedIn);
pw.println(" mPowerCharged: " + mPowerCharged);
@@ -1253,6 +1275,22 @@ public class KeyguardIndicationController {
mRotateTextViewController.dump(pw, args);
}
+ protected ColorStateList getInitialTextColorState() {
+ return mInitialTextColorState;
+ }
+
+ private void setIndicationColorToThemeColor() {
+ mInitialTextColorState = wallpaperTextColor();
+ }
+
+ /**
+ * @deprecated Use {@link #setIndicationColorToThemeColor}
+ */
+ @Deprecated
+ private void setIndicationTextColor(ColorStateList color) {
+ mInitialTextColorState = color;
+ }
+
protected class BaseKeyguardCallback extends KeyguardUpdateMonitorCallback {
@Override
public void onTimeChanged() {
@@ -1358,7 +1396,7 @@ public class KeyguardIndicationController {
mBouncerMessageInteractor.setFaceAcquisitionMessage(helpString);
}
mStatusBarKeyguardViewManager.setKeyguardMessage(helpString,
- mInitialTextColorState, biometricSourceType);
+ getInitialTextColorState(), biometricSourceType);
} else if (mScreenLifecycle.getScreenState() == SCREEN_ON) {
if (isCoExFaceAcquisitionMessage && msgId == FACE_ACQUIRED_TOO_DARK) {
showBiometricMessage(
@@ -1655,7 +1693,7 @@ public class KeyguardIndicationController {
private void showErrorMessageNowOrLater(String errString, @Nullable String followUpMsg,
BiometricSourceType biometricSourceType) {
if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
- mStatusBarKeyguardViewManager.setKeyguardMessage(errString, mInitialTextColorState,
+ mStatusBarKeyguardViewManager.setKeyguardMessage(errString, getInitialTextColorState(),
biometricSourceType);
} else if (mScreenLifecycle.getScreenState() == SCREEN_ON) {
showBiometricMessage(errString, followUpMsg, biometricSourceType);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index ccea254defaa..4c6fa4839e5c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -284,7 +284,8 @@ public class NotificationListener extends NotificationListenerWithPlugins implem
/* rankingAdjustment= */ 0,
/* isBubble= */ false,
/* proposedImportance= */ 0,
- /* sensitiveContent= */ false
+ /* sensitiveContent= */ false,
+ /* summarization = */ null
);
}
return ranking;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/headsup/shared/StatusBarNoHunBehavior.kt b/packages/SystemUI/src/com/android/systemui/statusbar/headsup/shared/StatusBarNoHunBehavior.kt
index 2ae54d7c6c83..c9024d934068 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/headsup/shared/StatusBarNoHunBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/headsup/shared/StatusBarNoHunBehavior.kt
@@ -33,7 +33,7 @@ object StatusBarNoHunBehavior {
/** Is the refactor enabled */
@JvmStatic
inline val isEnabled
- get() = Flags.statusBarNoHunBehavior() && android.app.Flags.notificationsRedesignAppIcons()
+ get() = Flags.statusBarNoHunBehavior()
/**
* Called to ensure code is only run when the flag is enabled. This protects users from the
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java
index 0171fb72e158..be61dc95fe20 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java
@@ -706,7 +706,7 @@ public class HeadsUpManagerImpl
}
private void updateHeadsUpFlow() {
- mHeadsUpNotificationRows.setValue(new HashSet<>(getHeadsUpEntryPhoneMap().values()));
+ mHeadsUpNotificationRows.setValue(new HashSet<>(mHeadsUpEntryMap.values()));
}
@Override
@@ -732,11 +732,6 @@ public class HeadsUpManagerImpl
return mHeadsUpAnimatingAway.getValue();
}
- @NonNull
- private ArrayMap<String, HeadsUpEntry> getHeadsUpEntryPhoneMap() {
- return mHeadsUpEntryMap;
- }
-
/**
* Called to notify the listeners that the HUN animating away animation has ended.
*/
@@ -1014,7 +1009,7 @@ public class HeadsUpManagerImpl
@Override
public void setRemoteInputActive(
@NonNull NotificationEntry entry, boolean remoteInputActive) {
- HeadsUpEntry headsUpEntry = getHeadsUpEntryPhone(entry.getKey());
+ HeadsUpEntry headsUpEntry = mHeadsUpEntryMap.get(entry.getKey());
if (headsUpEntry != null && headsUpEntry.mRemoteInputActive != remoteInputActive) {
headsUpEntry.mRemoteInputActive = remoteInputActive;
if (ExpandHeadsUpOnInlineReply.isEnabled() && remoteInputActive) {
@@ -1029,11 +1024,6 @@ public class HeadsUpManagerImpl
}
}
- @Nullable
- private HeadsUpEntry getHeadsUpEntryPhone(@NonNull String key) {
- return mHeadsUpEntryMap.get(key);
- }
-
@Override
public void setGutsShown(@NonNull NotificationEntry entry, boolean gutsShown) {
HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.getKey());
@@ -1125,7 +1115,7 @@ public class HeadsUpManagerImpl
return true;
}
- HeadsUpEntry headsUpEntry = getHeadsUpEntryPhone(key);
+ HeadsUpEntry headsUpEntry = mHeadsUpEntryMap.get(key);
HeadsUpEntry topEntry = getTopHeadsUpEntryPhone();
if (headsUpEntry == null || headsUpEntry != topEntry) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BundleNotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BundleNotificationInfo.java
index aad618d50067..9c6e41c482b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BundleNotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BundleNotificationInfo.java
@@ -64,11 +64,11 @@ public class BundleNotificationInfo extends NotificationInfo {
boolean isNonblockable,
boolean wasShownHighPriority,
AssistantFeedbackController assistantFeedbackController,
- MetricsLogger metricsLogger) throws RemoteException {
+ MetricsLogger metricsLogger, OnClickListener onCloseClick) throws RemoteException {
super.bindNotification(pm, iNotificationManager, onUserInteractionCallback,
channelEditorDialogController, pkg, notificationChannel, entry, onSettingsClick,
onAppSettingsClick, uiEventLogger, isDeviceProvisioned, isNonblockable,
- wasShownHighPriority, assistantFeedbackController, metricsLogger);
+ wasShownHighPriority, assistantFeedbackController, metricsLogger, onCloseClick);
// Additionally, bind the feedback button.
ComponentName assistant = iNotificationManager.getAllowedNotificationAssistant();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt
index 793b3b8b1e42..6aa5e405f29c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.row
+import android.animation.ValueAnimator
import android.content.Context
import android.graphics.BlendMode
import android.graphics.Canvas
@@ -38,8 +39,8 @@ import androidx.compose.ui.viewinterop.AndroidView
import com.android.internal.graphics.ColorUtils
import com.android.systemui.res.R
import com.android.systemui.surfaceeffects.shaderutil.ShaderUtilLibrary
-import kotlin.math.max
-import kotlin.math.roundToInt
+import com.android.wm.shell.shared.animation.Interpolators
+import kotlin.math.min
/**
* A background style for smarter-smart-actions. The style is composed by a simplex3d noise,
@@ -48,7 +49,7 @@ import kotlin.math.roundToInt
class MagicActionBackgroundDrawable(
context: Context,
primaryContainer: Int? = null,
- private val seed: Float = 0f,
+ seed: Float = 0f,
) : Drawable() {
private val pixelDensity = context.resources.displayMetrics.density
@@ -60,17 +61,15 @@ class MagicActionBackgroundDrawable(
.toFloat()
private val buttonShape = Path()
private val paddingVertical =
- context.resources
- .getDimensionPixelSize(R.dimen.smart_reply_button_padding_vertical)
- .toFloat()
+ context.resources.getDimensionPixelSize(R.dimen.smart_action_button_icon_padding).toFloat()
/** The color of the button background. */
private val mainColor =
primaryContainer
?: context.getColor(com.android.internal.R.color.materialColorPrimaryContainer)
- /** Slightly dimmed down version of [mainColor] used on the simplex noise. */
- private val dimColor: Int
+ /** Slightly brighter version of [mainColor] used on the simplex noise. */
+ private val effectColor: Int
get() {
val labColor = arrayOf(0.0, 0.0, 0.0).toDoubleArray()
ColorUtils.colorToLAB(mainColor, labColor)
@@ -78,56 +77,97 @@ class MagicActionBackgroundDrawable(
return ColorUtils.CAMToColor(
camColor.hue,
camColor.chroma,
- max(0f, (labColor[0] - 20).toFloat()),
+ min(100f, (labColor[0] + 10).toFloat()),
)
}
private val bgShader = MagicActionBackgroundShader()
private val bgPaint = Paint()
private val outlinePaint = Paint()
+ private val gradientAnimator =
+ ValueAnimator.ofFloat(0f, 1f).apply {
+ duration = 2500
+ interpolator = Interpolators.LINEAR
+ addUpdateListener { invalidateSelf() }
+ }
+ private val turbulenceAnimator =
+ ValueAnimator.ofFloat(seed, seed + TURBULENCE_MOVEMENT).apply {
+ duration = ANIMATION_DURATION
+ interpolator = Interpolators.LINEAR
+ addUpdateListener { invalidateSelf() }
+ start()
+ }
+ private val effectFadeAnimation =
+ ValueAnimator.ofFloat(0f, 1f).apply {
+ duration = 1000
+ startDelay = ANIMATION_DURATION - 1000L
+ interpolator = Interpolators.STANDARD_DECELERATE
+ addUpdateListener { invalidateSelf() }
+ }
init {
bgShader.setColorUniform("in_color", mainColor)
- bgShader.setColorUniform("in_dimColor", dimColor)
+ bgShader.setColorUniform("in_effectColor", effectColor)
bgPaint.shader = bgShader
outlinePaint.style = Paint.Style.STROKE
// Stroke is doubled in width and then clipped, to avoid anti-aliasing artifacts at the edge
// of the rectangle.
outlinePaint.strokeWidth = outlineStrokeWidth * 2
outlinePaint.blendMode = BlendMode.SCREEN
- outlinePaint.alpha = (255 * 0.32f).roundToInt()
+ outlinePaint.alpha = OUTLINE_ALPHA
+
+ animate()
+ }
+
+ private fun animate() {
+ turbulenceAnimator.start()
+ gradientAnimator.start()
+ effectFadeAnimation.start()
}
override fun draw(canvas: Canvas) {
+ updateShaders()
+
// We clip instead of drawing 2 rounded rects, otherwise there will be artifacts where
// around the button background and the outline.
+ canvas.save()
canvas.clipPath(buttonShape)
+ canvas.drawPath(buttonShape, bgPaint)
+ canvas.drawPath(buttonShape, outlinePaint)
+ canvas.restore()
+ }
- canvas.drawRect(bounds, bgPaint)
- canvas.drawRoundRect(
- bounds.left.toFloat(),
- bounds.top + paddingVertical,
- bounds.right.toFloat(),
- bounds.bottom - paddingVertical,
- cornerRadius,
- cornerRadius,
- outlinePaint,
- )
+ private fun updateShaders() {
+ val effectAlpha = 1f - effectFadeAnimation.animatedValue as Float
+ val turbulenceZ = turbulenceAnimator.animatedValue as Float
+ bgShader.setFloatUniform("in_sparkleMove", turbulenceZ * 1000)
+ bgShader.setFloatUniform("in_noiseMove", 0f, 0f, turbulenceZ)
+ bgShader.setFloatUniform("in_turbulenceAlpha", effectAlpha)
+ bgShader.setFloatUniform("in_spkarkleAlpha", SPARKLE_ALPHA * effectAlpha)
+ val gradientOffset = gradientAnimator.animatedValue as Float * bounds.width()
+ val outlineGradient =
+ LinearGradient(
+ gradientOffset + bounds.left.toFloat(),
+ 0f,
+ gradientOffset + bounds.right.toFloat(),
+ 0f,
+ mainColor,
+ ColorUtils.setAlphaComponent(mainColor, 0),
+ Shader.TileMode.MIRROR,
+ )
+ outlinePaint.shader = outlineGradient
}
override fun onBoundsChange(bounds: Rect) {
super.onBoundsChange(bounds)
val width = bounds.width().toFloat()
- val height = bounds.height() - paddingVertical * 2
+ val height = bounds.height().toFloat()
if (width == 0f || height == 0f) return
bgShader.setFloatUniform("in_gridNum", NOISE_SIZE)
- bgShader.setFloatUniform("in_spkarkleAlpha", SPARKLE_ALPHA)
- bgShader.setFloatUniform("in_noiseMove", 0f, 0f, 0f)
bgShader.setFloatUniform("in_size", width, height)
bgShader.setFloatUniform("in_aspectRatio", width / height)
- bgShader.setFloatUniform("in_time", seed)
bgShader.setFloatUniform("in_pixelDensity", pixelDensity)
buttonShape.reset()
@@ -140,18 +180,6 @@ class MagicActionBackgroundDrawable(
cornerRadius,
Path.Direction.CW,
)
-
- val outlineGradient =
- LinearGradient(
- bounds.left.toFloat(),
- 0f,
- bounds.right.toFloat(),
- 0f,
- mainColor,
- ColorUtils.setAlphaComponent(mainColor, 0),
- Shader.TileMode.CLAMP,
- )
- outlinePaint.shader = outlineGradient
}
override fun setAlpha(alpha: Int) {
@@ -168,10 +196,15 @@ class MagicActionBackgroundDrawable(
companion object {
/** Smoothness of the turbulence. Larger numbers yield more detail. */
- private const val NOISE_SIZE = 0.7f
-
+ private const val NOISE_SIZE = 0.57f
/** Strength of the sparkles overlaid on the turbulence. */
private const val SPARKLE_ALPHA = 0.15f
+ /** Alpha (0..255) of the button outline */
+ private const val OUTLINE_ALPHA = 82
+ /** Turbulence grid size */
+ private const val TURBULENCE_MOVEMENT = 4.3f
+ /** Total animation duration in millis */
+ private const val ANIMATION_DURATION = 5000L
}
}
@@ -183,24 +216,25 @@ private class MagicActionBackgroundShader : RuntimeShader(SHADER) {
"""
uniform float in_gridNum;
uniform vec3 in_noiseMove;
+ uniform half in_sparkleMove;
uniform vec2 in_size;
uniform float in_aspectRatio;
- uniform half in_time;
uniform half in_pixelDensity;
+ uniform float in_turbulenceAlpha;
uniform float in_spkarkleAlpha;
layout(color) uniform vec4 in_color;
- layout(color) uniform vec4 in_dimColor;
+ layout(color) uniform vec4 in_effectColor;
"""
private const val MAIN_SHADER =
"""vec4 main(vec2 p) {
vec2 uv = p / in_size.xy;
uv.x *= in_aspectRatio;
vec3 noiseP = vec3(uv + in_noiseMove.xy, in_noiseMove.z) * in_gridNum;
- half luma = 1.0 - getLuminosity(half3(simplex3d(noiseP)));
- half4 turbulenceColor = mix(in_color, in_dimColor, luma);
- float sparkle = sparkles(p - mod(p, in_pixelDensity * 0.8), in_time);
+ half luma = getLuminosity(half3(simplex3d(noiseP)));
+ half4 turbulenceColor = mix(in_color, in_effectColor, luma * in_turbulenceAlpha);
+ float sparkle = sparkles(p - mod(p, in_pixelDensity * 0.8), in_sparkleMove);
sparkle = min(sparkle * in_spkarkleAlpha, in_spkarkleAlpha);
- return turbulenceColor + half4(half3(sparkle), 1.0);
+ return saturate(turbulenceColor + half4(sparkle));
}
"""
private const val SHADER = UNIFORMS + ShaderUtilLibrary.SHADER_LIB + MAIN_SHADER
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 9712db8a1812..b1e5b22f9b1a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -70,10 +70,10 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewListener;
import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.wmshell.BubblesManager;
@@ -422,7 +422,8 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta
row.getIsNonblockable(),
mHighPriorityProvider.isHighPriority(row.getEntry()),
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ row.getCloseButtonOnClickListener(row));
}
/**
@@ -476,7 +477,8 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta
row.getIsNonblockable(),
mHighPriorityProvider.isHighPriority(row.getEntry()),
mAssistantFeedbackController,
- mMetricsLogger);
+ mMetricsLogger,
+ row.getCloseButtonOnClickListener(row));
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index 20120991b5ac..8d26f94ced21 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -202,7 +202,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
boolean isNonblockable,
boolean wasShownHighPriority,
AssistantFeedbackController assistantFeedbackController,
- MetricsLogger metricsLogger)
+ MetricsLogger metricsLogger, OnClickListener onCloseClick)
throws RemoteException {
mINotificationManager = iNotificationManager;
mMetricsLogger = metricsLogger;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index 6e8ec9576f80..bf738aa1128f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -42,6 +42,7 @@ import android.widget.FrameLayout.LayoutParams;
import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Flags;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.AlphaOptimizedImageView;
@@ -270,6 +271,10 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
mInfoItem = createPartialConversationItem(mContext);
} else if (personNotifType >= PeopleNotificationIdentifier.TYPE_FULL_PERSON) {
mInfoItem = createConversationItem(mContext);
+ } else if (android.app.Flags.uiRichOngoing()
+ && Flags.permissionHelperUiRichOngoing()
+ && entry.getSbn().getNotification().isPromotedOngoing()) {
+ mInfoItem = createPromotedItem(mContext);
} else {
mInfoItem = createInfoItem(mContext);
}
@@ -682,6 +687,16 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
R.drawable.ic_settings);
}
+ static NotificationMenuItem createPromotedItem(Context context) {
+ Resources res = context.getResources();
+ String infoDescription = res.getString(R.string.notification_menu_gear_description);
+ PromotedNotificationInfo infoContent =
+ (PromotedNotificationInfo) LayoutInflater.from(context).inflate(
+ R.layout.promoted_notification_info, null, false);
+ return new NotificationMenuItem(context, infoDescription, infoContent,
+ R.drawable.ic_settings);
+ }
+
static NotificationMenuItem createBundleItem(Context context) {
Resources res = context.getResources();
String infoDescription = res.getString(R.string.notification_menu_gear_description);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java
new file mode 100644
index 000000000000..e4a0fa5b534e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java
@@ -0,0 +1,104 @@
+/*
+ * 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.statusbar.notification.row;
+
+import android.app.INotificationManager;
+import android.app.NotificationChannel;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+import android.service.notification.StatusBarNotification;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.res.R;
+import com.android.systemui.statusbar.notification.AssistantFeedbackController;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+/**
+ * The guts of a notification revealed when performing a long press, specifically
+ * for notifications that are shown as promoted. Contains extra controls to allow user to revoke
+ * app permissions for sending promoted notifications.
+ */
+public class PromotedNotificationInfo extends NotificationInfo {
+ private static final String TAG = "PromotedNotifInfoGuts";
+ private INotificationManager mNotificationManager;
+
+ public PromotedNotificationInfo(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public void bindNotification(
+ PackageManager pm,
+ INotificationManager iNotificationManager,
+ OnUserInteractionCallback onUserInteractionCallback,
+ ChannelEditorDialogController channelEditorDialogController,
+ String pkg,
+ NotificationChannel notificationChannel,
+ NotificationEntry entry,
+ OnSettingsClickListener onSettingsClick,
+ OnAppSettingsClickListener onAppSettingsClick,
+ UiEventLogger uiEventLogger,
+ boolean isDeviceProvisioned,
+ boolean isNonblockable,
+ boolean wasShownHighPriority,
+ AssistantFeedbackController assistantFeedbackController,
+ MetricsLogger metricsLogger, OnClickListener onCloseClick) throws RemoteException {
+ super.bindNotification(pm, iNotificationManager, onUserInteractionCallback,
+ channelEditorDialogController, pkg, notificationChannel, entry, onSettingsClick,
+ onAppSettingsClick, uiEventLogger, isDeviceProvisioned, isNonblockable,
+ wasShownHighPriority, assistantFeedbackController, metricsLogger, onCloseClick);
+
+ mNotificationManager = iNotificationManager;
+
+ bindDismiss(entry.getSbn(), onCloseClick);
+ bindDemote(entry.getSbn(), pkg);
+ }
+
+
+ protected void bindDismiss(StatusBarNotification sbn,
+ View.OnClickListener onCloseClick) {
+ View dismissButton = findViewById(R.id.promoted_dismiss);
+
+ dismissButton.setOnClickListener(onCloseClick);
+ dismissButton.setVisibility(!sbn.isNonDismissable()
+ && dismissButton.hasOnClickListeners() ? VISIBLE : GONE);
+
+ }
+
+ protected void bindDemote(StatusBarNotification sbn, String packageName) {
+ View demoteButton = findViewById(R.id.promoted_demote);
+ demoteButton.setOnClickListener(getDemoteClickListener(sbn, packageName));
+ demoteButton.setVisibility(demoteButton.hasOnClickListeners() ? VISIBLE : GONE);
+ }
+
+ private OnClickListener getDemoteClickListener(StatusBarNotification sbn, String packageName) {
+ return ((View unusedView) -> {
+ try {
+ // TODO(b/391661009): Signal AutomaticPromotionCoordinator here
+ mNotificationManager.setCanBePromoted(packageName, sbn.getUid(), false, true);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Couldn't revoke live update permission", e);
+ }
+ });
+ }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
index 1965b9538df0..f7401440cfcb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
@@ -36,6 +36,8 @@ import com.android.systemui.util.kotlin.DisposableHandles
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
/** Binds the shared notification container to its view-model. */
@SysUISingleton
@@ -143,21 +145,25 @@ constructor(
if (!SceneContainerFlag.isEnabled) {
if (Flags.magicPortraitWallpapers()) {
launch {
- viewModel
- .getNotificationStackAbsoluteBottom(
- calculateMaxNotifications = calculateMaxNotifications,
- calculateHeight = { maxNotifications ->
- notificationStackSizeCalculator.computeHeight(
- maxNotifs = maxNotifications,
- shelfHeight = controller.getShelfHeight().toFloat(),
- stack = controller.view,
- )
- },
- controller.getShelfHeight().toFloat(),
+ combine(
+ viewModel.getNotificationStackAbsoluteBottom(
+ calculateMaxNotifications = calculateMaxNotifications,
+ calculateHeight = { maxNotifications ->
+ notificationStackSizeCalculator.computeHeight(
+ maxNotifs = maxNotifications,
+ shelfHeight =
+ controller.getShelfHeight().toFloat(),
+ stack = controller.view,
+ )
+ },
+ controller.getShelfHeight().toFloat(),
+ ),
+ viewModel.configurationBasedDimensions.map { it.marginTop },
+ ::Pair,
)
- .collect { bottom ->
+ .collect { (bottom: Float, marginTop: Int) ->
keyguardInteractor.setNotificationStackAbsoluteBottom(
- bottom
+ marginTop + bottom
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
index 16e9c717935c..a2f1ded042f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
@@ -26,24 +26,31 @@ import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
-import android.widget.TextView;
import androidx.annotation.StyleRes;
+import androidx.core.graphics.ColorUtils;
import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Flags;
import com.android.systemui.keyguard.KeyguardIndication;
import com.android.systemui.res.R;
+import com.android.systemui.shared.shadow.DoubleShadowTextView;
/**
* A view to show hints on Keyguard ("Swipe up to unlock", "Tap again to open").
*/
-public class KeyguardIndicationTextView extends TextView {
+public class KeyguardIndicationTextView extends DoubleShadowTextView {
+ // Minimum luminance for texts to receive shadows.
+ private static final float MIN_TEXT_SHADOW_LUMINANCE = 0.5f;
public static final long Y_IN_DURATION = 600L;
@StyleRes
private static int sStyleId = R.style.TextAppearance_Keyguard_BottomArea;
@StyleRes
+ private static int sStyleWithDoubleShadowTextId =
+ R.style.TextAppearance_Keyguard_BottomArea_DoubleShadow;
+ @StyleRes
private static int sButtonStyleId = R.style.TextAppearance_Keyguard_BottomArea_Button;
private boolean mAnimationsEnabled = true;
@@ -226,7 +233,14 @@ public class KeyguardIndicationTextView extends TextView {
if (mKeyguardIndicationInfo.getBackground() != null) {
setTextAppearance(sButtonStyleId);
} else {
- setTextAppearance(sStyleId);
+ // If text is transparent or dark color, don't draw any shadow
+ if (Flags.indicationTextA11yFix() && ColorUtils.calculateLuminance(
+ mKeyguardIndicationInfo.getTextColor().getDefaultColor())
+ > MIN_TEXT_SHADOW_LUMINANCE) {
+ setTextAppearance(sStyleWithDoubleShadowTextId);
+ } else {
+ setTextAppearance(sStyleId);
+ }
}
setBackground(mKeyguardIndicationInfo.getBackground());
setTextColor(mKeyguardIndicationInfo.getTextColor());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index 4c2bfe5ca257..40245aef4f67 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -23,6 +23,7 @@ import static com.android.systemui.Flags.updateUserSwitcherBackground;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
+import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -56,6 +57,7 @@ import com.android.systemui.log.core.LogLevel;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.res.R;
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
+import com.android.systemui.shade.ShadeDisplayAware;
import com.android.systemui.shade.ShadeViewStateProvider;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.StatusBarState;
@@ -114,6 +116,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
R.id.keyguard_hun_animator_start_tag);
private final CoroutineDispatcher mCoroutineDispatcher;
+ private final Context mContext;
private final CarrierTextController mCarrierTextController;
private final ConfigurationController mConfigurationController;
private final SystemStatusAnimationScheduler mAnimationScheduler;
@@ -129,7 +132,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
private final KeyguardStatusBarViewModel mKeyguardStatusBarViewModel;
private final BiometricUnlockController mBiometricUnlockController;
private final SysuiStatusBarStateController mStatusBarStateController;
- private final StatusBarContentInsetsProvider mInsetsProvider;
+ private final StatusBarContentInsetsProviderStore mInsetsProviderStore;
private final UserManager mUserManager;
private final StatusBarUserChipViewModel mStatusBarUserChipViewModel;
private final SecureSettings mSecureSettings;
@@ -314,6 +317,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
@Inject
public KeyguardStatusBarViewController(
@Main CoroutineDispatcher dispatcher,
+ @ShadeDisplayAware Context context,
KeyguardStatusBarView view,
CarrierTextController carrierTextController,
ConfigurationController configurationController,
@@ -347,6 +351,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
) {
super(view);
mCoroutineDispatcher = dispatcher;
+ mContext = context;
mCarrierTextController = carrierTextController;
mConfigurationController = configurationController;
mAnimationScheduler = animationScheduler;
@@ -362,7 +367,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
mKeyguardStatusBarViewModel = keyguardStatusBarViewModel;
mBiometricUnlockController = biometricUnlockController;
mStatusBarStateController = statusBarStateController;
- mInsetsProvider = statusBarContentInsetsProviderStore.getDefaultDisplay();
+ mInsetsProviderStore = statusBarContentInsetsProviderStore;
mUserManager = userManager;
mStatusBarUserChipViewModel = userChipViewModel;
mSecureSettings = secureSettings;
@@ -404,6 +409,10 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
mStatusOverlayHoverListenerFactory = statusOverlayHoverListenerFactory;
}
+ private StatusBarContentInsetsProvider insetsProvider() {
+ return mInsetsProviderStore.forDisplay(mContext.getDisplayId());
+ }
+
@Override
protected void onInit() {
super.onInit();
@@ -446,7 +455,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
.createDarkAwareListener(mSystemIconsContainer, mView.darkChangeFlow());
mSystemIconsContainer.setOnHoverListener(hoverListener);
mView.setOnApplyWindowInsetsListener(
- (view, windowInsets) -> mView.updateWindowInsets(windowInsets, mInsetsProvider));
+ (view, windowInsets) -> mView.updateWindowInsets(windowInsets, insetsProvider()));
mSecureSettings.registerContentObserverForUserSync(
Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON,
false,
@@ -645,7 +654,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
* {@code OnApplyWindowInsetsListener}s.
*/
public void setDisplayCutout(@Nullable DisplayCutout displayCutout) {
- mView.setDisplayCutout(displayCutout, mInsetsProvider);
+ mView.setDisplayCutout(displayCutout, insetsProvider());
}
/**
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 3f44f7bdef90..caf8a43b2aaf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -46,7 +46,6 @@ import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStore
-import com.android.systemui.statusbar.layout.StatusBarContentInsetsProvider
import com.android.systemui.statusbar.policy.Clock
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.window.StatusBarWindowStateController
@@ -84,7 +83,7 @@ private constructor(
private val configurationController: ConfigurationController,
private val statusOverlayHoverListenerFactory: StatusOverlayHoverListenerFactory,
private val darkIconDispatcher: DarkIconDispatcher,
- private val statusBarContentInsetsProvider: StatusBarContentInsetsProvider,
+ private val statusBarContentInsetsProviderStore: StatusBarContentInsetsProviderStore,
private val lazyStatusBarShadeDisplayPolicy: Lazy<StatusBarTouchShadeDisplayPolicy>,
) : ViewController<PhoneStatusBarView>(view) {
@@ -92,6 +91,8 @@ private constructor(
private lateinit var clock: Clock
private lateinit var startSideContainer: View
private lateinit var endSideContainer: View
+ private val statusBarContentInsetsProvider
+ get() = statusBarContentInsetsProviderStore.forDisplay(context.displayId)
private val iconsOnTouchListener =
object : View.OnTouchListener {
@@ -189,11 +190,9 @@ private constructor(
init {
// These should likely be done in `onInit`, not `init`.
mView.setTouchEventHandler(PhoneStatusBarViewTouchHandler())
- mView.setHasCornerCutoutFetcher {
- statusBarContentInsetsProvider.currentRotationHasCornerCutout()
- }
- mView.setInsetsFetcher {
- statusBarContentInsetsProvider.getStatusBarContentInsetsForCurrentRotation()
+ statusBarContentInsetsProvider?.let {
+ mView.setHasCornerCutoutFetcher { it.currentRotationHasCornerCutout() }
+ mView.setInsetsFetcher { it.getStatusBarContentInsetsForCurrentRotation() }
}
mView.init(userChipViewModel)
}
@@ -393,7 +392,7 @@ private constructor(
configurationController,
statusOverlayHoverListenerFactory,
darkIconDispatcher,
- statusBarContentInsetsProviderStore.defaultDisplay,
+ statusBarContentInsetsProviderStore,
lazyStatusBarShadeDisplayPolicy,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 0f6c3069609e..be48c3d928f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -66,6 +66,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState;
import com.android.systemui.keyguard.shared.model.ScrimAlpha;
import com.android.systemui.keyguard.shared.model.TransitionState;
import com.android.systemui.keyguard.shared.model.TransitionStep;
+import com.android.systemui.keyguard.ui.transitions.BlurConfig;
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToGoneTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
import com.android.systemui.res.R;
@@ -258,6 +259,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
private int mScrimsVisibility;
private final TriConsumer<ScrimState, Float, GradientColors> mScrimStateListener;
private final LargeScreenShadeInterpolator mLargeScreenShadeInterpolator;
+ private final BlurConfig mBlurConfig;
private Consumer<Integer> mScrimVisibleListener;
private boolean mBlankScreen;
private boolean mScreenBlankingCallbackCalled;
@@ -339,9 +341,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
KeyguardTransitionInteractor keyguardTransitionInteractor,
KeyguardInteractor keyguardInteractor,
@Main CoroutineDispatcher mainDispatcher,
- LargeScreenShadeInterpolator largeScreenShadeInterpolator) {
+ LargeScreenShadeInterpolator largeScreenShadeInterpolator,
+ BlurConfig blurConfig) {
mScrimStateListener = lightBarController::setScrimState;
mLargeScreenShadeInterpolator = largeScreenShadeInterpolator;
+ mBlurConfig = blurConfig;
// All scrims default alpha need to match bouncer background alpha to make sure the
// transitions involving the bouncer are smooth and don't overshoot the bouncer alpha.
mDefaultScrimAlpha =
@@ -406,7 +410,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
final ScrimState[] states = ScrimState.values();
for (int i = 0; i < states.length; i++) {
- states[i].init(mScrimInFront, mScrimBehind, mDozeParameters, mDockManager);
+ states[i].init(mScrimInFront, mScrimBehind, mDozeParameters, mDockManager, mBlurConfig);
states[i].setScrimBehindAlphaKeyguard(mScrimBehindAlphaKeyguard);
states[i].setDefaultScrimAlpha(mDefaultScrimAlpha);
}
@@ -868,7 +872,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
* bounds instead.
*/
public void setClipsQsScrim(boolean clipScrim) {
- if (Flags.notificationShadeBlur()) {
+ if (Flags.notificationShadeBlur() || Flags.bouncerUiRevamp()) {
// Never clip scrims when blur is enabled, colors of UI elements are supposed to "add"
// up across the scrims.
mClipsQsScrim = false;
@@ -1210,6 +1214,12 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
dispatchBackScrimState(mScrimBehind.getViewAlpha());
}
+ if (Flags.bouncerUiRevamp()) {
+ // Blur the notification scrim as needed. The blur is needed only when we show the
+ // expanded shade behind the bouncer. Without it, the notification scrim outline is
+ // visible behind the bouncer.
+ mNotificationsScrim.setBlurRadius(mState.getNotifBlurRadius());
+ }
// We also want to hide FLAG_SHOW_WHEN_LOCKED activities under the scrim.
boolean hideFlagShowWhenLockedActivities =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 8170e6d91a0f..5f423cf35edd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -24,6 +24,7 @@ import android.graphics.Color;
import com.android.app.tracing.coroutines.TrackTracer;
import com.android.systemui.Flags;
import com.android.systemui.dock.DockManager;
+import com.android.systemui.keyguard.ui.transitions.BlurConfig;
import com.android.systemui.res.R;
import com.android.systemui.scrim.ScrimView;
import com.android.systemui.shade.ui.ShadeColors;
@@ -149,7 +150,14 @@ public enum ScrimState {
@Override
public void prepare(ScrimState previousState) {
if (Flags.bouncerUiRevamp()) {
- mBehindAlpha = 0f;
+ if (previousState == SHADE_LOCKED) {
+ mBehindAlpha = previousState.getBehindAlpha();
+ mNotifAlpha = previousState.getNotifAlpha();
+ mNotifBlurRadius = mBlurConfig.getMaxBlurRadiusPx();
+ } else {
+ mNotifAlpha = 0f;
+ mBehindAlpha = 0f;
+ }
mFrontAlpha = TRANSPARENT_BOUNCER_SCRIM_ALPHA;
mFrontTint = mSurfaceColor;
return;
@@ -395,6 +403,7 @@ public enum ScrimState {
DozeParameters mDozeParameters;
DockManager mDockManager;
boolean mDisplayRequiresBlanking;
+ protected BlurConfig mBlurConfig;
boolean mLaunchingAffordanceWithPreview;
boolean mOccludeAnimationPlaying;
boolean mWakeLockScreenSensorActive;
@@ -403,8 +412,12 @@ public enum ScrimState {
boolean mClipQsScrim;
int mBackgroundColor;
+ // This is needed to blur the scrim behind the scrimmed bouncer to avoid showing
+ // the notification section border
+ protected float mNotifBlurRadius = 0.0f;
+
public void init(ScrimView scrimInFront, ScrimView scrimBehind, DozeParameters dozeParameters,
- DockManager dockManager) {
+ DockManager dockManager, BlurConfig blurConfig) {
mBackgroundColor = scrimBehind.getContext().getColor(R.color.shade_scrim_background_dark);
mScrimInFront = scrimInFront;
mScrimBehind = scrimBehind;
@@ -412,6 +425,7 @@ public enum ScrimState {
mDozeParameters = dozeParameters;
mDockManager = dockManager;
mDisplayRequiresBlanking = dozeParameters.getDisplayNeedsBlanking();
+ mBlurConfig = blurConfig;
}
/** Prepare state for transition. */
@@ -518,4 +532,8 @@ public enum ScrimState {
public void setClipQsScrim(boolean clipsQsScrim) {
mClipQsScrim = clipsQsScrim;
}
+
+ public float getNotifBlurRadius() {
+ return mNotifBlurRadius;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTransitionAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTransitionAnimatorController.kt
index 705a11df83fc..e12b21eb2c4a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTransitionAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTransitionAnimatorController.kt
@@ -1,6 +1,7 @@
package com.android.systemui.statusbar.phone
import android.view.View
+import com.android.systemui.Flags
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.animation.TransitionAnimator
import com.android.systemui.animation.TransitionAnimator.Companion.getProgress
@@ -22,7 +23,7 @@ class StatusBarTransitionAnimatorController(
private val notificationShadeWindowController: NotificationShadeWindowController,
private val commandQueue: CommandQueue,
@DisplayId private val displayId: Int,
- private val isLaunchForActivity: Boolean = true
+ private val isLaunchForActivity: Boolean = true,
) : ActivityTransitionAnimator.Controller by delegate {
private var hideIconsDuringLaunchAnimation: Boolean = true
@@ -41,8 +42,16 @@ class StatusBarTransitionAnimatorController(
}
override fun onTransitionAnimationStart(isExpandingFullyAbove: Boolean) {
- delegate.onTransitionAnimationStart(isExpandingFullyAbove)
- shadeAnimationInteractor.setIsLaunchingActivity(true)
+ if (Flags.shadeLaunchAccessibility()) {
+ // We set this before calling the delegate to make sure that accessibility is disabled
+ // for the whole duration of the transition, so that we don't have stray TalkBack events
+ // once the animating view becomes invisible.
+ shadeAnimationInteractor.setIsLaunchingActivity(true)
+ delegate.onTransitionAnimationStart(isExpandingFullyAbove)
+ } else {
+ delegate.onTransitionAnimationStart(isExpandingFullyAbove)
+ shadeAnimationInteractor.setIsLaunchingActivity(true)
+ }
if (!isExpandingFullyAbove) {
shadeController.collapseWithDuration(
ActivityTransitionAnimator.TIMINGS.totalDuration.toInt()
@@ -59,7 +68,7 @@ class StatusBarTransitionAnimatorController(
override fun onTransitionAnimationProgress(
state: TransitionAnimator.State,
progress: Float,
- linearProgress: Float
+ linearProgress: Float,
) {
delegate.onTransitionAnimationProgress(state, progress, linearProgress)
val hideIcons =
@@ -67,7 +76,7 @@ class StatusBarTransitionAnimatorController(
ActivityTransitionAnimator.TIMINGS,
linearProgress,
ANIMATION_DELAY_ICON_FADE_IN,
- 100
+ 100,
) == 0.0f
if (hideIcons != hideIconsDuringLaunchAnimation) {
hideIconsDuringLaunchAnimation = hideIcons
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 39b434ad65f1..83b7c1818341 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt
@@ -16,7 +16,6 @@
package com.android.systemui.volume.dialog
-import android.app.Dialog
import android.content.Context
import android.graphics.PixelFormat
import android.os.Bundle
@@ -24,6 +23,7 @@ import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
+import androidx.activity.ComponentDialog
import com.android.app.tracing.coroutines.coroutineScopeTraced
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.lifecycle.repeatWhenAttached
@@ -40,7 +40,7 @@ constructor(
@Application context: Context,
private val componentFactory: VolumeDialogComponent.Factory,
private val visibilityInteractor: VolumeDialogVisibilityInteractor,
-) : Dialog(context, R.style.Theme_SystemUI_Dialog_Volume) {
+) : ComponentDialog(context, R.style.Theme_SystemUI_Dialog_Volume) {
init {
with(window!!) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt
index 8c018606ebda..e261ceebf33e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt
@@ -17,6 +17,8 @@
package com.android.systemui.volume.dialog.domain.interactor
import android.annotation.SuppressLint
+import android.media.AudioManager.RINGER_MODE_NORMAL
+import android.media.AudioManager.RINGER_MODE_SILENT
import android.os.Handler
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.plugins.VolumeDialogController
@@ -60,10 +62,10 @@ constructor(
awaitClose { volumeDialogController.removeCallback(producer) }
}
.buffer(capacity = BUFFER_CAPACITY, onBufferOverflow = BufferOverflow.DROP_OLDEST)
- .shareIn(replay = 0, scope = coroutineScope, started = SharingStarted.WhileSubscribed())
+ .shareIn(replay = 0, scope = coroutineScope, started = SharingStarted.Eagerly)
.onStart { emit(VolumeDialogEventModel.SubscribedToEvents) }
- private class VolumeDialogEventModelProducer(
+ private inner class VolumeDialogEventModelProducer(
private val scope: ProducerScope<VolumeDialogEventModel>
) : VolumeDialogController.Callbacks {
override fun onShowRequested(reason: Int, keyguardLocked: Boolean, lockTaskModeState: Int) {
@@ -93,14 +95,6 @@ constructor(
// Configuration change is never emitted by the VolumeDialogControllerImpl now.
override fun onConfigurationChanged() = Unit
- override fun onShowVibrateHint() {
- scope.trySend(VolumeDialogEventModel.ShowVibrateHint)
- }
-
- override fun onShowSilentHint() {
- scope.trySend(VolumeDialogEventModel.ShowSilentHint)
- }
-
override fun onScreenOff() {
scope.trySend(VolumeDialogEventModel.ScreenOff)
}
@@ -113,16 +107,6 @@ constructor(
scope.trySend(VolumeDialogEventModel.AccessibilityModeChanged(showA11yStream == true))
}
- // Captions button is remove from the Volume Dialog
- override fun onCaptionComponentStateChanged(
- isComponentEnabled: Boolean,
- fromTooltip: Boolean,
- ) = Unit
-
- // Captions button is remove from the Volume Dialog
- override fun onCaptionEnabledStateChanged(isEnabled: Boolean, checkBeforeSwitch: Boolean) =
- Unit
-
override fun onShowCsdWarning(csdWarning: Int, durationMs: Int) {
scope.trySend(
VolumeDialogEventModel.ShowCsdWarning(
@@ -135,5 +119,25 @@ constructor(
override fun onVolumeChangedFromKey() {
scope.trySend(VolumeDialogEventModel.VolumeChangedFromKey)
}
+
+ // This should've been handled in side the controller itself.
+ override fun onShowVibrateHint() {
+ volumeDialogController.setRingerMode(RINGER_MODE_SILENT, false)
+ }
+
+ // This should've been handled in side the controller itself.
+ override fun onShowSilentHint() {
+ volumeDialogController.setRingerMode(RINGER_MODE_NORMAL, false)
+ }
+
+ // Captions button is remove from the Volume Dialog
+ override fun onCaptionComponentStateChanged(
+ isComponentEnabled: Boolean,
+ fromTooltip: Boolean,
+ ) = Unit
+
+ // Captions button is remove from the Volume Dialog
+ override fun onCaptionEnabledStateChanged(isEnabled: Boolean, checkBeforeSwitch: Boolean) =
+ Unit
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogEventModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogEventModel.kt
index 9793d2be6b98..a0214dc957a4 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogEventModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogEventModel.kt
@@ -38,10 +38,6 @@ sealed interface VolumeDialogEventModel {
data class LayoutDirectionChanged(val layoutDirection: Int) : VolumeDialogEventModel
- data object ShowVibrateHint : VolumeDialogEventModel
-
- data object ShowSilentHint : VolumeDialogEventModel
-
data object ScreenOff : VolumeDialogEventModel
data class ShowSafetyWarning(val flags: Int) : VolumeDialogEventModel
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractor.kt
index 40719185e290..73f6236393b2 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractor.kt
@@ -47,7 +47,6 @@ constructor(
private val audioSystemRepository: AudioSystemRepository,
private val ringerFeedbackRepository: VolumeDialogRingerFeedbackRepository,
) {
-
val ringerModel: Flow<VolumeDialogRingerModel> =
volumeDialogStateInteractor.volumeDialogState
.mapNotNull { toRingerModel(it) }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt
index 940c79c78d76..577e47bb3b83 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt
@@ -18,7 +18,6 @@ package com.android.systemui.volume.dialog.sliders.dagger
import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSliderType
import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogOverscrollViewBinder
-import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSliderHapticsViewBinder
import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSliderViewBinder
import dagger.BindsInstance
import dagger.Subcomponent
@@ -33,8 +32,6 @@ interface VolumeDialogSliderComponent {
fun sliderViewBinder(): VolumeDialogSliderViewBinder
- fun sliderHapticsViewBinder(): VolumeDialogSliderHapticsViewBinder
-
fun overscrollViewBinder(): VolumeDialogOverscrollViewBinder
@Subcomponent.Factory
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/data/repository/VolumeDialogSliderTouchEventsRepository.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/data/repository/VolumeDialogSliderTouchEventsRepository.kt
index 82885d65c513..07954f850286 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/data/repository/VolumeDialogSliderTouchEventsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/data/repository/VolumeDialogSliderTouchEventsRepository.kt
@@ -16,8 +16,8 @@
package com.android.systemui.volume.dialog.sliders.data.repository
-import android.view.MotionEvent
import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope
+import com.android.systemui.volume.dialog.sliders.shared.model.SliderInputEvent
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -26,10 +26,11 @@ import kotlinx.coroutines.flow.filterNotNull
@VolumeDialogSliderScope
class VolumeDialogSliderTouchEventsRepository @Inject constructor() {
- private val mutableSliderTouchEvents: MutableStateFlow<MotionEvent?> = MutableStateFlow(null)
- val sliderTouchEvent: Flow<MotionEvent> = mutableSliderTouchEvents.filterNotNull()
+ private val mutableSliderTouchEvents: MutableStateFlow<SliderInputEvent.Touch?> =
+ MutableStateFlow(null)
+ val sliderTouchEvent: Flow<SliderInputEvent.Touch> = mutableSliderTouchEvents.filterNotNull()
- fun update(event: MotionEvent) {
- mutableSliderTouchEvents.tryEmit(MotionEvent.obtain(event))
+ fun update(touch: SliderInputEvent.Touch) {
+ mutableSliderTouchEvents.value = touch
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInputEventsInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInputEventsInteractor.kt
index c7b4184a9f2f..351832bd275a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInputEventsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInputEventsInteractor.kt
@@ -16,7 +16,6 @@
package com.android.systemui.volume.dialog.sliders.domain.interactor
-import android.view.MotionEvent
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogCallbacksInteractor
import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor
@@ -45,7 +44,7 @@ constructor(
val event: Flow<SliderInputEvent> =
merge(
- repository.sliderTouchEvent.map { SliderInputEvent.Touch(it) },
+ repository.sliderTouchEvent,
volumeDialogCallbacksInteractor.event
.filterIsInstance(VolumeDialogEventModel.VolumeChangedFromKey::class)
.map { SliderInputEvent.Button },
@@ -55,7 +54,7 @@ constructor(
event.onEach { visibilityInteractor.resetDismissTimeout() }.launchIn(coroutineScope)
}
- fun onTouchEvent(newEvent: MotionEvent) {
- repository.update(newEvent)
+ fun onTouchEvent(pointerEvent: SliderInputEvent.Touch) {
+ repository.update(pointerEvent)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/shared/model/SliderInputEvent.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/shared/model/SliderInputEvent.kt
index 37dbb4b3a81d..841730857d71 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/shared/model/SliderInputEvent.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/shared/model/SliderInputEvent.kt
@@ -16,12 +16,20 @@
package com.android.systemui.volume.dialog.sliders.shared.model
-import android.view.MotionEvent
-
/** Models input event happened on the Volume Slider */
sealed interface SliderInputEvent {
- data class Touch(val event: MotionEvent) : SliderInputEvent
+ interface Touch : SliderInputEvent {
+
+ val x: Float
+ val y: Float
+
+ data class Start(override val x: Float, override val y: Float) : Touch
+
+ data class Move(override val x: Float, override val y: Float) : Touch
+
+ data class End(override val x: Float, override val y: Float) : Touch
+ }
data object Button : SliderInputEvent
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogOverscrollViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogOverscrollViewBinder.kt
index 8109b50aa34a..38feb69aad7b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogOverscrollViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogOverscrollViewBinder.kt
@@ -20,11 +20,9 @@ import android.view.View
import androidx.dynamicanimation.animation.FloatValueHolder
import androidx.dynamicanimation.animation.SpringAnimation
import androidx.dynamicanimation.animation.SpringForce
-import com.android.systemui.res.R
import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope
import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogOverscrollViewModel
import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogOverscrollViewModel.OverscrollEventModel
-import com.google.android.material.slider.Slider
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.launchIn
@@ -51,10 +49,6 @@ constructor(private val viewModel: VolumeDialogOverscrollViewModel) {
)
.addUpdateListener { _, value, _ -> viewsToAnimate.setTranslationY(value) }
- view.requireViewById<Slider>(R.id.volume_dialog_slider).addOnChangeListener { s, value, _ ->
- viewModel.setSlider(value = value, min = s.valueFrom, max = s.valueTo)
- }
-
viewModel.overscrollEvent
.onEach { event ->
when (event) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderHapticsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderHapticsViewBinder.kt
deleted file mode 100644
index 5a7fbc6341f2..000000000000
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderHapticsViewBinder.kt
+++ /dev/null
@@ -1,82 +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.volume.dialog.sliders.ui
-
-import android.view.View
-import com.android.systemui.haptics.slider.HapticSlider
-import com.android.systemui.haptics.slider.HapticSliderPlugin
-import com.android.systemui.res.R
-import com.android.systemui.statusbar.VibratorHelper
-import com.android.systemui.util.time.SystemClock
-import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope
-import com.android.systemui.volume.dialog.sliders.shared.model.SliderInputEvent
-import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderInputEventsViewModel
-import com.google.android.material.slider.Slider
-import com.google.android.msdl.domain.MSDLPlayer
-import javax.inject.Inject
-import kotlin.math.roundToInt
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-
-@VolumeDialogSliderScope
-class VolumeDialogSliderHapticsViewBinder
-@Inject
-constructor(
- private val inputEventsViewModel: VolumeDialogSliderInputEventsViewModel,
- private val vibratorHelper: VibratorHelper,
- private val msdlPlayer: MSDLPlayer,
- private val systemClock: SystemClock,
-) {
-
- fun CoroutineScope.bind(view: View) {
- val sliderView = view.requireViewById<Slider>(R.id.volume_dialog_slider)
- val hapticSliderPlugin =
- HapticSliderPlugin(
- slider = HapticSlider.Slider(sliderView),
- vibratorHelper = vibratorHelper,
- msdlPlayer = msdlPlayer,
- systemClock = systemClock,
- )
- hapticSliderPlugin.startInScope(this)
-
- sliderView.addOnChangeListener { _, value, fromUser ->
- hapticSliderPlugin.onProgressChanged(value.roundToInt(), fromUser)
- }
- sliderView.addOnSliderTouchListener(
- object : Slider.OnSliderTouchListener {
-
- override fun onStartTrackingTouch(slider: Slider) {
- hapticSliderPlugin.onStartTrackingTouch()
- }
-
- override fun onStopTrackingTouch(slider: Slider) {
- hapticSliderPlugin.onStopTrackingTouch()
- }
- }
- )
-
- inputEventsViewModel.event
- .onEach {
- when (it) {
- is SliderInputEvent.Button -> hapticSliderPlugin.onKeyDown()
- is SliderInputEvent.Touch -> hapticSliderPlugin.onTouchEvent(it.event)
- }
- }
- .launchIn(this)
- }
-}
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 d40302408dd6..21a392776235 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
@@ -16,96 +16,211 @@
package com.android.systemui.volume.dialog.sliders.ui
-import android.annotation.SuppressLint
+import android.graphics.drawable.Drawable
import android.view.View
-import androidx.dynamicanimation.animation.FloatPropertyCompat
-import androidx.dynamicanimation.animation.SpringAnimation
-import androidx.dynamicanimation.animation.SpringForce
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.SliderDefaults
+import androidx.compose.material3.SliderState
+import androidx.compose.material3.VerticalSlider
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshotFlow
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.compose.ui.graphics.painter.DrawablePainter
+import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel
+import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.res.R
import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope
-import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderInputEventsViewModel
-import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderStateModel
+import com.android.systemui.volume.dialog.sliders.ui.compose.VolumeDialogSliderTrack
+import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogOverscrollViewModel
import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderViewModel
-import com.google.android.material.slider.Slider
-import com.google.android.material.slider.Slider.OnSliderTouchListener
+import com.android.systemui.volume.haptics.ui.VolumeHapticsConfigsProvider
import javax.inject.Inject
+import kotlin.math.round
import kotlin.math.roundToInt
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.currentCoroutineContext
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.isActive
@VolumeDialogSliderScope
class VolumeDialogSliderViewBinder
@Inject
constructor(
private val viewModel: VolumeDialogSliderViewModel,
- private val inputViewModel: VolumeDialogSliderInputEventsViewModel,
+ private val overscrollViewModel: VolumeDialogOverscrollViewModel,
+ private val hapticsViewModelFactory: SliderHapticsViewModel.Factory,
) {
+ fun bind(view: View) {
+ val sliderComposeView: ComposeView = view.requireViewById(R.id.volume_dialog_slider)
+ sliderComposeView.setContent {
+ VolumeDialogSlider(
+ viewModel = viewModel,
+ overscrollViewModel = overscrollViewModel,
+ hapticsViewModelFactory =
+ if (com.android.systemui.Flags.hapticsForComposeSliders()) {
+ hapticsViewModelFactory
+ } else {
+ null
+ },
+ )
+ }
+ }
+}
- private val sliderValueProperty =
- object : FloatPropertyCompat<Slider>("value") {
- override fun getValue(slider: Slider): Float = slider.value
+@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
+@Composable
+private fun VolumeDialogSlider(
+ viewModel: VolumeDialogSliderViewModel,
+ overscrollViewModel: VolumeDialogOverscrollViewModel,
+ hapticsViewModelFactory: SliderHapticsViewModel.Factory?,
+ modifier: Modifier = Modifier,
+) {
+
+ val colors =
+ SliderDefaults.colors(
+ thumbColor = MaterialTheme.colorScheme.primary,
+ activeTickColor = MaterialTheme.colorScheme.surfaceContainerHighest,
+ inactiveTickColor = MaterialTheme.colorScheme.primary,
+ activeTrackColor = MaterialTheme.colorScheme.primary,
+ inactiveTrackColor = MaterialTheme.colorScheme.surfaceContainerHighest,
+ )
+ val collectedSliderState by viewModel.state.collectAsStateWithLifecycle(null)
+ val sliderState = collectedSliderState ?: return
- override fun setValue(slider: Slider, value: Float) {
- slider.value = value
+ val interactionSource = remember { MutableInteractionSource() }
+ val hapticsViewModel: SliderHapticsViewModel? =
+ hapticsViewModelFactory?.let {
+ rememberViewModel(traceName = "SliderHapticsViewModel") {
+ it.create(
+ interactionSource,
+ sliderState.valueRange,
+ Orientation.Vertical,
+ VolumeHapticsConfigsProvider.sliderHapticFeedbackConfig(sliderState.valueRange),
+ VolumeHapticsConfigsProvider.seekableSliderTrackerConfig,
+ )
}
}
- private val springForce =
- SpringForce().apply {
- stiffness = SpringForce.STIFFNESS_MEDIUM
- dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY
- }
- @SuppressLint("ClickableViewAccessibility")
- fun CoroutineScope.bind(view: View) {
- var isInitialUpdate = true
- val sliderView: Slider = view.requireViewById(R.id.volume_dialog_slider)
- val animation = SpringAnimation(sliderView, sliderValueProperty)
- animation.spring = springForce
- sliderView.setOnTouchListener { _, event ->
- inputViewModel.onTouchEvent(event)
- false
- }
- sliderView.addOnChangeListener { _, value, fromUser ->
- viewModel.setStreamVolume(value.roundToInt(), fromUser)
+ val state =
+ remember(sliderState.valueRange) {
+ SliderState(
+ value = sliderState.value,
+ valueRange = sliderState.valueRange,
+ steps =
+ (sliderState.valueRange.endInclusive - sliderState.valueRange.start - 1)
+ .toInt(),
+ )
+ .apply {
+ onValueChangeFinished = {
+ viewModel.onStreamChangeFinished(value.roundToInt())
+ hapticsViewModel?.onValueChangeEnded()
+ }
+ setOnValueChangeListener {
+ value = it
+ hapticsViewModel?.addVelocityDataPoint(it)
+ overscrollViewModel.setSlider(
+ value = value,
+ min = valueRange.start,
+ max = valueRange.endInclusive,
+ )
+ viewModel.setStreamVolume(it, true)
+ }
+ }
}
- sliderView.addOnSliderTouchListener(
- object : OnSliderTouchListener {
- override fun onStartTrackingTouch(slider: Slider) {}
+ var lastDiscreteStep by remember { mutableFloatStateOf(round(sliderState.value)) }
+ LaunchedEffect(sliderState.value) {
+ state.value = sliderState.value
+ snapshotFlow { sliderState.value }
+ .map { round(it) }
+ .filter { it != lastDiscreteStep }
+ .distinctUntilChanged()
+ .collect { discreteStep ->
+ lastDiscreteStep = discreteStep
+ hapticsViewModel?.onValueChange(discreteStep)
+ }
+ }
- override fun onStopTrackingTouch(slider: Slider) {
- viewModel.onStreamChangeFinished(slider.value.roundToInt())
+ VerticalSlider(
+ state = state,
+ enabled = !sliderState.isDisabled,
+ reverseDirection = true,
+ colors = colors,
+ interactionSource = interactionSource,
+ modifier =
+ modifier.pointerInput(Unit) {
+ coroutineScope {
+ val currentContext = currentCoroutineContext()
+ awaitPointerEventScope {
+ while (currentContext.isActive) {
+ viewModel.onTouchEvent(awaitPointerEvent())
+ }
+ }
}
- }
- )
+ },
+ track = {
+ VolumeDialogSliderTrack(
+ state,
+ colors = colors,
+ isEnabled = !sliderState.isDisabled,
+ activeTrackEndIcon = { iconsState ->
+ VolumeIcon(sliderState.icon, iconsState.isActiveTrackEndIconVisible)
+ },
+ inactiveTrackEndIcon = { iconsState ->
+ VolumeIcon(sliderState.icon, !iconsState.isActiveTrackEndIconVisible)
+ },
+ )
+ },
+ )
+}
- viewModel.isDisabledByZenMode.onEach { sliderView.isEnabled = !it }.launchIn(this)
- viewModel.state
- .onEach {
- sliderView.setModel(it, animation, isInitialUpdate)
- isInitialUpdate = false
- }
- .launchIn(this)
+@Composable
+private fun BoxScope.VolumeIcon(
+ drawable: Drawable,
+ isVisible: Boolean,
+ modifier: Modifier = Modifier,
+) {
+ AnimatedVisibility(
+ visible = isVisible,
+ enter = fadeIn(animationSpec = tween(durationMillis = 50)),
+ exit = fadeOut(animationSpec = tween(durationMillis = 50)),
+ modifier = modifier.align(Alignment.Center).size(40.dp).padding(10.dp),
+ ) {
+ Icon(painter = DrawablePainter(drawable), contentDescription = null)
}
+}
- @SuppressLint("UseCompatLoadingForDrawables")
- private fun Slider.setModel(
- model: VolumeDialogSliderStateModel,
- animation: SpringAnimation,
- isInitialUpdate: Boolean,
- ) {
- valueFrom = model.minValue
- animation.setMinValue(model.minValue)
- valueTo = model.maxValue
- animation.setMaxValue(model.maxValue)
- // coerce the current value to the new value range before animating it. This prevents
- // animating from the value that is outside of current [valueFrom, valueTo].
- value = value.coerceIn(valueFrom, valueTo)
- trackIconActiveStart = model.icon
- if (isInitialUpdate) {
- value = model.value
- } else {
- animation.animateToFinalPosition(model.value)
- }
+@OptIn(ExperimentalMaterial3Api::class)
+fun SliderState.setOnValueChangeListener(onValueChange: ((Float) -> Unit)?) {
+ with(javaClass.getDeclaredField("onValueChange")) {
+ val oldIsAccessible = isAccessible
+ AutoCloseable { isAccessible = oldIsAccessible }
+ .use {
+ isAccessible = true
+ set(this@setOnValueChangeListener, onValueChange)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
index 75d427acc05b..c66955a0c187 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
@@ -71,7 +71,6 @@ constructor(private val viewModel: VolumeDialogSlidersViewModel) {
viewsToAnimate: Array<View>,
) {
with(component.sliderViewBinder()) { bind(sliderContainer) }
- with(component.sliderHapticsViewBinder()) { bind(sliderContainer) }
with(component.overscrollViewBinder()) { bind(sliderContainer, viewsToAnimate) }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/compose/VolumeDialogSliderTrack.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/compose/VolumeDialogSliderTrack.kt
new file mode 100644
index 000000000000..1dd9ddac79be
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/compose/VolumeDialogSliderTrack.kt
@@ -0,0 +1,347 @@
+/*
+ * 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.sliders.ui.compose
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.width
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
+import androidx.compose.material3.LocalContentColor
+import androidx.compose.material3.SliderColors
+import androidx.compose.material3.SliderDefaults
+import androidx.compose.material3.SliderState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasurePolicy
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.layout.Placeable
+import androidx.compose.ui.layout.layoutId
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.util.fastFilter
+import androidx.compose.ui.util.fastFirst
+import kotlin.math.min
+
+@Composable
+@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
+fun VolumeDialogSliderTrack(
+ sliderState: SliderState,
+ colors: SliderColors,
+ isEnabled: Boolean,
+ modifier: Modifier = Modifier,
+ thumbTrackGapSize: Dp = 6.dp,
+ trackCornerSize: Dp = 12.dp,
+ trackInsideCornerSize: Dp = 2.dp,
+ trackSize: Dp = 40.dp,
+ activeTrackStartIcon: (@Composable BoxScope.(iconsState: SliderIconsState) -> Unit)? = null,
+ activeTrackEndIcon: (@Composable BoxScope.(iconsState: SliderIconsState) -> Unit)? = null,
+ inactiveTrackStartIcon: (@Composable BoxScope.(iconsState: SliderIconsState) -> Unit)? = null,
+ inactiveTrackEndIcon: (@Composable BoxScope.(iconsState: SliderIconsState) -> Unit)? = null,
+) {
+ val measurePolicy = remember(sliderState) { TrackMeasurePolicy(sliderState) }
+ Layout(
+ measurePolicy = measurePolicy,
+ content = {
+ SliderDefaults.Track(
+ sliderState = sliderState,
+ colors = colors,
+ enabled = isEnabled,
+ trackCornerSize = trackCornerSize,
+ trackInsideCornerSize = trackInsideCornerSize,
+ drawStopIndicator = null,
+ thumbTrackGapSize = thumbTrackGapSize,
+ drawTick = { _, _ -> },
+ modifier = Modifier.width(trackSize).layoutId(Contents.Track),
+ )
+
+ TrackIcon(
+ icon = activeTrackStartIcon,
+ contentsId = Contents.Active.TrackStartIcon,
+ isEnabled = isEnabled,
+ colors = colors,
+ state = measurePolicy,
+ )
+ TrackIcon(
+ icon = activeTrackEndIcon,
+ contentsId = Contents.Active.TrackEndIcon,
+ isEnabled = isEnabled,
+ colors = colors,
+ state = measurePolicy,
+ )
+ TrackIcon(
+ icon = inactiveTrackStartIcon,
+ contentsId = Contents.Inactive.TrackStartIcon,
+ isEnabled = isEnabled,
+ colors = colors,
+ state = measurePolicy,
+ )
+ TrackIcon(
+ icon = inactiveTrackEndIcon,
+ contentsId = Contents.Inactive.TrackEndIcon,
+ isEnabled = isEnabled,
+ colors = colors,
+ state = measurePolicy,
+ )
+ },
+ modifier = modifier,
+ )
+}
+
+@Composable
+private fun TrackIcon(
+ icon: (@Composable BoxScope.(sliderIconsState: SliderIconsState) -> Unit)?,
+ isEnabled: Boolean,
+ contentsId: Contents,
+ state: SliderIconsState,
+ colors: SliderColors,
+ modifier: Modifier = Modifier,
+) {
+ icon ?: return
+ Box(modifier = modifier.layoutId(contentsId).fillMaxSize()) {
+ CompositionLocalProvider(
+ LocalContentColor provides contentsId.getColor(colors, isEnabled)
+ ) {
+ icon(state)
+ }
+ }
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+private class TrackMeasurePolicy(private val sliderState: SliderState) :
+ MeasurePolicy, SliderIconsState {
+
+ private val isVisible: Map<Contents, MutableState<Boolean>> =
+ mutableMapOf(
+ Contents.Active.TrackStartIcon to mutableStateOf(false),
+ Contents.Active.TrackEndIcon to mutableStateOf(false),
+ Contents.Inactive.TrackStartIcon to mutableStateOf(false),
+ Contents.Inactive.TrackEndIcon to mutableStateOf(false),
+ )
+
+ override val isActiveTrackStartIconVisible: Boolean
+ get() = isVisible.getValue(Contents.Active.TrackStartIcon).value
+
+ override val isActiveTrackEndIconVisible: Boolean
+ get() = isVisible.getValue(Contents.Active.TrackEndIcon).value
+
+ override val isInactiveTrackStartIconVisible: Boolean
+ get() = isVisible.getValue(Contents.Inactive.TrackStartIcon).value
+
+ override val isInactiveTrackEndIconVisible: Boolean
+ get() = isVisible.getValue(Contents.Inactive.TrackEndIcon).value
+
+ override fun MeasureScope.measure(
+ measurables: List<Measurable>,
+ constraints: Constraints,
+ ): MeasureResult {
+ val track = measurables.fastFirst { it.layoutId == Contents.Track }.measure(constraints)
+
+ val iconSize = min(track.width, track.height)
+ val iconConstraints = constraints.copy(maxWidth = iconSize, maxHeight = iconSize)
+
+ val icons =
+ measurables
+ .fastFilter { it.layoutId != Contents.Track }
+ .associateBy(
+ keySelector = { it.layoutId as Contents },
+ valueTransform = { it.measure(iconConstraints) },
+ )
+
+ return layout(track.width, track.height) {
+ with(Contents.Track) {
+ performPlacing(
+ placeable = track,
+ width = track.width,
+ height = track.height,
+ sliderState = sliderState,
+ )
+ }
+
+ for (iconLayoutId in icons.keys) {
+ with(iconLayoutId) {
+ performPlacing(
+ placeable = icons.getValue(iconLayoutId),
+ width = track.width,
+ height = track.height,
+ sliderState = sliderState,
+ )
+
+ isVisible.getValue(iconLayoutId).value =
+ isVisible(
+ placeable = icons.getValue(iconLayoutId),
+ width = track.width,
+ height = track.height,
+ sliderState = sliderState,
+ )
+ }
+ }
+ }
+ }
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+private sealed interface Contents {
+
+ data object Track : Contents {
+ override fun Placeable.PlacementScope.performPlacing(
+ placeable: Placeable,
+ width: Int,
+ height: Int,
+ sliderState: SliderState,
+ ) = placeable.place(x = 0, y = 0)
+
+ override fun isVisible(
+ placeable: Placeable,
+ width: Int,
+ height: Int,
+ sliderState: SliderState,
+ ) = true
+
+ override fun getColor(sliderColors: SliderColors, isEnabled: Boolean): Color =
+ error("Unsupported")
+ }
+
+ interface Active : Contents {
+ override fun getColor(sliderColors: SliderColors, isEnabled: Boolean): Color {
+ return if (isEnabled) {
+ sliderColors.activeTickColor
+ } else {
+ sliderColors.disabledActiveTickColor
+ }
+ }
+
+ data object TrackStartIcon : Active {
+ override fun Placeable.PlacementScope.performPlacing(
+ placeable: Placeable,
+ width: Int,
+ height: Int,
+ sliderState: SliderState,
+ ) =
+ placeable.place(
+ x = 0,
+ y = (height * (1 - sliderState.coercedValueAsFraction)).toInt(),
+ )
+
+ override fun isVisible(
+ placeable: Placeable,
+ width: Int,
+ height: Int,
+ sliderState: SliderState,
+ ): Boolean = (height * (sliderState.coercedValueAsFraction)).toInt() > placeable.height
+ }
+
+ data object TrackEndIcon : Active {
+ override fun Placeable.PlacementScope.performPlacing(
+ placeable: Placeable,
+ width: Int,
+ height: Int,
+ sliderState: SliderState,
+ ) = placeable.place(x = 0, y = (height - placeable.height))
+
+ override fun isVisible(
+ placeable: Placeable,
+ width: Int,
+ height: Int,
+ sliderState: SliderState,
+ ): Boolean = (height * (sliderState.coercedValueAsFraction)).toInt() > placeable.height
+ }
+ }
+
+ interface Inactive : Contents {
+
+ override fun getColor(sliderColors: SliderColors, isEnabled: Boolean): Color {
+ return if (isEnabled) {
+ sliderColors.inactiveTickColor
+ } else {
+ sliderColors.disabledInactiveTickColor
+ }
+ }
+
+ data object TrackStartIcon : Inactive {
+ override fun Placeable.PlacementScope.performPlacing(
+ placeable: Placeable,
+ width: Int,
+ height: Int,
+ sliderState: SliderState,
+ ) {
+ placeable.place(x = 0, y = 0)
+ }
+
+ override fun isVisible(
+ placeable: Placeable,
+ width: Int,
+ height: Int,
+ sliderState: SliderState,
+ ): Boolean =
+ (height * (1 - sliderState.coercedValueAsFraction)).toInt() > placeable.height
+ }
+
+ data object TrackEndIcon : Inactive {
+ override fun Placeable.PlacementScope.performPlacing(
+ placeable: Placeable,
+ width: Int,
+ height: Int,
+ sliderState: SliderState,
+ ) {
+ placeable.place(
+ x = 0,
+ y =
+ (height * (1 - sliderState.coercedValueAsFraction)).toInt() -
+ placeable.height,
+ )
+ }
+
+ override fun isVisible(
+ placeable: Placeable,
+ width: Int,
+ height: Int,
+ sliderState: SliderState,
+ ): Boolean =
+ (height * (1 - sliderState.coercedValueAsFraction)).toInt() > placeable.height
+ }
+ }
+
+ fun Placeable.PlacementScope.performPlacing(
+ placeable: Placeable,
+ width: Int,
+ height: Int,
+ sliderState: SliderState,
+ )
+
+ fun isVisible(placeable: Placeable, width: Int, height: Int, sliderState: SliderState): Boolean
+
+ fun getColor(sliderColors: SliderColors, isEnabled: Boolean): Color
+}
+
+/** Provides visibility state for each of the Slider's icons. */
+interface SliderIconsState {
+ val isActiveTrackStartIconVisible: Boolean
+ val isActiveTrackEndIconVisible: Boolean
+ val isInactiveTrackStartIconVisible: Boolean
+ val isInactiveTrackEndIconVisible: Boolean
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogOverscrollViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogOverscrollViewModel.kt
index 0d41860d9f57..0fdf5d6266d0 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogOverscrollViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogOverscrollViewModel.kt
@@ -95,18 +95,17 @@ constructor(
private fun overscrollEvents(direction: Float): Flow<OverscrollEventModel> {
var startPosition: Float? = null
return inputEventsInteractor.event
- .mapNotNull { (it as? SliderInputEvent.Touch)?.event }
+ .mapNotNull { it as? SliderInputEvent.Touch }
.transform { touchEvent ->
// Skip events from inside the slider bounds for the case when the user adjusts
- // slider
- // towards max when the slider is already on max value.
- if (touchEvent.isFinalEvent()) {
+ // slider towards max when the slider is already on max value.
+ if (touchEvent is SliderInputEvent.Touch.End) {
startPosition = null
emit(OverscrollEventModel.Animate(0f))
return@transform
}
val currentStartPosition = startPosition
- val newPosition: Float = touchEvent.rawY
+ val newPosition: Float = touchEvent.y
if (currentStartPosition == null) {
startPosition = newPosition
} else {
@@ -126,11 +125,6 @@ constructor(
}
}
- /** @return true when the [MotionEvent] indicates the end of the gesture. */
- private fun MotionEvent.isFinalEvent(): Boolean {
- return actionMasked == MotionEvent.ACTION_UP || actionMasked == MotionEvent.ACTION_CANCEL
- }
-
/** Models overscroll event */
sealed interface OverscrollEventModel {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderInputEventsViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderInputEventsViewModel.kt
deleted file mode 100644
index 755776ac9723..000000000000
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderInputEventsViewModel.kt
+++ /dev/null
@@ -1,43 +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.volume.dialog.sliders.ui.viewmodel
-
-import android.view.MotionEvent
-import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
-import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope
-import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSliderInputEventsInteractor
-import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.filterNotNull
-import kotlinx.coroutines.flow.stateIn
-
-@VolumeDialogSliderScope
-class VolumeDialogSliderInputEventsViewModel
-@Inject
-constructor(
- @VolumeDialog private val coroutineScope: CoroutineScope,
- private val interactor: VolumeDialogSliderInputEventsInteractor,
-) {
-
- val event =
- interactor.event.stateIn(coroutineScope, SharingStarted.Eagerly, null).filterNotNull()
-
- fun onTouchEvent(event: MotionEvent) {
- interactor.onTouchEvent(event)
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt
index 8df9e788905c..b01046b377b0 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt
@@ -20,17 +20,20 @@ import android.graphics.drawable.Drawable
import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
data class VolumeDialogSliderStateModel(
- val minValue: Float,
- val maxValue: Float,
val value: Float,
+ val isDisabled: Boolean,
+ val valueRange: ClosedFloatingPointRange<Float>,
val icon: Drawable,
)
-fun VolumeDialogStreamModel.toStateModel(icon: Drawable): VolumeDialogSliderStateModel {
+fun VolumeDialogStreamModel.toStateModel(
+ isDisabled: Boolean,
+ icon: Drawable,
+): VolumeDialogSliderStateModel {
return VolumeDialogSliderStateModel(
- minValue = levelMin.toFloat(),
value = level.toFloat(),
- maxValue = levelMax.toFloat(),
+ isDisabled = isDisabled,
+ valueRange = levelMin.toFloat()..levelMax.toFloat(),
icon = icon,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
index a752f1f78e74..e89d5ab53560 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
@@ -16,6 +16,9 @@
package com.android.systemui.volume.dialog.sliders.ui.viewmodel
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.pointer.PointerEvent
+import androidx.compose.ui.input.pointer.PointerEventType
import com.android.systemui.util.time.SystemClock
import com.android.systemui.volume.Events
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
@@ -23,20 +26,23 @@ import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibili
import com.android.systemui.volume.dialog.shared.VolumeDialogLogger
import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope
+import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSliderInputEventsInteractor
import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSliderInteractor
import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSliderType
+import com.android.systemui.volume.dialog.sliders.shared.model.SliderInputEvent
import javax.inject.Inject
+import kotlin.math.roundToInt
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.stateIn
@@ -62,6 +68,7 @@ constructor(
private val visibilityInteractor: VolumeDialogVisibilityInteractor,
@VolumeDialog private val coroutineScope: CoroutineScope,
private val volumeDialogSliderIconProvider: VolumeDialogSliderIconProvider,
+ private val inputEventsInteractor: VolumeDialogSliderInputEventsInteractor,
private val systemClock: SystemClock,
private val logger: VolumeDialogLogger,
) {
@@ -77,11 +84,12 @@ constructor(
.stateIn(coroutineScope, SharingStarted.Eagerly, null)
.filterNotNull()
- val isDisabledByZenMode: Flow<Boolean> = interactor.isDisabledByZenMode
val state: Flow<VolumeDialogSliderStateModel> =
- model
- .flatMapLatest { streamModel ->
- with(streamModel) {
+ combine(
+ interactor.isDisabledByZenMode,
+ model,
+ model.flatMapLatest { streamModel ->
+ with(streamModel) {
val isMuted = muteSupported && muted
when (sliderType) {
is VolumeDialogSliderType.Stream ->
@@ -101,7 +109,9 @@ constructor(
}
}
}
- .map { icon -> streamModel.toStateModel(icon) }
+ },
+ ) { isDisabledByZenMode, model, icon ->
+ model.toStateModel(icon = icon, isDisabled = isDisabledByZenMode)
}
.stateIn(coroutineScope, SharingStarted.Eagerly, null)
.filterNotNull()
@@ -116,11 +126,14 @@ constructor(
.launchIn(coroutineScope)
}
- fun setStreamVolume(volume: Int, fromUser: Boolean) {
+ fun setStreamVolume(volume: Float, fromUser: Boolean) {
if (fromUser) {
visibilityInteractor.resetDismissTimeout()
userVolumeUpdates.value =
- VolumeUpdate(newVolumeLevel = volume, timestampMillis = getTimestampMillis())
+ VolumeUpdate(
+ newVolumeLevel = volume.roundToInt(),
+ timestampMillis = getTimestampMillis(),
+ )
}
}
@@ -128,6 +141,28 @@ constructor(
logger.onVolumeSliderAdjustmentFinished(volume = volume, stream = sliderType.audioStream)
}
+ fun onTouchEvent(pointerEvent: PointerEvent) {
+ val position: Offset = pointerEvent.changes.first().position
+ when (pointerEvent.type) {
+ PointerEventType.Press ->
+ inputEventsInteractor.onTouchEvent(
+ SliderInputEvent.Touch.Start(position.x, position.y)
+ )
+ PointerEventType.Move ->
+ inputEventsInteractor.onTouchEvent(
+ SliderInputEvent.Touch.Move(position.x, position.y)
+ )
+ PointerEventType.Scroll ->
+ inputEventsInteractor.onTouchEvent(
+ SliderInputEvent.Touch.Move(position.x, position.y)
+ )
+ PointerEventType.Release ->
+ inputEventsInteractor.onTouchEvent(
+ SliderInputEvent.Touch.End(position.x, position.y)
+ )
+ }
+ }
+
private fun getTimestampMillis(): Long = systemClock.uptimeMillis()
private data class VolumeUpdate(val newVolumeLevel: Int, val timestampMillis: Long)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/haptics/ui/VolumeHapticsConfigsProvider.kt b/packages/SystemUI/src/com/android/systemui/volume/haptics/ui/VolumeHapticsConfigsProvider.kt
new file mode 100644
index 000000000000..92e9bf2d1ffc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/haptics/ui/VolumeHapticsConfigsProvider.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.haptics.ui
+
+import com.android.systemui.haptics.slider.SeekableSliderTrackerConfig
+import com.android.systemui.haptics.slider.SliderHapticFeedbackConfig
+
+object VolumeHapticsConfigsProvider {
+
+ fun sliderHapticFeedbackConfig(
+ valueRange: ClosedFloatingPointRange<Float>
+ ): SliderHapticFeedbackConfig {
+ val sliderStepSize = 1f / (valueRange.endInclusive - valueRange.start)
+ return SliderHapticFeedbackConfig(
+ lowerBookendScale = 0.2f,
+ progressBasedDragMinScale = 0.2f,
+ progressBasedDragMaxScale = 0.5f,
+ deltaProgressForDragThreshold = 0f,
+ additionalVelocityMaxBump = 0.2f,
+ maxVelocityToScale = 0.1f, /* slider progress(from 0 to 1) per sec */
+ sliderStepSize = sliderStepSize,
+ )
+ }
+
+ val seekableSliderTrackerConfig =
+ SeekableSliderTrackerConfig(lowerBookendThreshold = 0f, upperBookendThreshold = 1f)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt
index 8487ee751948..ec74f4f47bc9 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt
@@ -36,4 +36,5 @@ class NoopWallpaperRepository @Inject constructor() : WallpaperRepository {
override val wallpaperInfo: StateFlow<WallpaperInfo?> = MutableStateFlow(null).asStateFlow()
override val wallpaperSupportsAmbientMode = flowOf(false)
override var rootView: View? = null
+ override val shouldSendFocalArea: StateFlow<Boolean> = MutableStateFlow(false).asStateFlow()
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt
index ed43f8323c31..9794c619041e 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt
@@ -31,7 +31,6 @@ import com.android.systemui.Flags
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.keyguard.data.repository.KeyguardClockRepository
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.shared.Flags.ambientAod
import com.android.systemui.user.data.model.SelectedUserModel
@@ -45,6 +44,7 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.SharingStarted.Companion.WhileSubscribed
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
@@ -65,6 +65,9 @@ interface WallpaperRepository {
/** Set rootView to get its windowToken afterwards */
var rootView: View?
+
+ /** when we use magic portrait wallpapers, we should always get its bounds from keyguard */
+ val shouldSendFocalArea: StateFlow<Boolean>
}
@SysUISingleton
@@ -76,7 +79,6 @@ constructor(
broadcastDispatcher: BroadcastDispatcher,
userRepository: UserRepository,
keyguardRepository: KeyguardRepository,
- keyguardClockRepository: KeyguardClockRepository,
private val wallpaperManager: WallpaperManager,
context: Context,
) : WallpaperRepository {
@@ -97,27 +99,7 @@ constructor(
// Only update the wallpaper status once the user selection has finished.
.filter { it.selectionStatus == SelectionStatus.SELECTION_COMPLETE }
- /** The bottom of notification stack respect to the top of screen. */
- private val notificationStackAbsoluteBottom: StateFlow<Float> =
- keyguardRepository.notificationStackAbsoluteBottom
-
- /** The top of shortcut respect to the top of screen. */
- private val shortcutAbsoluteTop: StateFlow<Float> = keyguardRepository.shortcutAbsoluteTop
-
- /**
- * The top of notification stack to give a default state of lockscreen remaining space for
- * states with notifications to compare with. It's the bottom of smartspace date and weather
- * smartspace in small clock state, plus proper bottom margin.
- */
- private val notificationStackDefaultTop = keyguardClockRepository.notificationDefaultTop
@VisibleForTesting var sendLockscreenLayoutJob: Job? = null
- private val lockscreenRemainingSpaceWithNotification: Flow<Triple<Float, Float, Float>> =
- combine(
- notificationStackAbsoluteBottom,
- notificationStackDefaultTop,
- shortcutAbsoluteTop,
- ::Triple,
- )
override val wallpaperInfo: StateFlow<WallpaperInfo?> =
if (!wallpaperManager.isWallpaperSupported) {
@@ -140,15 +122,16 @@ constructor(
override var rootView: View? = null
- val shouldSendNotificationLayout =
+ override val shouldSendFocalArea =
wallpaperInfo
.map {
- val shouldSendNotificationLayout = shouldSendNotificationLayout(it)
+ val shouldSendNotificationLayout =
+ it?.component?.className == MAGIC_PORTRAIT_CLASSNAME
if (shouldSendNotificationLayout) {
sendLockscreenLayoutJob =
scope.launch {
- lockscreenRemainingSpaceWithNotification.collect {
- (notificationBottom, notificationDefaultTop, shortcutTop) ->
+ keyguardRepository.wallpaperFocalAreaBounds.collect {
+ wallpaperFocalAreaBounds ->
wallpaperManager.sendWallpaperCommand(
/* windowToken = */ rootView?.windowToken,
/* action = */ WallpaperManager
@@ -157,14 +140,22 @@ constructor(
/* y = */ 0,
/* z = */ 0,
/* extras = */ Bundle().apply {
- putFloat("screenLeft", 0F)
- putFloat("smartspaceBottom", notificationDefaultTop)
- putFloat("notificationBottom", notificationBottom)
putFloat(
- "screenRight",
- context.resources.displayMetrics.widthPixels.toFloat(),
+ "wallpaperFocalAreaLeft",
+ wallpaperFocalAreaBounds.left,
+ )
+ putFloat(
+ "wallpaperFocalAreaRight",
+ wallpaperFocalAreaBounds.right,
+ )
+ putFloat(
+ "wallpaperFocalAreaTop",
+ wallpaperFocalAreaBounds.top,
+ )
+ putFloat(
+ "wallpaperFocalAreaBottom",
+ wallpaperFocalAreaBounds.bottom,
)
- putFloat("shortCutTop", shortcutTop)
},
)
}
@@ -176,10 +167,9 @@ constructor(
}
.stateIn(
scope,
- // Always be listening for wallpaper changes.
- if (Flags.magicPortraitWallpapers()) SharingStarted.Eagerly
- else SharingStarted.Lazily,
- initialValue = false,
+ // Always be listening for wallpaper changes when magic portrait flag is on
+ if (Flags.magicPortraitWallpapers()) SharingStarted.Eagerly else WhileSubscribed(),
+ initialValue = Flags.magicPortraitWallpapers(),
)
private suspend fun getWallpaper(selectedUser: SelectedUserModel): WallpaperInfo? {
@@ -188,14 +178,6 @@ constructor(
}
}
- private fun shouldSendNotificationLayout(wallpaperInfo: WallpaperInfo?): Boolean {
- return if (wallpaperInfo != null && wallpaperInfo.component != null) {
- wallpaperInfo.component!!.className == MAGIC_PORTRAIT_CLASSNAME
- } else {
- false
- }
- }
-
companion object {
const val MAGIC_PORTRAIT_CLASSNAME =
"com.google.android.apps.magicportrait.service.MagicPortraitWallpaperService"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index 21519b0cb38a..ab691c630f97 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -304,9 +304,7 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
testScope = TestScope(testDispatcher)
underTest =
KeyguardQuickAffordanceInteractor(
- keyguardInteractor =
- KeyguardInteractorFactory.create(featureFlags = featureFlags)
- .keyguardInteractor,
+ keyguardInteractor = kosmos.keyguardInteractor,
shadeInteractor = kosmos.shadeInteractor,
lockPatternUtils = lockPatternUtils,
keyguardStateController = keyguardStateController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
index b5a227104900..051aba3d593f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
@@ -44,9 +44,10 @@ import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanc
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
@@ -191,9 +192,8 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() {
dockManager = DockManagerFake()
biometricSettingsRepository = FakeBiometricSettingsRepository()
- val withDeps = KeyguardInteractorFactory.create()
- keyguardInteractor = withDeps.keyguardInteractor
- repository = withDeps.repository
+ keyguardInteractor = kosmos.keyguardInteractor
+ repository = kosmos.fakeKeyguardRepository
whenever(userTracker.userHandle).thenReturn(mock())
whenever(lockPatternUtils.getStrongAuthForUser(ArgumentMatchers.anyInt()))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index e68153ad2606..70450d29c74e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -57,7 +57,10 @@ import com.android.systemui.res.R
import com.android.systemui.settings.brightness.data.repository.BrightnessMirrorShowingRepository
import com.android.systemui.settings.brightness.domain.interactor.BrightnessMirrorShowingInteractor
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
+import com.android.systemui.shade.data.repository.ShadeAnimationRepository
+import com.android.systemui.shade.data.repository.ShadeRepositoryImpl
import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
+import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractorLegacyImpl
import com.android.systemui.statusbar.BlurUtils
import com.android.systemui.statusbar.DragDownHelper
import com.android.systemui.statusbar.LockscreenShadeTransitionController
@@ -219,6 +222,10 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) :
notificationShadeDepthController,
view,
shadeViewController,
+ ShadeAnimationInteractorLegacyImpl(
+ ShadeAnimationRepository(),
+ ShadeRepositoryImpl(testScope),
+ ),
panelExpansionInteractor,
ShadeExpansionStateManager(),
stackScrollLayoutController,
@@ -521,6 +528,18 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) :
verify(view).findViewById<ViewGroup>(R.id.keyguard_message_area)
}
+ @EnableFlags(Flags.FLAG_SHADE_LAUNCH_ACCESSIBILITY)
+ @Test
+ fun notifiesTheViewWhenLaunchAnimationIsRunning() {
+ testScope.runTest {
+ underTest.setExpandAnimationRunning(true)
+ verify(view).setAnimatingContentLaunch(true)
+
+ underTest.setExpandAnimationRunning(false)
+ verify(view).setAnimatingContentLaunch(false)
+ }
+ }
+
@Test
@DisableSceneContainer
fun setsUpCommunalHubLayout_whenFlagEnabled() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
index eae828562223..a5cd81ff3116 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
@@ -53,6 +53,7 @@ import com.android.systemui.shade.ShadeHeaderController.Companion.QQS_HEADER_CON
import com.android.systemui.shade.ShadeHeaderController.Companion.QS_HEADER_CONSTRAINT
import com.android.systemui.shade.carrier.ShadeCarrierGroup
import com.android.systemui.shade.carrier.ShadeCarrierGroupController
+import com.android.systemui.shade.data.repository.shadeDisplaysRepository
import com.android.systemui.statusbar.data.repository.fakeStatusBarContentInsetsProviderStore
import com.android.systemui.statusbar.phone.StatusIconContainer
import com.android.systemui.statusbar.phone.StatusOverlayHoverListenerFactory
@@ -96,7 +97,7 @@ class ShadeHeaderControllerTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val insetsProviderStore = kosmos.fakeStatusBarContentInsetsProviderStore
- private val insetsProvider = insetsProviderStore.defaultDisplay
+ private val insetsProvider = insetsProviderStore.forDisplay(context.displayId)
@Mock(answer = Answers.RETURNS_MOCKS) private lateinit var view: MotionLayout
@Mock private lateinit var statusIcons: StatusIconContainer
@@ -196,6 +197,7 @@ class ShadeHeaderControllerTest : SysuiTestCase() {
privacyIconsController,
insetsProviderStore,
configurationController,
+ kosmos.shadeDisplaysRepository,
variableDateViewControllerFactory,
batteryMeterViewController,
dumpManager,
@@ -809,6 +811,43 @@ class ShadeHeaderControllerTest : SysuiTestCase() {
}
@Test
+ fun sameInsetsTwice_listenerCallsOnApplyWindowInsetsOnlyOnce() {
+ val windowInsets = createWindowInsets()
+
+ val captor = ArgumentCaptor.forClass(View.OnApplyWindowInsetsListener::class.java)
+ verify(view).setOnApplyWindowInsetsListener(capture(captor))
+
+ val listener = captor.value
+
+ listener.onApplyWindowInsets(view, windowInsets)
+
+ verify(view, times(1)).onApplyWindowInsets(any())
+
+ listener.onApplyWindowInsets(view, windowInsets)
+
+ verify(view, times(1)).onApplyWindowInsets(any())
+ }
+
+ @Test
+ fun twoDifferentInsets_listenerCallsOnApplyWindowInsetsTwice() {
+ val windowInsets1 = WindowInsets(Rect(1, 2, 3, 4))
+ val windowInsets2 = WindowInsets(Rect(5, 6, 7, 8))
+
+ val captor = ArgumentCaptor.forClass(View.OnApplyWindowInsetsListener::class.java)
+ verify(view).setOnApplyWindowInsetsListener(capture(captor))
+
+ val listener = captor.value
+
+ listener.onApplyWindowInsets(view, windowInsets1)
+
+ verify(view, times(1)).onApplyWindowInsets(any())
+
+ listener.onApplyWindowInsets(view, windowInsets2)
+
+ verify(view, times(2)).onApplyWindowInsets(any())
+ }
+
+ @Test
fun alarmIconNotIgnored() {
verify(statusIcons, Mockito.never())
.addIgnoredSlot(context.getString(com.android.internal.R.string.status_bar_alarm_clock))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
index 61943f2283e0..8645a40319f4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
@@ -444,6 +444,7 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
eq(true), /* wasShownHighPriority */
eq(assistantFeedbackController),
any<MetricsLogger>(),
+ any<View.OnClickListener>(),
)
}
@@ -476,6 +477,7 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
eq(false), /* wasShownHighPriority */
eq(assistantFeedbackController),
any<MetricsLogger>(),
+ any<View.OnClickListener>(),
)
}
@@ -508,6 +510,7 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
eq(false), /* wasShownHighPriority */
eq(assistantFeedbackController),
any<MetricsLogger>(),
+ any<View.OnClickListener>(),
)
}
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 437ccb6a9821..68f66611c981 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
@@ -90,6 +90,8 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val statusBarContentInsetsProviderStore = kosmos.fakeStatusBarContentInsetsProviderStore
private val statusBarContentInsetsProvider = statusBarContentInsetsProviderStore.defaultDisplay
+ private val statusBarContentInsetsProviderForSecondaryDisplay =
+ statusBarContentInsetsProviderStore.forDisplay(SECONDARY_DISPLAY_ID)
private val fakeDarkIconDispatcher = kosmos.fakeDarkIconDispatcher
@Mock private lateinit var shadeViewController: ShadeViewController
@@ -144,6 +146,12 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
controller = createAndInitController(view)
}
+ `when`(
+ statusBarContentInsetsProviderForSecondaryDisplay
+ .getStatusBarContentInsetsForCurrentRotation()
+ )
+ .thenReturn(Insets.NONE)
+
val contextForSecondaryDisplay =
SysuiTestableContext(
mContext.createDisplayContext(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index a02d333d1507..a7fe1ba76590 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -59,6 +59,7 @@ import com.android.internal.colorextraction.ColorExtractor.GradientColors;
import com.android.keyguard.BouncerPanelExpansionCalculator;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.DejankUtils;
+import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.ShadeInterpolation;
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants;
@@ -71,6 +72,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac
import com.android.systemui.keyguard.shared.model.KeyguardState;
import com.android.systemui.keyguard.shared.model.TransitionState;
import com.android.systemui.keyguard.shared.model.TransitionStep;
+import com.android.systemui.keyguard.ui.transitions.BlurConfig;
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToGoneTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
import com.android.systemui.kosmos.KosmosJavaAdapter;
@@ -110,6 +112,9 @@ import java.util.Map;
@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
+// TODO(b/381263619) there are more changes and tweaks required to match the new bouncer/shade specs
+// Disabling for now but it will be fixed before the flag is fully ramped up.
+@DisableFlags(Flags.FLAG_BOUNCER_UI_REVAMP)
public class ScrimControllerTest extends SysuiTestCase {
@Rule public Expect mExpect = Expect.create();
@@ -286,7 +291,8 @@ public class ScrimControllerTest extends SysuiTestCase {
mKeyguardTransitionInteractor,
mKeyguardInteractor,
mKosmos.getTestDispatcher(),
- mLinearLargeScreenShadeInterpolator);
+ mLinearLargeScreenShadeInterpolator,
+ new BlurConfig(0.0f, 0.0f));
mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
mScrimController.setAnimatorListener(mAnimatorListener);
@@ -1251,7 +1257,8 @@ public class ScrimControllerTest extends SysuiTestCase {
mKeyguardTransitionInteractor,
mKeyguardInteractor,
mKosmos.getTestDispatcher(),
- mLinearLargeScreenShadeInterpolator);
+ mLinearLargeScreenShadeInterpolator,
+ new BlurConfig(0.0f, 0.0f));
mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
mScrimController.setAnimatorListener(mAnimatorListener);
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 fab7922f58e7..5d88f72b805b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -98,6 +98,7 @@ import android.view.View;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
@@ -2894,6 +2895,12 @@ public class BubblesTest extends SysuiTestCase {
@Override
public void animateBubbleBarLocation(BubbleBarLocation location) {
}
+
+ @Override
+ public void onDragItemOverBubbleBarDragZone(@NonNull BubbleBarLocation location) {}
+
+ @Override
+ public void onItemDraggedOutsideBubbleBarDropZone() {}
}
private static class FakeBubbleProperties implements BubbleProperties {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
index 0192fa47b434..739f6c2af2b4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
@@ -79,7 +79,7 @@ var Kosmos.shortcutHelperInputShortcutsSource: KeyboardShortcutGroupsSource by
var Kosmos.shortcutHelperCurrentAppShortcutsSource: KeyboardShortcutGroupsSource by
Kosmos.Fixture { CurrentAppShortcutsSource(windowManager) }
-val Kosmos.shortcutHelperAccessibilityShortcutsSource: KeyboardShortcutGroupsSource by
+var Kosmos.shortcutHelperAccessibilityShortcutsSource: KeyboardShortcutGroupsSource by
Kosmos.Fixture { AccessibilityShortcutsSource(mainResources) }
val Kosmos.shortcutHelperExclusions by
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 8489d8380041..8ea80081a871 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -18,6 +18,7 @@
package com.android.systemui.keyguard.data.repository
import android.graphics.Point
+import android.graphics.RectF
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
@@ -129,6 +130,10 @@ class FakeKeyguardRepository @Inject constructor() : KeyguardRepository {
override val notificationStackAbsoluteBottom: StateFlow<Float>
get() = _notificationStackAbsoluteBottom.asStateFlow()
+ private val _wallpaperFocalAreaBounds = MutableStateFlow(RectF(0f, 0f, 0f, 0f))
+ override val wallpaperFocalAreaBounds: StateFlow<RectF>
+ get() = _wallpaperFocalAreaBounds.asStateFlow()
+
private val _isKeyguardEnabled = MutableStateFlow(true)
override val isKeyguardEnabled: StateFlow<Boolean> = _isKeyguardEnabled.asStateFlow()
@@ -287,6 +292,10 @@ class FakeKeyguardRepository @Inject constructor() : KeyguardRepository {
_notificationStackAbsoluteBottom.value = bottom
}
+ override fun setWallpaperFocalAreaBounds(bounds: RectF) {
+ _wallpaperFocalAreaBounds.value = bounds
+ }
+
override fun setCanIgnoreAuthAndReturnToGone(canWake: Boolean) {
_canIgnoreAuthAndReturnToGone.value = canWake
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WallpaperFocalAreaInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WallpaperFocalAreaInteractorKosmos.kt
new file mode 100644
index 000000000000..8fd6f62b315f
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WallpaperFocalAreaInteractorKosmos.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.content.applicationContext
+import com.android.systemui.keyguard.data.repository.keyguardClockRepository
+import com.android.systemui.keyguard.data.repository.keyguardRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.shade.data.repository.shadeRepository
+import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
+import com.android.systemui.wallpapers.data.repository.wallpaperRepository
+
+val Kosmos.wallpaperFocalAreaInteractor by
+ Kosmos.Fixture {
+ WallpaperFocalAreaInteractor(
+ applicationScope = applicationCoroutineScope,
+ context = applicationContext,
+ keyguardRepository = keyguardRepository,
+ shadeRepository = shadeRepository,
+ activeNotificationsInteractor = activeNotificationsInteractor,
+ keyguardClockRepository = keyguardClockRepository,
+ wallpaperRepository = wallpaperRepository,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
index 40b8e0e62b03..37df05b68f9e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
@@ -20,6 +20,7 @@ import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.pulseExpansionInteractor
+import com.android.systemui.keyguard.domain.interactor.wallpaperFocalAreaInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.applicationCoroutineScope
@@ -87,5 +88,6 @@ val Kosmos.keyguardRootViewModel by Fixture {
screenOffAnimationController = screenOffAnimationController,
aodBurnInViewModel = aodBurnInViewModel,
shadeInteractor = shadeInteractor,
+ wallpaperFocalAreaInteractor = wallpaperFocalAreaInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt
index 9d73ae3f176f..a1e7d5cede13 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt
@@ -40,7 +40,8 @@ class FakeVolumeDialogController(private val audioManager: AudioManager) : Volum
private val callbacks = CopyOnWriteArraySet<VolumeDialogController.Callbacks>()
private var hasVibrator: Boolean = true
- private var state = VolumeDialogController.State()
+ var state = VolumeDialogController.State()
+ private set
override fun setActiveStream(stream: Int) {
updateState {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorKosmos.kt
index 7892e962d63d..1b50094ec0b7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorKosmos.kt
@@ -16,14 +16,57 @@
package com.android.systemui.shade.domain.interactor
+import android.provider.Settings
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.shade.data.repository.shadeRepository
+import com.android.systemui.shared.settings.data.repository.secureSettingsRepository
+import kotlinx.coroutines.launch
val Kosmos.shadeModeInteractor by Fixture {
ShadeModeInteractorImpl(
applicationScope = applicationCoroutineScope,
repository = shadeRepository,
+ secureSettingsRepository = secureSettingsRepository,
)
}
+
+// TODO(b/391578667): Make this user-aware once supported by FakeSecureSettingsRepository.
+/**
+ * Enables the Dual Shade setting, and (optionally) sets the shade layout to be wide (`true`)
+ * or narrow (`false`).
+ *
+ * In a wide layout, notifications and quick settings shades each take up only half the screen
+ * width. In a narrow layout, they each take up the entire screen width.
+ */
+fun Kosmos.enableDualShade(wideLayout: Boolean? = null) {
+ testScope.launch {
+ secureSettingsRepository.setInt(Settings.Secure.DUAL_SHADE, 1)
+
+ if (wideLayout != null) {
+ fakeShadeRepository.setShadeLayoutWide(wideLayout)
+ }
+ }
+}
+
+// TODO(b/391578667): Make this user-aware once supported by FakeSecureSettingsRepository.
+fun Kosmos.disableDualShade() {
+ testScope.launch { secureSettingsRepository.setInt(Settings.Secure.DUAL_SHADE, 0) }
+}
+
+fun Kosmos.enableSingleShade() {
+ testScope.launch {
+ disableDualShade()
+ fakeShadeRepository.setShadeLayoutWide(false)
+ }
+}
+
+fun Kosmos.enableSplitShade() {
+ testScope.launch {
+ disableDualShade()
+ fakeShadeRepository.setShadeLayoutWide(true)
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/RankingBuilder.java b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/RankingBuilder.java
index 6cd6594c3404..c6daed1aa58f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/RankingBuilder.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/RankingBuilder.java
@@ -61,6 +61,7 @@ public class RankingBuilder {
private boolean mIsBubble = false;
private int mProposedImportance = IMPORTANCE_UNSPECIFIED;
private boolean mSensitiveContent = false;
+ private String mSummarization = null;
public RankingBuilder() {
}
@@ -92,6 +93,7 @@ public class RankingBuilder {
mIsBubble = ranking.isBubble();
mProposedImportance = ranking.getProposedImportance();
mSensitiveContent = ranking.hasSensitiveContent();
+ mSummarization = ranking.getSummarization();
}
public Ranking build() {
@@ -122,7 +124,8 @@ public class RankingBuilder {
mRankingAdjustment,
mIsBubble,
mProposedImportance,
- mSensitiveContent);
+ mSensitiveContent,
+ mSummarization);
return ranking;
}
@@ -262,6 +265,11 @@ public class RankingBuilder {
return this;
}
+ public RankingBuilder setSummarization(String summary) {
+ mSummarization = summary;
+ return this;
+ }
+
private static <E> ArrayList<E> copyList(List<E> list) {
if (list == null) {
return null;
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponentKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponentKosmos.kt
index 36fa82f82f0d..4ca044d60f3f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponentKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponentKosmos.kt
@@ -32,10 +32,8 @@ import com.android.systemui.volume.dialog.data.repository.volumeDialogVisibility
import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSliderType
import com.android.systemui.volume.dialog.sliders.domain.model.volumeDialogSliderType
import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogOverscrollViewBinder
-import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSliderHapticsViewBinder
import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSliderViewBinder
import com.android.systemui.volume.dialog.sliders.ui.volumeDialogOverscrollViewBinder
-import com.android.systemui.volume.dialog.sliders.ui.volumeDialogSliderHapticsViewBinder
import com.android.systemui.volume.dialog.sliders.ui.volumeDialogSliderViewBinder
import com.android.systemui.volume.mediaControllerRepository
import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.mediaControllerInteractor
@@ -65,9 +63,6 @@ fun Kosmos.volumeDialogSliderComponent(type: VolumeDialogSliderType): VolumeDial
override fun sliderViewBinder(): VolumeDialogSliderViewBinder =
localKosmos.volumeDialogSliderViewBinder
- override fun sliderHapticsViewBinder(): VolumeDialogSliderHapticsViewBinder =
- localKosmos.volumeDialogSliderHapticsViewBinder
-
override fun overscrollViewBinder(): VolumeDialogOverscrollViewBinder =
localKosmos.volumeDialogOverscrollViewBinder
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderHapticsViewBinderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderHapticsViewBinderKosmos.kt
deleted file mode 100644
index d6845b1ff7e3..000000000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderHapticsViewBinderKosmos.kt
+++ /dev/null
@@ -1,33 +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.volume.dialog.sliders.ui
-
-import com.android.systemui.haptics.msdl.msdlPlayer
-import com.android.systemui.haptics.vibratorHelper
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.util.time.systemClock
-import com.android.systemui.volume.dialog.sliders.ui.viewmodel.volumeDialogSliderInputEventsViewModel
-
-val Kosmos.volumeDialogSliderHapticsViewBinder by
- Kosmos.Fixture {
- VolumeDialogSliderHapticsViewBinder(
- volumeDialogSliderInputEventsViewModel,
- vibratorHelper,
- msdlPlayer,
- systemClock,
- )
- }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinderKosmos.kt
index c6db717e004f..484a7cc30152 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinderKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinderKosmos.kt
@@ -16,14 +16,16 @@
package com.android.systemui.volume.dialog.sliders.ui
+import com.android.systemui.haptics.slider.sliderHapticsViewModelFactory
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.volume.dialog.sliders.ui.viewmodel.volumeDialogSliderInputEventsViewModel
+import com.android.systemui.volume.dialog.sliders.ui.viewmodel.volumeDialogOverscrollViewModel
import com.android.systemui.volume.dialog.sliders.ui.viewmodel.volumeDialogSliderViewModel
val Kosmos.volumeDialogSliderViewBinder by
Kosmos.Fixture {
VolumeDialogSliderViewBinder(
volumeDialogSliderViewModel,
- volumeDialogSliderInputEventsViewModel,
+ volumeDialogOverscrollViewModel,
+ sliderHapticsViewModelFactory,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderInputEventsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderInputEventsViewModelKosmos.kt
deleted file mode 100644
index 2de0e8f76a4b..000000000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderInputEventsViewModelKosmos.kt
+++ /dev/null
@@ -1,29 +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.volume.dialog.sliders.ui.viewmodel
-
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.applicationCoroutineScope
-import com.android.systemui.volume.dialog.sliders.domain.interactor.volumeDialogSliderInputEventsInteractor
-
-val Kosmos.volumeDialogSliderInputEventsViewModel by
- Kosmos.Fixture {
- VolumeDialogSliderInputEventsViewModel(
- applicationCoroutineScope,
- volumeDialogSliderInputEventsInteractor,
- )
- }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModelKosmos.kt
index b26081c40c38..90bbb28ff519 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModelKosmos.kt
@@ -21,6 +21,7 @@ import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.util.time.systemClock
import com.android.systemui.volume.dialog.domain.interactor.volumeDialogVisibilityInteractor
import com.android.systemui.volume.dialog.shared.volumeDialogLogger
+import com.android.systemui.volume.dialog.sliders.domain.interactor.volumeDialogSliderInputEventsInteractor
import com.android.systemui.volume.dialog.sliders.domain.interactor.volumeDialogSliderInteractor
import com.android.systemui.volume.dialog.sliders.domain.model.volumeDialogSliderType
@@ -29,6 +30,7 @@ val Kosmos.volumeDialogSliderViewModel by
VolumeDialogSliderViewModel(
sliderType = volumeDialogSliderType,
interactor = volumeDialogSliderInteractor,
+ inputEventsInteractor = volumeDialogSliderInputEventsInteractor,
visibilityInteractor = volumeDialogVisibilityInteractor,
coroutineScope = applicationCoroutineScope,
volumeDialogSliderIconProvider = volumeDialogSliderIconProvider,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryKosmos.kt
index ddb9a3ffee6d..f0c0d30e6db4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryKosmos.kt
@@ -19,7 +19,6 @@ package com.android.systemui.wallpapers.data.repository
import android.content.applicationContext
import com.android.app.wallpaperManager
import com.android.systemui.broadcast.broadcastDispatcher
-import com.android.systemui.keyguard.data.repository.keyguardClockRepository
import com.android.systemui.keyguard.data.repository.keyguardRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -34,8 +33,7 @@ val Kosmos.wallpaperRepository by Fixture {
bgDispatcher = testDispatcher,
broadcastDispatcher = broadcastDispatcher,
userRepository = userRepository,
- wallpaperManager = wallpaperManager,
- keyguardClockRepository = keyguardClockRepository,
keyguardRepository = keyguardRepository,
+ wallpaperManager = wallpaperManager,
)
}
diff --git a/packages/Vcn/framework-b/framework-vcn-jarjar-rules.txt b/packages/Vcn/framework-b/framework-vcn-jarjar-rules.txt
index 7e27b24f749c..33287b7f3449 100644
--- a/packages/Vcn/framework-b/framework-vcn-jarjar-rules.txt
+++ b/packages/Vcn/framework-b/framework-vcn-jarjar-rules.txt
@@ -1,2 +1,3 @@
rule android.net.vcn.persistablebundleutils.** android.net.connectivity.android.net.vcn.persistablebundleutils.@1
-rule android.net.vcn.util.** android.net.connectivity.android.net.vcn.util.@1 \ No newline at end of file
+rule android.net.vcn.util.** android.net.connectivity.android.net.vcn.util.@1
+rule android.util.IndentingPrintWriter android.net.connectivity.android.util.IndentingPrintWriter \ No newline at end of file
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index adbb3afb4130..65550f2b4273 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -661,6 +661,9 @@ android_ravenwood_libgroup {
// StatsD
"framework-statsd.ravenwood",
+ // Graphics
+ "framework-graphics.ravenwood",
+
// Provide runtime versions of utils linked in below
"junit",
"truth",
diff --git a/ravenwood/Framework.bp b/ravenwood/Framework.bp
index 99fc31b258e9..71496b0d5766 100644
--- a/ravenwood/Framework.bp
+++ b/ravenwood/Framework.bp
@@ -399,3 +399,55 @@ java_genrule {
"framework-statsd.ravenwood.jar",
],
}
+
+//////////////////////
+// framework-graphics
+//////////////////////
+
+java_genrule {
+ name: "framework-graphics.ravenwood-base",
+ tools: ["hoststubgen"],
+ cmd: "$(location hoststubgen) " +
+ "@$(location :ravenwood-standard-options) " +
+
+ "--debug-log $(location framework-graphics.log) " +
+ "--stats-file $(location framework-graphics_stats.csv) " +
+ "--supported-api-list-file $(location framework-graphics_apis.csv) " +
+ "--gen-keep-all-file $(location framework-graphics_keep_all.txt) " +
+ "--gen-input-dump-file $(location framework-graphics_dump.txt) " +
+
+ "--out-impl-jar $(location ravenwood.jar) " +
+ "--in-jar $(location :framework-graphics.impl{.jar}) " +
+
+ "--policy-override-file $(location :ravenwood-common-policies) ",
+ srcs: [
+ ":framework-graphics.impl{.jar}",
+
+ ":ravenwood-common-policies",
+ ":ravenwood-standard-options",
+ ],
+ out: [
+ "ravenwood.jar",
+
+ // Following files are created just as FYI.
+ "framework-graphics_keep_all.txt",
+ "framework-graphics_dump.txt",
+
+ "framework-graphics.log",
+ "framework-graphics_stats.csv",
+ "framework-graphics_apis.csv",
+ ],
+ visibility: ["//visibility:private"],
+}
+
+java_genrule {
+ name: "framework-graphics.ravenwood",
+ defaults: ["ravenwood-internal-only-visibility-genrule"],
+ cmd: "cp $(in) $(out)",
+ srcs: [
+ ":framework-graphics.ravenwood-base{ravenwood.jar}",
+ ],
+ out: [
+ "framework-graphics.ravenwood.jar",
+ ],
+}
diff --git a/ravenwood/scripts/pta-framework.sh b/ravenwood/scripts/pta-framework.sh
index 224ab59e2e09..46c2c01c8ee8 100755
--- a/ravenwood/scripts/pta-framework.sh
+++ b/ravenwood/scripts/pta-framework.sh
@@ -79,6 +79,7 @@ run_pta() {
$extra_args
if ! [[ -f $OUT_SCRIPT ]] ; then
+ echo "No files need updating."
# no operations generated.
exit 0
fi
@@ -88,4 +89,4 @@ run_pta() {
return 0
}
-run_pta "$extra_args" \ No newline at end of file
+run_pta "$extra_args"
diff --git a/ravenwood/texts/ravenwood-framework-policies.txt b/ravenwood/texts/ravenwood-framework-policies.txt
index 4033782c607e..fff9e6ad41d5 100644
--- a/ravenwood/texts/ravenwood-framework-policies.txt
+++ b/ravenwood/texts/ravenwood-framework-policies.txt
@@ -62,4 +62,4 @@ class android.text.ClipboardManager keep # no-pta
# Just enough to allow ResourcesManager to run
class android.hardware.display.DisplayManagerGlobal keep # no-pta
- method getInstance ()Landroid/hardware/display/DisplayManagerGlobal; ignore
+ method getInstance ()Landroid/hardware/display/DisplayManagerGlobal; ignore # no-pta
diff --git a/ravenwood/texts/ravenwood-standard-options.txt b/ravenwood/texts/ravenwood-standard-options.txt
index 27223d8b72ff..91fd9283aff2 100644
--- a/ravenwood/texts/ravenwood-standard-options.txt
+++ b/ravenwood/texts/ravenwood-standard-options.txt
@@ -31,6 +31,9 @@
--remove-annotation
android.ravenwood.annotation.RavenwoodRemove
+--ignore-annotation
+ android.ravenwood.annotation.RavenwoodIgnore
+
--substitute-annotation
android.ravenwood.annotation.RavenwoodReplace
diff --git a/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/Annotations.kt b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/Annotations.kt
index 4a11259a8ef7..ef1cb5dfca89 100644
--- a/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/Annotations.kt
+++ b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/Annotations.kt
@@ -45,7 +45,8 @@ class Annotations {
"@android.ravenwood.annotation.RavenwoodRedirect"
FilterPolicy.Throw ->
"@android.ravenwood.annotation.RavenwoodThrow"
- FilterPolicy.Ignore -> null // Ignore has no annotation. (because it's not very safe.)
+ FilterPolicy.Ignore ->
+ "@android.ravenwood.annotation.RavenwoodIgnore"
FilterPolicy.Remove ->
"@android.ravenwood.annotation.RavenwoodRemove"
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 875b655fe3d2..91775f8eed96 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -5084,39 +5084,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
final List<String> permittedServices = dpm.getPermittedAccessibilityServices(userId);
// permittedServices null means all accessibility services are allowed.
- boolean allowed = permittedServices == null || permittedServices.contains(packageName);
- if (allowed) {
- if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
- && android.security.Flags.extendEcmToAllSettings()) {
- try {
- final EnhancedConfirmationManager userContextEcm =
- mContext.createContextAsUser(UserHandle.of(userId), /* flags = */ 0)
- .getSystemService(EnhancedConfirmationManager.class);
- if (userContextEcm != null) {
- return !userContextEcm.isRestricted(packageName,
- AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE);
- }
- return false;
- } catch (PackageManager.NameNotFoundException e) {
- Log.e(LOG_TAG, "Exception when retrieving package:" + packageName, e);
- return false;
- }
- } else {
- try {
- final int mode = mContext.getSystemService(AppOpsManager.class)
- .noteOpNoThrow(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
- uid, packageName);
- final boolean ecmEnabled = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_enhancedConfirmationModeEnabled);
- return !ecmEnabled || mode == AppOpsManager.MODE_ALLOWED
- || mode == AppOpsManager.MODE_DEFAULT;
- } catch (Exception e) {
- // Fallback in case if app ops is not available in testing.
- return false;
- }
- }
- }
- return false;
+ return permittedServices == null || permittedServices.contains(packageName);
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index a37b2b926c9c..02a8f6218468 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -612,7 +612,7 @@ public class CompanionDeviceManagerService extends SystemService {
@Override
public void enablePermissionsSync(int associationId) {
- if (UserHandle.getAppId(Binder.getCallingUid()) == SYSTEM_UID) {
+ if (UserHandle.getAppId(Binder.getCallingUid()) != SYSTEM_UID) {
throw new SecurityException("Caller must be system UID");
}
mSystemDataTransferProcessor.enablePermissionsSync(associationId);
@@ -620,7 +620,7 @@ public class CompanionDeviceManagerService extends SystemService {
@Override
public void disablePermissionsSync(int associationId) {
- if (UserHandle.getAppId(Binder.getCallingUid()) == SYSTEM_UID) {
+ if (UserHandle.getAppId(Binder.getCallingUid()) != SYSTEM_UID) {
throw new SecurityException("Caller must be system UID");
}
mSystemDataTransferProcessor.disablePermissionsSync(associationId);
@@ -628,7 +628,7 @@ public class CompanionDeviceManagerService extends SystemService {
@Override
public PermissionSyncRequest getPermissionSyncRequest(int associationId) {
- if (UserHandle.getAppId(Binder.getCallingUid()) == SYSTEM_UID) {
+ if (UserHandle.getAppId(Binder.getCallingUid()) != SYSTEM_UID) {
throw new SecurityException("Caller must be system UID");
}
return mSystemDataTransferProcessor.getPermissionSyncRequest(associationId);
@@ -704,7 +704,7 @@ public class CompanionDeviceManagerService extends SystemService {
@Override
public byte[] getBackupPayload(int userId) {
- if (UserHandle.getAppId(Binder.getCallingUid()) == SYSTEM_UID) {
+ if (UserHandle.getAppId(Binder.getCallingUid()) != SYSTEM_UID) {
throw new SecurityException("Caller must be system");
}
return mBackupRestoreProcessor.getBackupPayload(userId);
@@ -712,7 +712,7 @@ public class CompanionDeviceManagerService extends SystemService {
@Override
public void applyRestoredPayload(byte[] payload, int userId) {
- if (UserHandle.getAppId(Binder.getCallingUid()) == SYSTEM_UID) {
+ if (UserHandle.getAppId(Binder.getCallingUid()) != SYSTEM_UID) {
throw new SecurityException("Caller must be system");
}
mBackupRestoreProcessor.applyRestoredPayload(payload, userId);
diff --git a/services/core/java/com/android/server/MasterClearReceiver.java b/services/core/java/com/android/server/MasterClearReceiver.java
index 2b30c013235c..6f2ecd82a79c 100644
--- a/services/core/java/com/android/server/MasterClearReceiver.java
+++ b/services/core/java/com/android/server/MasterClearReceiver.java
@@ -130,7 +130,7 @@ public class MasterClearReceiver extends BroadcastReceiver {
if (mWipeExternalStorage) {
// thr will be started at the end of this task.
Slog.i(TAG, "Wiping external storage on async task");
- new WipeDataTask(context, thr).execute();
+ new WipeDataTask(context, thr).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} else {
Slog.i(TAG, "NOT wiping external storage; starting thread " + thr.getName());
thr.start();
diff --git a/services/core/java/com/android/server/am/BroadcastController.java b/services/core/java/com/android/server/am/BroadcastController.java
index bfacfbba4e22..bec5db79ff9d 100644
--- a/services/core/java/com/android/server/am/BroadcastController.java
+++ b/services/core/java/com/android/server/am/BroadcastController.java
@@ -317,7 +317,7 @@ class BroadcastController {
Slog.w(TAG, "registerReceiverWithFeature: no app for " + caller);
return null;
}
- if (callerApp.info.uid != SYSTEM_UID
+ if (!UserHandle.isCore(callerApp.info.uid)
&& !callerApp.getPkgList().containsKey(callerPackage)) {
throw new SecurityException("Given caller package " + callerPackage
+ " is not running in process " + callerApp);
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 0b7890167c08..3817ba1a28b9 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -305,10 +305,6 @@ public final class PendingIntentRecord extends IIntentSender.Stub {
this.stringName = null;
}
- @VisibleForTesting TempAllowListDuration getAllowlistDurationLocked(IBinder allowlistToken) {
- return mAllowlistDuration.get(allowlistToken);
- }
-
void setAllowBgActivityStarts(IBinder token, int flags) {
if (token == null) return;
if ((flags & FLAG_ACTIVITY_SENDER) != 0) {
@@ -327,12 +323,6 @@ public final class PendingIntentRecord extends IIntentSender.Stub {
mAllowBgActivityStartsForActivitySender.remove(token);
mAllowBgActivityStartsForBroadcastSender.remove(token);
mAllowBgActivityStartsForServiceSender.remove(token);
- if (mAllowlistDuration != null) {
- mAllowlistDuration.remove(token);
- if (mAllowlistDuration.isEmpty()) {
- mAllowlistDuration = null;
- }
- }
}
public void registerCancelListenerLocked(IResultReceiver receiver) {
@@ -713,7 +703,7 @@ public final class PendingIntentRecord extends IIntentSender.Stub {
return res;
}
- @VisibleForTesting BackgroundStartPrivileges getBackgroundStartPrivilegesForActivitySender(
+ private BackgroundStartPrivileges getBackgroundStartPrivilegesForActivitySender(
IBinder allowlistToken) {
return mAllowBgActivityStartsForActivitySender.contains(allowlistToken)
? BackgroundStartPrivileges.allowBackgroundActivityStarts(allowlistToken)
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 8e09e3b8e112..833599810210 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -604,7 +604,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
- /** Returned from {@link #verifyAndGetBypass(int, String, String, String, boolean)}. */
+ /** Returned from {@link #verifyAndGetBypass(int, String, String, int, String, boolean)}. */
private static final class PackageVerificationResult {
final RestrictionBypass bypass;
@@ -3087,10 +3087,10 @@ public class AppOpsService extends IAppOpsService.Stub {
public int checkPackage(int uid, String packageName) {
Objects.requireNonNull(packageName);
try {
- verifyAndGetBypass(uid, packageName, null, null, true);
+ verifyAndGetBypass(uid, packageName, null, Process.INVALID_UID, null, true);
// When the caller is the system, it's possible that the packageName is the special
// one (e.g., "root") which isn't actually existed.
- if (resolveUid(packageName) == uid
+ if (resolveNonAppUid(packageName) == uid
|| (isPackageExisted(packageName)
&& !filterAppAccessUnlocked(packageName, UserHandle.getUserId(uid)))) {
return AppOpsManager.MODE_ALLOWED;
@@ -3306,7 +3306,7 @@ public class AppOpsService extends IAppOpsService.Stub {
boolean shouldCollectMessage, int notedCount) {
PackageVerificationResult pvr;
try {
- pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyPackageName);
+ pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyUid, proxyPackageName);
if (!pvr.isAttributionTagValid) {
attributionTag = null;
}
@@ -3930,7 +3930,7 @@ public class AppOpsService extends IAppOpsService.Stub {
// Test if the proxied operation will succeed before starting the proxy operation
final SyncNotedAppOp testProxiedOp = startOperationDryRun(code,
proxiedUid, resolvedProxiedPackageName, proxiedAttributionTag,
- proxiedVirtualDeviceId, resolvedProxyPackageName, proxiedFlags,
+ proxiedVirtualDeviceId, proxyUid, resolvedProxyPackageName, proxiedFlags,
startIfModeDefault);
if (!shouldStartForMode(testProxiedOp.getOpMode(), startIfModeDefault)) {
@@ -3970,7 +3970,7 @@ public class AppOpsService extends IAppOpsService.Stub {
int attributionChainId) {
PackageVerificationResult pvr;
try {
- pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyPackageName);
+ pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyUid, proxyPackageName);
if (!pvr.isAttributionTagValid) {
attributionTag = null;
}
@@ -4097,11 +4097,11 @@ public class AppOpsService extends IAppOpsService.Stub {
*/
private SyncNotedAppOp startOperationDryRun(int code, int uid,
@NonNull String packageName, @Nullable String attributionTag, int virtualDeviceId,
- String proxyPackageName, @OpFlags int flags,
+ int proxyUid, String proxyPackageName, @OpFlags int flags,
boolean startIfModeDefault) {
PackageVerificationResult pvr;
try {
- pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyPackageName);
+ pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyUid, proxyPackageName);
if (!pvr.isAttributionTagValid) {
attributionTag = null;
}
@@ -4656,13 +4656,17 @@ public class AppOpsService extends IAppOpsService.Stub {
private boolean isSpecialPackage(int callingUid, @Nullable String packageName) {
final String resolvedPackage = AppOpsManager.resolvePackageName(callingUid, packageName);
return callingUid == Process.SYSTEM_UID
- || resolveUid(resolvedPackage) != Process.INVALID_UID;
+ || resolveNonAppUid(resolvedPackage) != Process.INVALID_UID;
}
private boolean isCallerAndAttributionTrusted(@NonNull AttributionSource attributionSource) {
if (attributionSource.getUid() != Binder.getCallingUid()
&& attributionSource.isTrusted(mContext)) {
- return true;
+ // if there is a next attribution source, it must be trusted, as well.
+ if (attributionSource.getNext() == null
+ || attributionSource.getNext().isTrusted(mContext)) {
+ return true;
+ }
}
return mContext.checkPermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
Binder.getCallingPid(), Binder.getCallingUid(), null)
@@ -4757,19 +4761,20 @@ public class AppOpsService extends IAppOpsService.Stub {
}
/**
- * @see #verifyAndGetBypass(int, String, String, String, boolean)
+ * @see #verifyAndGetBypass(int, String, String, int, String, boolean)
*/
private @NonNull PackageVerificationResult verifyAndGetBypass(int uid, String packageName,
@Nullable String attributionTag) {
- return verifyAndGetBypass(uid, packageName, attributionTag, null);
+ return verifyAndGetBypass(uid, packageName, attributionTag, Process.INVALID_UID, null);
}
/**
- * @see #verifyAndGetBypass(int, String, String, String, boolean)
+ * @see #verifyAndGetBypass(int, String, String, int, String, boolean)
*/
private @NonNull PackageVerificationResult verifyAndGetBypass(int uid, String packageName,
- @Nullable String attributionTag, @Nullable String proxyPackageName) {
- return verifyAndGetBypass(uid, packageName, attributionTag, proxyPackageName, false);
+ @Nullable String attributionTag, int proxyUid, @Nullable String proxyPackageName) {
+ return verifyAndGetBypass(uid, packageName, attributionTag, proxyUid, proxyPackageName,
+ false);
}
/**
@@ -4780,14 +4785,15 @@ public class AppOpsService extends IAppOpsService.Stub {
* @param uid The uid the package belongs to
* @param packageName The package the might belong to the uid
* @param attributionTag attribution tag or {@code null} if no need to verify
- * @param proxyPackageName The proxy package, from which the attribution tag is to be pulled
+ * @param proxyUid The proxy uid, from which the attribution tag is to be pulled
+ * @param proxyPackageName The proxy package, from which the attribution tag may be pulled
* @param suppressErrorLogs Whether to print to logcat about nonmatching parameters
*
* @return PackageVerificationResult containing {@link RestrictionBypass} and whether the
* attribution tag is valid
*/
private @NonNull PackageVerificationResult verifyAndGetBypass(int uid, String packageName,
- @Nullable String attributionTag, @Nullable String proxyPackageName,
+ @Nullable String attributionTag, int proxyUid, @Nullable String proxyPackageName,
boolean suppressErrorLogs) {
if (uid == Process.ROOT_UID) {
// For backwards compatibility, don't check package name for root UID.
@@ -4831,34 +4837,47 @@ public class AppOpsService extends IAppOpsService.Stub {
int callingUid = Binder.getCallingUid();
- // Allow any attribution tag for resolvable uids
- int pkgUid;
+ // Allow any attribution tag for resolvable, non-app uids
+ int nonAppUid;
if (Objects.equals(packageName, "com.android.shell")) {
// Special case for the shell which is a package but should be able
// to bypass app attribution tag restrictions.
- pkgUid = Process.SHELL_UID;
+ nonAppUid = Process.SHELL_UID;
} else {
- pkgUid = resolveUid(packageName);
+ nonAppUid = resolveNonAppUid(packageName);
}
- if (pkgUid != Process.INVALID_UID) {
- if (pkgUid != UserHandle.getAppId(uid)) {
+ if (nonAppUid != Process.INVALID_UID) {
+ if (nonAppUid != UserHandle.getAppId(uid)) {
if (!suppressErrorLogs) {
Slog.e(TAG, "Bad call made by uid " + callingUid + ". "
- + "Package \"" + packageName + "\" does not belong to uid " + uid
- + ".");
+ + "Package \"" + packageName + "\" does not belong to uid " + uid
+ + ".");
+ }
+ String otherUidMessage =
+ DEBUG ? " but it is really " + nonAppUid : " but it is not";
+ throw new SecurityException("Specified package \"" + packageName
+ + "\" under uid " + UserHandle.getAppId(uid) + otherUidMessage);
+ }
+ // We only allow bypassing the attribution tag verification if the proxy is a
+ // system app (or is null), in order to prevent abusive apps clogging the appops
+ // system with unlimited attribution tags via proxy calls.
+ boolean proxyIsSystemAppOrNull = true;
+ if (proxyPackageName != null) {
+ int proxyAppId = UserHandle.getAppId(proxyUid);
+ if (proxyAppId >= Process.FIRST_APPLICATION_UID) {
+ proxyIsSystemAppOrNull =
+ mPackageManagerInternal.isSystemPackage(proxyPackageName);
}
- String otherUidMessage = DEBUG ? " but it is really " + pkgUid : " but it is not";
- throw new SecurityException("Specified package \"" + packageName + "\" under uid "
- + UserHandle.getAppId(uid) + otherUidMessage);
}
return new PackageVerificationResult(RestrictionBypass.UNRESTRICTED,
- /* isAttributionTagValid */ true);
+ /* isAttributionTagValid */ proxyIsSystemAppOrNull);
}
int userId = UserHandle.getUserId(uid);
RestrictionBypass bypass = null;
boolean isAttributionTagValid = false;
+ int pkgUid = nonAppUid;
final long ident = Binder.clearCallingIdentity();
try {
PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class);
@@ -5649,7 +5668,7 @@ public class AppOpsService extends IAppOpsService.Stub {
if (nonpackageUid != -1) {
packageName = null;
} else {
- packageUid = resolveUid(packageName);
+ packageUid = resolveNonAppUid(packageName);
if (packageUid < 0) {
packageUid = AppGlobals.getPackageManager().getPackageUid(packageName,
PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
@@ -6749,7 +6768,13 @@ public class AppOpsService extends IAppOpsService.Stub {
if (restricted && attrOp.isRunning()) {
attrOp.pause();
} else if (attrOp.isPaused()) {
- attrOp.resume();
+ RestrictionBypass bypass = verifyAndGetBypass(uid, ops.packageName, attrOp.tag)
+ .bypass;
+ if (!isOpRestrictedLocked(uid, code, ops.packageName, attrOp.tag,
+ Context.DEVICE_ID_DEFAULT, bypass, false)) {
+ // Only resume if there are no other restrictions remaining on this op
+ attrOp.resume();
+ }
}
}
}
@@ -7198,7 +7223,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
- private static int resolveUid(String packageName) {
+ private static int resolveNonAppUid(String packageName) {
if (packageName == null) {
return Process.INVALID_UID;
}
diff --git a/services/core/java/com/android/server/audio/AudioManagerShellCommand.java b/services/core/java/com/android/server/audio/AudioManagerShellCommand.java
index 030ce12f5063..c35f4fca6edd 100644
--- a/services/core/java/com/android/server/audio/AudioManagerShellCommand.java
+++ b/services/core/java/com/android/server/audio/AudioManagerShellCommand.java
@@ -65,6 +65,10 @@ class AudioManagerShellCommand extends ShellCommand {
return setRingerMode();
case "set-volume":
return setVolume();
+ case "get-min-volume":
+ return getMinVolume();
+ case "get-max-volume":
+ return getMaxVolume();
case "set-device-volume":
return setDeviceVolume();
case "adj-mute":
@@ -106,6 +110,10 @@ class AudioManagerShellCommand extends ShellCommand {
pw.println(" Sets the Ringer mode to one of NORMAL|SILENT|VIBRATE");
pw.println(" set-volume STREAM_TYPE VOLUME_INDEX");
pw.println(" Sets the volume for STREAM_TYPE to VOLUME_INDEX");
+ pw.println(" get-min-volume STREAM_TYPE");
+ pw.println(" Gets the min volume for STREAM_TYPE");
+ pw.println(" get-max-volume STREAM_TYPE");
+ pw.println(" Gets the max volume for STREAM_TYPE");
pw.println(" set-device-volume STREAM_TYPE VOLUME_INDEX NATIVE_DEVICE_TYPE");
pw.println(" Sets for NATIVE_DEVICE_TYPE the STREAM_TYPE volume to VOLUME_INDEX");
pw.println(" adj-mute STREAM_TYPE");
@@ -296,6 +304,24 @@ class AudioManagerShellCommand extends ShellCommand {
return 0;
}
+ private int getMinVolume() {
+ final Context context = mService.mContext;
+ final AudioManager am = context.getSystemService(AudioManager.class);
+ final int stream = readIntArg();
+ final int result = am.getStreamMinVolume(stream);
+ getOutPrintWriter().println("AudioManager.getStreamMinVolume(" + stream + ") -> " + result);
+ return 0;
+ }
+
+ private int getMaxVolume() {
+ final Context context = mService.mContext;
+ final AudioManager am = context.getSystemService(AudioManager.class);
+ final int stream = readIntArg();
+ final int result = am.getStreamMaxVolume(stream);
+ getOutPrintWriter().println("AudioManager.getStreamMaxVolume(" + stream + ") -> " + result);
+ return 0;
+ }
+
private int setDeviceVolume() {
final Context context = mService.mContext;
final AudioDeviceVolumeManager advm = (AudioDeviceVolumeManager) context.getSystemService(
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 709c13bc9704..336243f0289e 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -47,9 +47,9 @@ import static android.media.AudioManager.RINGER_MODE_NORMAL;
import static android.media.AudioManager.RINGER_MODE_SILENT;
import static android.media.AudioManager.RINGER_MODE_VIBRATE;
import static android.media.AudioManager.STREAM_SYSTEM;
-import static android.media.IAudioManagerNative.HardeningType;
import static android.media.audio.Flags.autoPublicVolumeApiHardening;
import static android.media.audio.Flags.automaticBtDeviceType;
+import static android.media.audio.Flags.cacheGetStreamMinMaxVolume;
import static android.media.audio.Flags.concurrentAudioRecordBypassPermission;
import static android.media.audio.Flags.featureSpatialAudioHeadtrackingLowLatency;
import static android.media.audio.Flags.focusFreezeTestApi;
@@ -898,6 +898,16 @@ public class AudioService extends IAudioService.Stub
public void permissionUpdateBarrier() {
AudioService.this.permissionUpdateBarrier();
}
+
+ /**
+ * Update mute state event for port
+ * @param portId Port id to update
+ * @param event the mute event containing info about the mute
+ */
+ @Override
+ public void portMuteEvent(int portId, int event) {
+ mPlaybackMonitor.portMuteEvent(portId, event, Binder.getCallingUid());
+ }
};
// List of binder death handlers for setMode() client processes.
@@ -4976,6 +4986,8 @@ public class AudioService extends IAudioService.Stub
+ ringMyCar());
pw.println("\tandroid.media.audio.Flags.concurrentAudioRecordBypassPermission:"
+ concurrentAudioRecordBypassPermission());
+ pw.println("\tandroid.media.audio.Flags.cacheGetStreamMinMaxVolume:"
+ + cacheGetStreamMinMaxVolume());
}
private void dumpAudioMode(PrintWriter pw) {
@@ -9366,6 +9378,12 @@ public class AudioService extends IAudioService.Stub
mIndexMinNoPerm = mIndexMin;
}
}
+ if (cacheGetStreamMinMaxVolume() && mStreamType == AudioSystem.STREAM_VOICE_CALL) {
+ if (DEBUG_VOL) {
+ Log.d(TAG, "Clear min volume cache from updateIndexFactors");
+ }
+ AudioManager.clearVolumeCache(AudioManager.VOLUME_MIN_CACHING_API);
+ }
final int status = AudioSystem.initStreamVolume(
mStreamType, indexMinVolCurve, indexMaxVolCurve);
@@ -9403,11 +9421,19 @@ public class AudioService extends IAudioService.Stub
* @param index minimum index expressed in "UI units", i.e. no 10x factor
*/
public void updateNoPermMinIndex(int index) {
+ boolean changedNoPermMinIndex =
+ cacheGetStreamMinMaxVolume() && (index * 10) != mIndexMinNoPerm;
mIndexMinNoPerm = index * 10;
if (mIndexMinNoPerm < mIndexMin) {
Log.e(TAG, "Invalid mIndexMinNoPerm for stream " + mStreamType);
mIndexMinNoPerm = mIndexMin;
}
+ if (changedNoPermMinIndex) {
+ if (DEBUG_VOL) {
+ Log.d(TAG, "Clear min volume cache from updateNoPermMinIndex");
+ }
+ AudioManager.clearVolumeCache(AudioManager.VOLUME_MIN_CACHING_API);
+ }
}
/**
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index e2e06b63c7d6..57b5febf4df0 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -435,6 +435,49 @@ public final class PlaybackActivityMonitor
/**
* Update event for port
* @param portId Port id to update
+ * @param event the mute event containing info about the mute
+ * @param binderUid Calling binder uid
+ */
+ public void portMuteEvent(int portId, @PlayerMuteEvent int event, int binderUid) {
+ if (!UserHandle.isCore(binderUid)) {
+ Log.e(TAG, "Forbidden operation from uid " + binderUid);
+ return;
+ }
+
+ synchronized (mPlayerLock) {
+ int piid;
+ if (portToPiidSimplification()) {
+ int idxOfPiid = mPiidToPortId.indexOfValue(portId);
+ if (idxOfPiid < 0) {
+ Log.w(TAG, "No piid assigned for invalid/internal port id " + portId);
+ return;
+ }
+ piid = mPiidToPortId.keyAt(idxOfPiid);
+ } else {
+ piid = mPortIdToPiid.get(portId, PLAYER_PIID_INVALID);
+ if (piid == PLAYER_PIID_INVALID) {
+ Log.w(TAG, "No piid assigned for invalid/internal port id " + portId);
+ return;
+ }
+ }
+ final AudioPlaybackConfiguration apc = mPlayers.get(piid);
+ if (apc == null) {
+ Log.w(TAG, "No AudioPlaybackConfiguration assigned for piid " + piid);
+ return;
+ }
+
+ if (apc.getPlayerType()
+ == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
+ // FIXME SoundPool not ready for state reporting
+ return;
+ }
+ mEventHandler.sendMessage(
+ mEventHandler.obtainMessage(MSG_IIL_UPDATE_PLAYER_MUTED_EVENT, piid, event, null));
+ }
+ }
+ /**
+ * Update event for port
+ * @param portId Port id to update
* @param event The new port event
* @param extras The values associated with this event
* @param binderUid Calling binder uid
@@ -479,15 +522,10 @@ public final class PlaybackActivityMonitor
return;
}
- if (event == AudioPlaybackConfiguration.PLAYER_UPDATE_MUTED) {
- mEventHandler.sendMessage(
- mEventHandler.obtainMessage(MSG_IIL_UPDATE_PLAYER_MUTED_EVENT, piid,
- portId,
- extras));
- } else if (event == AudioPlaybackConfiguration.PLAYER_UPDATE_FORMAT) {
+ if (event == AudioPlaybackConfiguration.PLAYER_UPDATE_FORMAT) {
mEventHandler.sendMessage(
mEventHandler.obtainMessage(MSG_IIL_UPDATE_PLAYER_FORMAT, piid,
- portId,
+ -1,
extras));
}
}
@@ -1695,9 +1733,7 @@ public final class PlaybackActivityMonitor
* event for player getting muted
* args:
* msg.arg1: piid
- * msg.arg2: port id
- * msg.obj: extras describing the mute reason
- * type: PersistableBundle
+ * msg.arg2: mute reason
*/
private static final int MSG_IIL_UPDATE_PLAYER_MUTED_EVENT = 2;
@@ -1705,7 +1741,6 @@ public final class PlaybackActivityMonitor
* event for player reporting playback format and spatialization status
* args:
* msg.arg1: piid
- * msg.arg2: port id
* msg.obj: extras describing the sample rate, channel mask, spatialized
* type: PersistableBundle
*/
@@ -1729,17 +1764,9 @@ public final class PlaybackActivityMonitor
break;
case MSG_IIL_UPDATE_PLAYER_MUTED_EVENT:
- // TODO: replace PersistableBundle with own struct
- PersistableBundle extras = (PersistableBundle) msg.obj;
- if (extras == null) {
- Log.w(TAG, "Received mute event with no extras");
- break;
- }
- @PlayerMuteEvent int eventValue = extras.getInt(EXTRA_PLAYER_EVENT_MUTE);
-
synchronized (mPlayerLock) {
int piid = msg.arg1;
-
+ @PlayerMuteEvent int eventValue = msg.arg2;
int[] eventValues = new int[1];
eventValues[0] = eventValue;
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index d435144b28c6..b9ce8c93dbde 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -21,6 +21,7 @@ import android.os.Build;
import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.Slog;
+import android.window.DesktopExperienceFlags;
import com.android.server.display.feature.flags.Flags;
import com.android.server.display.utils.DebugUtils;
@@ -250,7 +251,7 @@ public class DisplayManagerFlags {
);
private final FlagState mEnableDisplayContentModeManagementFlagState = new FlagState(
Flags.FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT,
- Flags::enableDisplayContentModeManagement
+ DesktopExperienceFlags.ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT::isTrue
);
private final FlagState mSubscribeGranularDisplayEvents = new FlagState(
diff --git a/services/core/java/com/android/server/input/InputGestureManager.java b/services/core/java/com/android/server/input/InputGestureManager.java
index fd755e3cefe2..be8a94149fdc 100644
--- a/services/core/java/com/android/server/input/InputGestureManager.java
+++ b/services/core/java/com/android/server/input/InputGestureManager.java
@@ -186,21 +186,11 @@ final class InputGestureManager {
KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT
),
createKeyGesture(
- KeyEvent.KEYCODE_DPAD_LEFT,
- KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT
- ),
- createKeyGesture(
KeyEvent.KEYCODE_DPAD_RIGHT,
KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT
),
createKeyGesture(
- KeyEvent.KEYCODE_DPAD_RIGHT,
- KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT
- ),
- createKeyGesture(
KeyEvent.KEYCODE_SLASH,
KeyEvent.META_META_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER
diff --git a/services/core/java/com/android/server/media/quality/MediaQualityService.java b/services/core/java/com/android/server/media/quality/MediaQualityService.java
index 2a5b779546d5..5259bcc78311 100644
--- a/services/core/java/com/android/server/media/quality/MediaQualityService.java
+++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java
@@ -29,6 +29,8 @@ import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.hardware.tv.mediaquality.AmbientBacklightColorFormat;
+import android.hardware.tv.mediaquality.DolbyAudioProcessing;
+import android.hardware.tv.mediaquality.DtsVirtualX;
import android.hardware.tv.mediaquality.IMediaQuality;
import android.hardware.tv.mediaquality.PictureParameter;
import android.hardware.tv.mediaquality.PictureParameters;
@@ -461,7 +463,7 @@ public class MediaQualityService extends SystemService {
}
if (params.containsKey(PictureQuality.PARAMETER_AUTO_SUPER_RESOLUTION_ENABLED)) {
pictureParams.add(PictureParameter.autoSuperResolutionEnabled(params.getBoolean(
- PictureQuality.PARAMETER_AUTO_SUPER_RESOLUTION_ENABLED)));
+ PictureQuality.PARAMETER_AUTO_SUPER_RESOLUTION_ENABLED)));
}
if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_RED_GAIN)) {
pictureParams.add(PictureParameter.colorTemperatureRedGain(params.getInt(
@@ -475,63 +477,214 @@ public class MediaQualityService extends SystemService {
pictureParams.add(PictureParameter.colorTemperatureBlueGain(params.getInt(
PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN)));
}
-
- /**
- * TODO: add conversion for following after adding to MediaQualityContract
- *
- * PictureParameter.levelRange
- * PictureParameter.gamutMapping
- * PictureParameter.pcMode
- * PictureParameter.lowLatency
- * PictureParameter.vrr
- * PictureParameter.cvrr
- * PictureParameter.hdmiRgbRange
- * PictureParameter.colorSpace
- * PictureParameter.panelInitMaxLuminceNits
- * PictureParameter.panelInitMaxLuminceValid
- * PictureParameter.gamma
- * PictureParameter.colorTemperatureRedOffset
- * PictureParameter.colorTemperatureGreenOffset
- * PictureParameter.colorTemperatureBlueOffset
- * PictureParameter.elevenPointRed
- * PictureParameter.elevenPointGreen
- * PictureParameter.elevenPointBlue
- * PictureParameter.lowBlueLight
- * PictureParameter.LdMode
- * PictureParameter.osdRedGain
- * PictureParameter.osdGreenGain
- * PictureParameter.osdBlueGain
- * PictureParameter.osdRedOffset
- * PictureParameter.osdGreenOffset
- * PictureParameter.osdBlueOffset
- * PictureParameter.osdHue
- * PictureParameter.osdSaturation
- * PictureParameter.osdContrast
- * PictureParameter.colorTunerSwitch
- * PictureParameter.colorTunerHueRed
- * PictureParameter.colorTunerHueGreen
- * PictureParameter.colorTunerHueBlue
- * PictureParameter.colorTunerHueCyan
- * PictureParameter.colorTunerHueMagenta
- * PictureParameter.colorTunerHueYellow
- * PictureParameter.colorTunerHueFlesh
- * PictureParameter.colorTunerSaturationRed
- * PictureParameter.colorTunerSaturationGreen
- * PictureParameter.colorTunerSaturationBlue
- * PictureParameter.colorTunerSaturationCyan
- * PictureParameter.colorTunerSaturationMagenta
- * PictureParameter.colorTunerSaturationYellow
- * PictureParameter.colorTunerSaturationFlesh
- * PictureParameter.colorTunerLuminanceRed
- * PictureParameter.colorTunerLuminanceGreen
- * PictureParameter.colorTunerLuminanceBlue
- * PictureParameter.colorTunerLuminanceCyan
- * PictureParameter.colorTunerLuminanceMagenta
- * PictureParameter.colorTunerLuminanceYellow
- * PictureParameter.colorTunerLuminanceFlesh
- * PictureParameter.activeProfile
- * PictureParameter.pictureQualityEventType
- */
+ if (params.containsKey(PictureQuality.PARAMETER_LEVEL_RANGE)) {
+ pictureParams.add(PictureParameter.levelRange(
+ (byte) params.getInt(PictureQuality.PARAMETER_LEVEL_RANGE)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_GAMUT_MAPPING)) {
+ pictureParams.add(PictureParameter.gamutMapping(params.getBoolean(
+ PictureQuality.PARAMETER_GAMUT_MAPPING)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_PC_MODE)) {
+ pictureParams.add(PictureParameter.pcMode(params.getBoolean(
+ PictureQuality.PARAMETER_PC_MODE)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_LOW_LATENCY)) {
+ pictureParams.add(PictureParameter.lowLatency(params.getBoolean(
+ PictureQuality.PARAMETER_LOW_LATENCY)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_VRR)) {
+ pictureParams.add(PictureParameter.vrr(params.getBoolean(
+ PictureQuality.PARAMETER_VRR)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_CVRR)) {
+ pictureParams.add(PictureParameter.cvrr(params.getBoolean(
+ PictureQuality.PARAMETER_CVRR)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_HDMI_RGB_RANGE)) {
+ pictureParams.add(PictureParameter.hdmiRgbRange(
+ (byte) params.getInt(PictureQuality.PARAMETER_HDMI_RGB_RANGE)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_SPACE)) {
+ pictureParams.add(PictureParameter.colorSpace(
+ (byte) params.getInt(PictureQuality.PARAMETER_COLOR_SPACE)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_PANEL_INIT_MAX_LUMINCE_NITS)) {
+ pictureParams.add(PictureParameter.panelInitMaxLuminceNits(
+ params.getInt(PictureQuality.PARAMETER_PANEL_INIT_MAX_LUMINCE_NITS)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_PANEL_INIT_MAX_LUMINCE_VALID)) {
+ pictureParams.add(PictureParameter.panelInitMaxLuminceValid(
+ params.getBoolean(PictureQuality.PARAMETER_PANEL_INIT_MAX_LUMINCE_VALID)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_GAMMA)) {
+ pictureParams.add(PictureParameter.gamma(
+ (byte) params.getInt(PictureQuality.PARAMETER_GAMMA)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TEMPERATURE_RED_OFFSET)) {
+ pictureParams.add(PictureParameter.colorTemperatureRedOffset(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TEMPERATURE_RED_OFFSET)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TEMPERATURE_GREEN_OFFSET)) {
+ pictureParams.add(PictureParameter.colorTemperatureGreenOffset(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TEMPERATURE_GREEN_OFFSET)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TEMPERATURE_BLUE_OFFSET)) {
+ pictureParams.add(PictureParameter.colorTemperatureBlueOffset(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TEMPERATURE_BLUE_OFFSET)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_ELEVEN_POINT_RED)) {
+ pictureParams.add(PictureParameter.elevenPointRed(params.getIntArray(
+ PictureQuality.PARAMETER_ELEVEN_POINT_RED)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_ELEVEN_POINT_GREEN)) {
+ pictureParams.add(PictureParameter.elevenPointGreen(params.getIntArray(
+ PictureQuality.PARAMETER_ELEVEN_POINT_GREEN)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_ELEVEN_POINT_BLUE)) {
+ pictureParams.add(PictureParameter.elevenPointBlue(params.getIntArray(
+ 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)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_LD_MODE)) {
+ pictureParams.add(PictureParameter.LdMode(
+ (byte) params.getInt(PictureQuality.PARAMETER_LD_MODE)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_OSD_RED_GAIN)) {
+ pictureParams.add(PictureParameter.osdRedGain(params.getInt(
+ PictureQuality.PARAMETER_OSD_RED_GAIN)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_OSD_GREEN_GAIN)) {
+ pictureParams.add(PictureParameter.osdGreenGain(params.getInt(
+ PictureQuality.PARAMETER_OSD_GREEN_GAIN)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_OSD_BLUE_GAIN)) {
+ pictureParams.add(PictureParameter.osdBlueGain(params.getInt(
+ PictureQuality.PARAMETER_OSD_BLUE_GAIN)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_OSD_RED_OFFSET)) {
+ pictureParams.add(PictureParameter.osdRedOffset(params.getInt(
+ PictureQuality.PARAMETER_OSD_RED_OFFSET)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_OSD_GREEN_OFFSET)) {
+ pictureParams.add(PictureParameter.osdGreenOffset(params.getInt(
+ PictureQuality.PARAMETER_OSD_GREEN_OFFSET)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_OSD_BLUE_OFFSET)) {
+ pictureParams.add(PictureParameter.osdBlueOffset(params.getInt(
+ PictureQuality.PARAMETER_OSD_BLUE_OFFSET)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_OSD_HUE)) {
+ pictureParams.add(PictureParameter.osdHue(params.getInt(
+ PictureQuality.PARAMETER_OSD_HUE)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_OSD_SATURATION)) {
+ pictureParams.add(PictureParameter.osdSaturation(params.getInt(
+ PictureQuality.PARAMETER_OSD_SATURATION)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_OSD_CONTRAST)) {
+ pictureParams.add(PictureParameter.osdContrast(params.getInt(
+ PictureQuality.PARAMETER_OSD_CONTRAST)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SWITCH)) {
+ pictureParams.add(PictureParameter.colorTunerSwitch(params.getBoolean(
+ PictureQuality.PARAMETER_COLOR_TUNER_SWITCH)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE_RED)) {
+ pictureParams.add(PictureParameter.colorTunerHueRed(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_HUE_RED)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE_GREEN)) {
+ pictureParams.add(PictureParameter.colorTunerHueGreen(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_HUE_GREEN)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE_BLUE)) {
+ pictureParams.add(PictureParameter.colorTunerHueBlue(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_HUE_BLUE)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE_CYAN)) {
+ pictureParams.add(PictureParameter.colorTunerHueCyan(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_HUE_CYAN)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE_MAGENTA)) {
+ pictureParams.add(PictureParameter.colorTunerHueMagenta(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_HUE_MAGENTA)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE_YELLOW)) {
+ pictureParams.add(PictureParameter.colorTunerHueYellow(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_HUE_YELLOW)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE_FLESH)) {
+ pictureParams.add(PictureParameter.colorTunerHueFlesh(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_HUE_FLESH)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_RED)) {
+ pictureParams.add(PictureParameter.colorTunerSaturationRed(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_RED)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_GREEN)) {
+ pictureParams.add(PictureParameter.colorTunerSaturationGreen(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_GREEN)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_BLUE)) {
+ pictureParams.add(PictureParameter.colorTunerSaturationBlue(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_BLUE)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_CYAN)) {
+ pictureParams.add(PictureParameter.colorTunerSaturationCyan(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_CYAN)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_MAGENTA)) {
+ pictureParams.add(PictureParameter.colorTunerSaturationMagenta(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_MAGENTA)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_YELLOW)) {
+ pictureParams.add(PictureParameter.colorTunerSaturationYellow(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_YELLOW)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_FLESH)) {
+ pictureParams.add(PictureParameter.colorTunerSaturationFlesh(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_FLESH)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_RED)) {
+ pictureParams.add(PictureParameter.colorTunerLuminanceRed(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_RED)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_GREEN)) {
+ pictureParams.add(PictureParameter.colorTunerLuminanceGreen(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_GREEN)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_BLUE)) {
+ pictureParams.add(PictureParameter.colorTunerLuminanceBlue(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_BLUE)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_CYAN)) {
+ pictureParams.add(PictureParameter.colorTunerLuminanceCyan(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_CYAN)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_MAGENTA)) {
+ pictureParams.add(PictureParameter.colorTunerLuminanceMagenta(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_MAGENTA)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_YELLOW)) {
+ pictureParams.add(PictureParameter.colorTunerLuminanceYellow(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_YELLOW)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_FLESH)) {
+ pictureParams.add(PictureParameter.colorTunerLuminanceFlesh(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_FLESH)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_ACTIVE_PROFILE)) {
+ pictureParams.add(PictureParameter.activeProfile(params.getBoolean(
+ PictureQuality.PARAMETER_ACTIVE_PROFILE)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_PICTURE_QUALITY_EVENT_TYPE)) {
+ pictureParams.add(PictureParameter.pictureQualityEventType(
+ (byte) params.getInt(PictureQuality.PARAMETER_PICTURE_QUALITY_EVENT_TYPE)));
+ }
return (PictureParameter[]) pictureParams.toArray();
}
@@ -775,6 +928,7 @@ public class MediaQualityService extends SystemService {
private SoundParameter[] convertPersistableBundleToSoundParameterList(
PersistableBundle params) {
+ //TODO: set EqualizerDetail
List<SoundParameter> soundParams = new ArrayList<>();
if (params.containsKey(SoundQuality.PARAMETER_BALANCE)) {
soundParams.add(SoundParameter.balance(params.getInt(
@@ -811,15 +965,54 @@ public class MediaQualityService extends SystemService {
soundParams.add(SoundParameter.surroundSoundEnabled(params.getBoolean(
SoundQuality.PARAMETER_DIGITAL_OUTPUT_DELAY_MILLIS)));
}
- //TODO: equalizerDetail
- //TODO: downmixMode
- //TODO: enhancedAudioReturnChannelEnabled
- //TODO: dolbyAudioProcessing
- //TODO: dolbyDialogueEnhancer
- //TODO: dtsVirtualX
- //TODO: digitalOutput
- //TODO: activeProfile
- //TODO: soundStyle
+ if (params.containsKey(SoundQuality.PARAMETER_EARC)) {
+ soundParams.add(SoundParameter.enhancedAudioReturnChannelEnabled(params.getBoolean(
+ SoundQuality.PARAMETER_EARC)));
+ }
+ if (params.containsKey(SoundQuality.PARAMETER_DOWN_MIX_MODE)) {
+ soundParams.add(SoundParameter.downmixMode((byte) params.getInt(
+ SoundQuality.PARAMETER_DOWN_MIX_MODE)));
+ }
+ if (params.containsKey(SoundQuality.PARAMETER_ACTIVE_PROFILE)) {
+ soundParams.add(SoundParameter.activeProfile(params.getBoolean(
+ SoundQuality.PARAMETER_ACTIVE_PROFILE)));
+ }
+ if (params.containsKey(SoundQuality.PARAMETER_SOUND_STYLE)) {
+ soundParams.add(SoundParameter.soundStyle((byte) params.getInt(
+ SoundQuality.PARAMETER_SOUND_STYLE)));
+ }
+ if (params.containsKey(SoundQuality.PARAMETER_DIGITAL_OUTPUT_MODE)) {
+ soundParams.add(SoundParameter.digitalOutput((byte) params.getInt(
+ SoundQuality.PARAMETER_DIGITAL_OUTPUT_MODE)));
+ }
+ if (params.containsKey(SoundQuality.PARAMETER_DIALOGUE_ENHANCER)) {
+ soundParams.add(SoundParameter.dolbyDialogueEnhancer((byte) params.getInt(
+ SoundQuality.PARAMETER_DIALOGUE_ENHANCER)));
+ }
+
+ DolbyAudioProcessing dab = new DolbyAudioProcessing();
+ dab.soundMode =
+ (byte) params.getInt(SoundQuality.PARAMETER_DOLBY_AUDIO_PROCESSING_SOUND_MODE);
+ dab.volumeLeveler =
+ params.getBoolean(SoundQuality.PARAMETER_DOLBY_AUDIO_PROCESSING_VOLUME_LEVELER);
+ dab.surroundVirtualizer = params.getBoolean(
+ SoundQuality.PARAMETER_DOLBY_AUDIO_PROCESSING_SURROUND_VIRTUALIZER);
+ dab.dolbyAtmos =
+ params.getBoolean(SoundQuality.PARAMETER_DOLBY_AUDIO_PROCESSING_DOLBY_ATMOS);
+ soundParams.add(SoundParameter.dolbyAudioProcessing(dab));
+
+ DtsVirtualX dts = new DtsVirtualX();
+ dts.tbHdx = params.getBoolean(SoundQuality.PARAMETER_DTS_VIRTUAL_X_TBHDX);
+ dts.limiter = params.getBoolean(SoundQuality.PARAMETER_DTS_VIRTUAL_X_LIMITER);
+ dts.truSurroundX = params.getBoolean(
+ SoundQuality.PARAMETER_DTS_VIRTUAL_X_TRU_SURROUND_X);
+ dts.truVolumeHd = params.getBoolean(SoundQuality.PARAMETER_DTS_VIRTUAL_X_TRU_VOLUME_HD);
+ dts.dialogClarity = params.getBoolean(
+ SoundQuality.PARAMETER_DTS_VIRTUAL_X_DIALOG_CLARITY);
+ dts.definition = params.getBoolean(SoundQuality.PARAMETER_DTS_VIRTUAL_X_DEFINITION);
+ dts.height = params.getBoolean(SoundQuality.PARAMETER_DTS_VIRTUAL_X_HEIGHT);
+ soundParams.add(SoundParameter.dtsVirtualX(dts));
+
return (SoundParameter[]) soundParams.toArray();
}
@@ -1472,7 +1665,13 @@ public class MediaQualityService extends SystemService {
RemoteCallbackList<IPictureProfileCallback> {
@Override
public void onCallbackDied(IPictureProfileCallback callback) {
- //todo
+ synchronized ("mPictureProfileLock") { //TODO: Change to lock
+ for (int i = 0; i < mUserStates.size(); i++) {
+ int userId = mUserStates.keyAt(i);
+ UserState userState = getOrCreateUserStateLocked(userId);
+ userState.mPictureProfileCallbackPidUidMap.remove(callback);
+ }
+ }
}
}
@@ -1480,7 +1679,13 @@ public class MediaQualityService extends SystemService {
RemoteCallbackList<ISoundProfileCallback> {
@Override
public void onCallbackDied(ISoundProfileCallback callback) {
- //todo
+ synchronized ("mSoundProfileLock") { //TODO: Change to lock
+ for (int i = 0; i < mUserStates.size(); i++) {
+ int userId = mUserStates.keyAt(i);
+ UserState userState = getOrCreateUserStateLocked(userId);
+ userState.mSoundProfileCallbackPidUidMap.remove(callback);
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 7de2815eba6b..1d376b4bdfd5 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -3612,13 +3612,16 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final long token = Binder.clearCallingIdentity();
try {
config = mCarrierConfigManager.getConfigForSubId(subId);
- tm = mContext.getSystemService(TelephonyManager.class);
+ tm = mContext.getSystemService(TelephonyManager.class).createForSubscriptionId(subId);
} finally {
Binder.restoreCallingIdentity(token);
}
- // First check: does caller have carrier privilege?
- if (tm != null && tm.hasCarrierPrivileges(subId)) {
+ // First check: does callingPackage have carrier privilege?
+ // Note that we can't call TelephonyManager.hasCarrierPrivileges() which will check if
+ // ourself has carrier privileges
+ if (tm != null && (tm.checkCarrierPrivilegesForPackage(callingPackage)
+ == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS)) {
return;
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index daa1042bb255..0d3c18ac339f 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -481,7 +481,8 @@ public class NotificationManagerService extends SystemService {
Adjustment.KEY_SENSITIVE_CONTENT,
Adjustment.KEY_RANKING_SCORE,
Adjustment.KEY_NOT_CONVERSATION,
- Adjustment.KEY_TYPE
+ Adjustment.KEY_TYPE,
+ Adjustment.KEY_SUMMARIZATION
};
static final Integer[] DEFAULT_ALLOWED_ADJUSTMENT_KEY_TYPES = new Integer[] {
@@ -631,6 +632,8 @@ public class NotificationManagerService extends SystemService {
// Minium number of sparse groups for a package before autogrouping them
private static final int AUTOGROUP_SPARSE_GROUPS_AT_COUNT = 3;
+ private static final Duration ZEN_BROADCAST_DELAY = Duration.ofMillis(250);
+
private IActivityManager mAm;
private ActivityTaskManagerInternal mAtm;
private ActivityManager mActivityManager;
@@ -3169,6 +3172,24 @@ public class NotificationManagerService extends SystemService {
sendRegisteredOnlyBroadcast(new Intent(action));
}
+ /**
+ * Schedules a broadcast to be sent to runtime receivers and DND-policy-access packages. The
+ * broadcast will be sent after {@link #ZEN_BROADCAST_DELAY}, unless a new broadcast is
+ * scheduled in the interim, in which case the previous one is dropped and the waiting period
+ * is <em>restarted</em>.
+ *
+ * <p>Note that this uses <em>equality of the {@link Intent#getAction}</em> as the criteria for
+ * deduplicating pending broadcasts, ignoring the extras and anything else. This is intentional
+ * so that e.g. rapidly changing some value A -> B -> C will only produce a broadcast for C
+ * (instead of every time because the extras are different).
+ */
+ private void sendZenBroadcastWithDelay(Intent intent) {
+ String token = "zen_broadcast:" + intent.getAction();
+ mHandler.removeCallbacksAndEqualMessages(token);
+ mHandler.postDelayed(() -> sendRegisteredOnlyBroadcast(intent), token,
+ ZEN_BROADCAST_DELAY.toMillis());
+ }
+
private void sendRegisteredOnlyBroadcast(Intent baseIntent) {
int[] userIds = mUmInternal.getProfileIds(mAmi.getCurrentUserId(), true);
if (Flags.nmBinderPerfReduceZenBroadcasts()) {
@@ -3362,14 +3383,25 @@ public class NotificationManagerService extends SystemService {
@GuardedBy("mNotificationLock")
private void updateEffectsSuppressorLocked() {
+ final long oldSuppressedEffects = mZenModeHelper.getSuppressedEffects();
final long updatedSuppressedEffects = calculateSuppressedEffects();
- if (updatedSuppressedEffects == mZenModeHelper.getSuppressedEffects()) return;
+ if (updatedSuppressedEffects == oldSuppressedEffects) return;
+
final List<ComponentName> suppressors = getSuppressors();
ZenLog.traceEffectsSuppressorChanged(
- mEffectsSuppressors, suppressors, updatedSuppressedEffects);
- mEffectsSuppressors = suppressors;
+ mEffectsSuppressors, suppressors, oldSuppressedEffects, updatedSuppressedEffects);
mZenModeHelper.setSuppressedEffects(updatedSuppressedEffects);
- sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
+
+ if (Flags.nmBinderPerfThrottleEffectsSuppressorBroadcast()) {
+ if (!suppressors.equals(mEffectsSuppressors)) {
+ mEffectsSuppressors = suppressors;
+ sendZenBroadcastWithDelay(
+ new Intent(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED));
+ }
+ } else {
+ mEffectsSuppressors = suppressors;
+ sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
+ }
}
private void exitIdle() {
@@ -3491,12 +3523,18 @@ public class NotificationManagerService extends SystemService {
}
private ArrayList<ComponentName> getSuppressors() {
- ArrayList<ComponentName> names = new ArrayList<ComponentName>();
+ ArrayList<ComponentName> names = new ArrayList<>();
for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
ArraySet<ComponentName> serviceInfoList = mListenersDisablingEffects.valueAt(i);
for (ComponentName info : serviceInfoList) {
- names.add(info);
+ if (Flags.nmBinderPerfThrottleEffectsSuppressorBroadcast()) {
+ if (!names.contains(info)) {
+ names.add(info);
+ }
+ } else {
+ names.add(info);
+ }
}
}
@@ -7212,7 +7250,7 @@ public class NotificationManagerService extends SystemService {
if (!mAssistants.isAdjustmentAllowed(potentialKey)) {
toRemove.add(potentialKey);
}
- if (notificationClassification() && adjustments.containsKey(KEY_TYPE)) {
+ if (notificationClassification() && potentialKey.equals(KEY_TYPE)) {
mAssistants.setNasUnsupportedDefaults(r.getSbn().getNormalizedUserId());
if (!mAssistants.isAdjustmentKeyTypeAllowed(adjustments.getInt(KEY_TYPE))) {
toRemove.add(potentialKey);
@@ -10395,7 +10433,8 @@ public class NotificationManagerService extends SystemService {
r.getRankingScore(),
r.isConversation(),
r.getProposedImportance(),
- r.hasSensitiveContent());
+ r.hasSensitiveContent(),
+ r.getSummarization());
extractorDataBefore.put(r.getKey(), extractorData);
mRankingHelper.extractSignals(r);
}
@@ -11715,7 +11754,8 @@ public class NotificationManagerService extends SystemService {
: (record.getRankingScore() > 0 ? RANKING_PROMOTED : RANKING_DEMOTED),
record.getNotification().isBubbleNotification(),
record.getProposedImportance(),
- hasSensitiveContent
+ hasSensitiveContent,
+ record.getSummarization()
);
rankings.add(ranking);
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 81af0d8a6d80..52101e336920 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -25,6 +25,7 @@ import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MIN;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+import static android.service.notification.Adjustment.KEY_SUMMARIZATION;
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE;
@@ -225,6 +226,8 @@ public final class NotificationRecord {
// type of the bundle if the notification was classified
private @Adjustment.Types int mBundleType = Adjustment.TYPE_OTHER;
+ private String mSummarization = null;
+
public NotificationRecord(Context context, StatusBarNotification sbn,
NotificationChannel channel) {
this.sbn = sbn;
@@ -589,6 +592,7 @@ public final class NotificationRecord {
pw.println(prefix + "shortcut=" + notification.getShortcutId()
+ " found valid? " + (mShortcutInfo != null));
pw.println(prefix + "mUserVisOverride=" + getPackageVisibilityOverride());
+ pw.println(prefix + "hasSummarization=" + (mSummarization != null));
}
private void dumpNotification(PrintWriter pw, String prefix, Notification notification,
@@ -811,6 +815,12 @@ public final class NotificationRecord {
Adjustment.KEY_TYPE,
mChannel.getId());
}
+ if ((android.app.Flags.nmSummarizationUi() || android.app.Flags.nmSummarization())
+ && signals.containsKey(KEY_SUMMARIZATION)) {
+ mSummarization = signals.getString(KEY_SUMMARIZATION);
+ EventLogTags.writeNotificationAdjusted(getKey(),
+ KEY_SUMMARIZATION, Boolean.toString(mSummarization != null));
+ }
if (!signals.isEmpty() && adjustment.getIssuer() != null) {
mAdjustmentIssuer = adjustment.getIssuer();
}
@@ -983,6 +993,13 @@ public final class NotificationRecord {
return null;
}
+ public String getSummarization() {
+ if ((android.app.Flags.nmSummarizationUi() || android.app.Flags.nmSummarization())) {
+ return mSummarization;
+ }
+ return null;
+ }
+
public boolean setIntercepted(boolean intercept) {
mIntercept = intercept;
mInterceptSet = true;
diff --git a/services/core/java/com/android/server/notification/NotificationRecordExtractorData.java b/services/core/java/com/android/server/notification/NotificationRecordExtractorData.java
index 3f4f7d3bbc38..9315ddc0d5b0 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordExtractorData.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordExtractorData.java
@@ -47,6 +47,7 @@ public final class NotificationRecordExtractorData {
private final boolean mIsConversation;
private final int mProposedImportance;
private final boolean mSensitiveContent;
+ private final String mSummarization;
NotificationRecordExtractorData(int position, int visibility, boolean showBadge,
boolean allowBubble, boolean isBubble, NotificationChannel channel, String groupKey,
@@ -54,7 +55,8 @@ public final class NotificationRecordExtractorData {
Integer userSentiment, Integer suppressVisually,
ArrayList<Notification.Action> systemSmartActions,
ArrayList<CharSequence> smartReplies, int importance, float rankingScore,
- boolean isConversation, int proposedImportance, boolean sensitiveContent) {
+ boolean isConversation, int proposedImportance, boolean sensitiveContent,
+ String summarization) {
mPosition = position;
mVisibility = visibility;
mShowBadge = showBadge;
@@ -73,6 +75,7 @@ public final class NotificationRecordExtractorData {
mIsConversation = isConversation;
mProposedImportance = proposedImportance;
mSensitiveContent = sensitiveContent;
+ mSummarization = summarization;
}
// Returns whether the provided NotificationRecord differs from the cached data in any way.
@@ -93,7 +96,8 @@ public final class NotificationRecordExtractorData {
|| !Objects.equals(mSmartReplies, r.getSmartReplies())
|| mImportance != r.getImportance()
|| mProposedImportance != r.getProposedImportance()
- || mSensitiveContent != r.hasSensitiveContent();
+ || mSensitiveContent != r.hasSensitiveContent()
+ || !Objects.equals(mSummarization, r.getSummarization());
}
// Returns whether the NotificationRecord has a change from this data for which we should
@@ -117,6 +121,7 @@ public final class NotificationRecordExtractorData {
|| !r.rankingScoreMatches(mRankingScore)
|| mIsConversation != r.isConversation()
|| mProposedImportance != r.getProposedImportance()
- || mSensitiveContent != r.hasSensitiveContent();
+ || mSensitiveContent != r.hasSensitiveContent()
+ || !Objects.equals(mSummarization, r.getSummarization());
}
}
diff --git a/services/core/java/com/android/server/notification/ZenLog.java b/services/core/java/com/android/server/notification/ZenLog.java
index 7e853d9d2d0b..49f93b8b7c16 100644
--- a/services/core/java/com/android/server/notification/ZenLog.java
+++ b/services/core/java/com/android/server/notification/ZenLog.java
@@ -140,8 +140,9 @@ public class ZenLog {
}
public static void traceEffectsSuppressorChanged(List<ComponentName> oldSuppressors,
- List<ComponentName> newSuppressors, long suppressedEffects) {
- append(TYPE_SUPPRESSOR_CHANGED, "suppressed effects:" + suppressedEffects + ","
+ List<ComponentName> newSuppressors, long oldSuppressedEffects, long suppressedEffects) {
+ append(TYPE_SUPPRESSOR_CHANGED, "suppressed effects:"
+ + oldSuppressedEffects + "->" + suppressedEffects + ","
+ componentListToString(oldSuppressors) + "->"
+ componentListToString(newSuppressors));
}
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index 822ff48c831c..048f2b6b0cbc 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -182,6 +182,16 @@ flag {
}
flag {
+ name: "nm_binder_perf_throttle_effects_suppressor_broadcast"
+ namespace: "systemui"
+ description: "Delay sending the ACTION_EFFECTS_SUPPRESSOR_CHANGED broadcast if it changes too often"
+ bug: "371776935"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "fix_calling_uid_from_cps"
namespace: "systemui"
description: "Correctly checks zen rule ownership when a CPS notifies with a Condition"
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
index 0b58c759b284..f011d283c8bb 100644
--- a/services/core/java/com/android/server/pm/DexOptHelper.java
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -96,6 +96,9 @@ import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
@@ -105,6 +108,11 @@ import java.util.function.Predicate;
public final class DexOptHelper {
private static final long SEVEN_DAYS_IN_MILLISECONDS = 7 * 24 * 60 * 60 * 1000;
+ @NonNull
+ private static final ThreadPoolExecutor sDexoptExecutor =
+ new ThreadPoolExecutor(1 /* corePoolSize */, 1 /* maximumPoolSize */,
+ 60 /* keepAliveTime */, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
+
private static boolean sArtManagerLocalIsInitialized = false;
private final PackageManagerService mPm;
@@ -113,6 +121,11 @@ public final class DexOptHelper {
// used, to make it available to the onDexoptDone callback.
private volatile long mBootDexoptStartTime;
+ static {
+ // Recycle the thread if it's not used for `keepAliveTime`.
+ sDexoptExecutor.allowsCoreThreadTimeOut();
+ }
+
DexOptHelper(PackageManagerService pm) {
mPm = pm;
}
@@ -746,43 +759,11 @@ public final class DexOptHelper {
*/
static void performDexoptIfNeeded(InstallRequest installRequest, DexManager dexManager,
Context context, PackageManagerTracedLock.RawLock installLock) {
-
// Construct the DexoptOptions early to see if we should skip running dexopt.
- //
- // Do not run PackageDexOptimizer through the local performDexOpt
- // method because `pkg` may not be in `mPackages` yet.
- //
- // Also, don't fail application installs if the dexopt step fails.
DexoptOptions dexoptOptions = getDexoptOptionsByInstallRequest(installRequest, dexManager);
- // Check whether we need to dexopt the app.
- //
- // NOTE: it is IMPORTANT to call dexopt:
- // - after doRename which will sync the package data from AndroidPackage and
- // its corresponding ApplicationInfo.
- // - after installNewPackageLIF or replacePackageLIF which will update result with the
- // uid of the application (pkg.applicationInfo.uid).
- // This update happens in place!
- //
- // We only need to dexopt if the package meets ALL of the following conditions:
- // 1) it is not an instant app or if it is then dexopt is enabled via gservices.
- // 2) it is not debuggable.
- // 3) it is not on Incremental File System.
- //
- // Note that we do not dexopt instant apps by default. dexopt can take some time to
- // complete, so we skip this step during installation. Instead, we'll take extra time
- // the first time the instant app starts. It's preferred to do it this way to provide
- // continuous progress to the useur instead of mysteriously blocking somewhere in the
- // middle of running an instant app. The default behaviour can be overridden
- // via gservices.
- //
- // Furthermore, dexopt may be skipped, depending on the install scenario and current
- // state of the device.
- //
- // TODO(b/174695087): instantApp and onIncremental should be removed and their install
- // path moved to SCENARIO_FAST.
+ boolean performDexopt =
+ DexOptHelper.shouldPerformDexopt(installRequest, dexoptOptions, context);
- final boolean performDexopt = DexOptHelper.shouldPerformDexopt(installRequest,
- dexoptOptions, context);
if (performDexopt) {
// dexopt can take long, and ArtService doesn't require installd, so we release
// the lock here and re-acquire the lock after dexopt is finished.
@@ -791,6 +772,7 @@ public final class DexOptHelper {
}
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
+ // Don't fail application installs if the dexopt step fails.
DexoptResult dexOptResult = DexOptHelper.dexoptPackageUsingArtService(
installRequest, dexoptOptions);
installRequest.onDexoptFinished(dexOptResult);
@@ -804,6 +786,41 @@ public final class DexOptHelper {
}
/**
+ * Same as above, but runs asynchronously.
+ */
+ static CompletableFuture<Void> performDexoptIfNeededAsync(InstallRequest installRequest,
+ DexManager dexManager, Context context) {
+ // Construct the DexoptOptions early to see if we should skip running dexopt.
+ DexoptOptions dexoptOptions = getDexoptOptionsByInstallRequest(installRequest, dexManager);
+ boolean performDexopt =
+ DexOptHelper.shouldPerformDexopt(installRequest, dexoptOptions, context);
+
+ if (performDexopt) {
+ return CompletableFuture
+ .runAsync(() -> {
+ try {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
+ // Don't fail application installs if the dexopt step fails.
+ // TODO(jiakaiz): Make this async in ART Service.
+ DexoptResult dexOptResult = DexOptHelper.dexoptPackageUsingArtService(
+ installRequest, dexoptOptions);
+ installRequest.onDexoptFinished(dexOptResult);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ }, sDexoptExecutor)
+ .exceptionally((t) -> {
+ // This should never happen. A normal dexopt failure should result in a
+ // DexoptResult.DEXOPT_FAILED, not an exception.
+ Slog.wtf(TAG, "Dexopt encountered a fatal error", t);
+ return null;
+ });
+ } else {
+ return CompletableFuture.completedFuture(null);
+ }
+ }
+
+ /**
* Use ArtService to perform dexopt by the given InstallRequest.
*/
static DexoptResult dexoptPackageUsingArtService(InstallRequest installRequest,
@@ -840,6 +857,20 @@ public final class DexOptHelper {
*/
static boolean shouldPerformDexopt(InstallRequest installRequest, DexoptOptions dexoptOptions,
Context context) {
+ // We only need to dexopt if the package meets ALL of the following conditions:
+ // 1) it is not an instant app or if it is then dexopt is enabled via gservices.
+ // 2) it is not debuggable.
+ // 3) it is not on Incremental File System.
+ //
+ // Note that we do not dexopt instant apps by default. dexopt can take some time to
+ // complete, so we skip this step during installation. Instead, we'll take extra time
+ // the first time the instant app starts. It's preferred to do it this way to provide
+ // continuous progress to the user instead of mysteriously blocking somewhere in the
+ // middle of running an instant app. The default behaviour can be overridden
+ // via gservices.
+ //
+ // Furthermore, dexopt may be skipped, depending on the install scenario and current
+ // state of the device.
final boolean isApex = ((installRequest.getScanFlags() & SCAN_AS_APEX) != 0);
final boolean instantApp = ((installRequest.getScanFlags() & SCAN_AS_INSTANT_APP) != 0);
final PackageSetting ps = installRequest.getScannedPackageSetting();
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 8eb5b6f11cb2..0c2782393879 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -1158,6 +1158,7 @@ final class InstallPackageHelper {
return;
}
request.setKeepArtProfile(true);
+ // TODO(b/388159696): Use performDexoptIfNeededAsync.
DexOptHelper.performDexoptIfNeeded(request, mDexManager, mContext, null);
}
}
diff --git a/services/core/java/com/android/server/pm/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java
index 14b0fc81fdd2..c62aaebf673b 100644
--- a/services/core/java/com/android/server/pm/UserManagerInternal.java
+++ b/services/core/java/com/android/server/pm/UserManagerInternal.java
@@ -584,6 +584,12 @@ public abstract class UserManagerInternal {
* Returns the user id of the main user, or {@link android.os.UserHandle#USER_NULL} if there is
* no main user.
*
+ * <p>NB: Features should ideally not limit functionality to the main user. Ideally, they
+ * should either work for all users or for all admin users. If a feature should only work for
+ * select users, its determination of which user should be done intelligently or be
+ * customizable. Not all devices support a main user, and the idea of singling out one user as
+ * special is contrary to overall multiuser goals.
+ *
* @see UserManager#isMainUser()
*/
public abstract @UserIdInt int getMainUserId();
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index a2c53e56b9c9..8cbccf5feead 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -3590,8 +3590,6 @@ public class UserManagerService extends IUserManager.Stub {
}
/**
- * @hide
- *
* Returns who set a user restriction on a user.
* Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
* @param restrictionKey the string key representing the restriction
@@ -6275,9 +6273,6 @@ public class UserManagerService extends IUserManager.Stub {
}
}
- /**
- * @hide
- */
@Override
public @NonNull UserInfo createRestrictedProfileWithThrow(
@Nullable String name, @UserIdInt int parentUserId)
@@ -8504,7 +8499,6 @@ public class UserManagerService extends IUserManager.Stub {
}
/**
- * @hide
* Checks whether to show a notification for sounds (e.g., alarms, timers, etc.) from
* background users.
*/
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 7f511e1e2aa1..283979483e73 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3801,10 +3801,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
true /* leftOrTop */);
notifyKeyGestureCompleted(event,
KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT);
- } else if (event.isAltPressed()) {
- setSplitscreenFocus(true /* leftOrTop */);
- notifyKeyGestureCompleted(event,
- KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT);
} else {
notifyKeyGestureCompleted(event,
KeyGestureEvent.KEY_GESTURE_TYPE_BACK);
@@ -3821,11 +3817,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
notifyKeyGestureCompleted(event,
KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT);
return true;
- } else if (event.isAltPressed()) {
- setSplitscreenFocus(false /* leftOrTop */);
- notifyKeyGestureCompleted(event,
- KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT);
- return true;
}
}
break;
@@ -4241,9 +4232,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
case KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION:
case KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE:
case KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT:
- case KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT:
case KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT:
- case KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT:
case KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER:
case KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_UP:
case KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_DOWN:
@@ -4379,22 +4368,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
true /* leftOrTop */);
}
return true;
- case KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT:
- if (complete) {
- setSplitscreenFocus(true /* leftOrTop */);
- }
- return true;
case KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT:
if (complete) {
moveFocusedTaskToStageSplit(getTargetDisplayIdForKeyGestureEvent(event),
false /* leftOrTop */);
}
return true;
- case KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT:
- if (complete) {
- setSplitscreenFocus(false /* leftOrTop */);
- }
- return true;
case KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER:
if (complete) {
toggleKeyboardShortcutsMenu(deviceId);
@@ -5084,13 +5063,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
- private void setSplitscreenFocus(boolean leftOrTop) {
- StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
- if (statusbar != null) {
- statusbar.setSplitscreenFocus(leftOrTop);
- }
- }
-
void launchHomeFromHotKey(int displayId) {
launchHomeFromHotKey(displayId, true /* awakenFromDreams */, true /*respectKeyguard*/);
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 4827c9f414ad..0ed522805bef 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -56,12 +56,12 @@ public interface StatusBarManagerInternal {
void toggleKeyboardShortcutsMenu(int deviceId);
/**
- * Used by InputMethodManagerService to notify the IME status.
+ * Sets the new IME window status.
*
- * @param displayId The display to which the IME is bound to.
- * @param vis The IME visibility.
- * @param backDisposition The IME back disposition.
- * @param showImeSwitcher {@code true} when the IME switcher button should be shown.
+ * @param displayId The id of the display to which the IME is bound.
+ * @param vis The IME window visibility.
+ * @param backDisposition The IME back disposition mode.
+ * @param showImeSwitcher Whether the IME Switcher button should be shown.
*/
void setImeWindowStatus(int displayId, @ImeWindowVisibility int vis,
@BackDispositionMode int backDisposition, boolean showImeSwitcher);
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index a8deeeac311d..83e146df3e53 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -118,6 +118,7 @@ import android.service.wallpaper.WallpaperService;
import android.system.ErrnoException;
import android.system.Os;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.EventLog;
import android.util.IntArray;
import android.util.Slog;
@@ -160,6 +161,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -772,6 +774,12 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
private final ComponentName mImageWallpaper;
/**
+ * Name of the component that is used when the user-selected wallpaper is incompatible with the
+ * display's resolution or aspect ratio.
+ */
+ @Nullable private final ComponentName mFallbackWallpaperComponent;
+
+ /**
* Default image wallpaper shall never changed after system service started, caching it when we
* first read the image file.
*/
@@ -799,12 +807,38 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
final WallpaperDisplayHelper mWallpaperDisplayHelper;
final WallpaperCropper mWallpaperCropper;
- private boolean supportsMultiDisplay(WallpaperConnection connection) {
- if (connection != null) {
- return connection.mInfo == null // This is image wallpaper
- || connection.mInfo.supportsMultipleDisplays();
+ // TODO(b/384519749): Remove this set after we introduce the aspect ratio check.
+ private final Set<Integer> mWallpaperCompatibleDisplaysForTest = new ArraySet<>();
+
+ private boolean isWallpaperCompatibleForDisplay(int displayId, WallpaperConnection connection) {
+ if (connection == null) {
+ return false;
}
- return false;
+ // Non image wallpaper.
+ if (connection.mInfo != null) {
+ return connection.mInfo.supportsMultipleDisplays();
+ }
+
+ // Image wallpaper
+ if (enableConnectedDisplaysWallpaper()) {
+ // TODO(b/384519749): check display's resolution and image wallpaper cropped image
+ // aspect ratio.
+ return displayId == DEFAULT_DISPLAY
+ || mWallpaperCompatibleDisplaysForTest.contains(displayId);
+ }
+ // When enableConnectedDisplaysWallpaper is off, we assume the image wallpaper supports all
+ // usable displays.
+ return true;
+ }
+
+ @VisibleForTesting
+ void addWallpaperCompatibleDisplayForTest(int displayId) {
+ mWallpaperCompatibleDisplaysForTest.add(displayId);
+ }
+
+ @VisibleForTesting
+ void removeWallpaperCompatibleDisplayForTest(int displayId) {
+ mWallpaperCompatibleDisplaysForTest.remove(displayId);
}
private void updateFallbackConnection() {
@@ -815,7 +849,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
Slog.w(TAG, "Fallback wallpaper connection has not been created yet!!");
return;
}
- if (supportsMultiDisplay(systemConnection)) {
+ // TODO(b/384520326) Passing DEFAULT_DISPLAY temporarily before we revamp the
+ // multi-display supports.
+ if (isWallpaperCompatibleForDisplay(DEFAULT_DISPLAY, systemConnection)) {
if (fallbackConnection.mDisplayConnector.size() != 0) {
fallbackConnection.forEachDisplayConnector(connector -> {
if (connector.mEngine != null) {
@@ -990,16 +1026,14 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
private void initDisplayState() {
// Do not initialize fallback wallpaper
if (!mWallpaper.equals(mFallbackWallpaper)) {
- if (supportsMultiDisplay(this)) {
- // The system wallpaper is image wallpaper or it can supports multiple displays.
- appendConnectorWithCondition(display ->
- mWallpaperDisplayHelper.isUsableDisplay(display, mClientUid));
- } else {
- // The system wallpaper does not support multiple displays, so just attach it on
- // default display.
- mDisplayConnector.append(DEFAULT_DISPLAY,
- new DisplayConnector(DEFAULT_DISPLAY));
- }
+ appendConnectorWithCondition(display -> {
+ final int displayId = display.getDisplayId();
+ if (display.getDisplayId() == DEFAULT_DISPLAY) {
+ return true;
+ }
+ return mWallpaperDisplayHelper.isUsableDisplay(display, mClientUid)
+ && isWallpaperCompatibleForDisplay(displayId, /* connection= */ this);
+ });
}
}
@@ -1601,6 +1635,12 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
mShuttingDown = false;
mImageWallpaper = ComponentName.unflattenFromString(
context.getResources().getString(R.string.image_wallpaper_component));
+ if (enableConnectedDisplaysWallpaper()) {
+ mFallbackWallpaperComponent = ComponentName.unflattenFromString(
+ context.getResources().getString(R.string.fallback_wallpaper_component));
+ } else {
+ mFallbackWallpaperComponent = null;
+ }
ComponentName defaultComponent = WallpaperManager.getCmfDefaultWallpaperComponent(context);
mDefaultWallpaperComponent = defaultComponent == null ? mImageWallpaper : defaultComponent;
mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
@@ -3613,6 +3653,13 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
if (componentName != null && !componentName.equals(mImageWallpaper)) {
// The requested component is not the static wallpaper service, so make sure it's
// actually a wallpaper service.
+ if (mFallbackWallpaperComponent != null
+ && componentName.equals(mFallbackWallpaperComponent)) {
+ // The fallback wallpaper does not declare WallpaperService.SERVICE_INTERFACE
+ // action in its intent filter to prevent it from being listed in the wallpaper
+ // picker. And thus, use explicit intent to query the metadata.
+ intent = new Intent().setComponent(mFallbackWallpaperComponent);
+ }
List<ResolveInfo> ris =
mIPackageManager.queryIntentServices(intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
@@ -3971,7 +4018,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
for (int i = 0; i < wallpapers.size(); i++) {
WallpaperData wallpaper = wallpapers.get(i);
- if (supportsMultiDisplay(wallpaper.connection)) {
+ if (isWallpaperCompatibleForDisplay(displayId, wallpaper.connection)) {
final DisplayConnector connector =
wallpaper.connection.getDisplayConnectorOrCreate(displayId);
if (connector != null) {
@@ -3993,7 +4040,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
mFallbackWallpaper.mWhich = useFallbackWallpaperWhich;
} else {
- if (supportsMultiDisplay(mLastWallpaper.connection)) {
+ if (isWallpaperCompatibleForDisplay(displayId, mLastWallpaper.connection)) {
final DisplayConnector connector =
mLastWallpaper.connection.getDisplayConnectorOrCreate(displayId);
if (connector == null) return;
@@ -4100,8 +4147,14 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
mFallbackWallpaper.allowBackup = false;
mFallbackWallpaper.wallpaperId = makeWallpaperIdLocked();
mFallbackWallpaper.mBindSource = BindSource.INITIALIZE_FALLBACK;
- bindWallpaperComponentLocked(mDefaultWallpaperComponent, true, false,
- mFallbackWallpaper, null);
+ if (mFallbackWallpaperComponent == null) {
+ bindWallpaperComponentLocked(mDefaultWallpaperComponent, true, false,
+ mFallbackWallpaper, null);
+ } else {
+ mFallbackWallpaper.mWhich = FLAG_SYSTEM | FLAG_LOCK;
+ bindWallpaperComponentLocked(mFallbackWallpaperComponent, true, false,
+ mFallbackWallpaper, null);
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 1d12c561f118..064ef1aa0eff 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -3231,8 +3231,8 @@ final class ActivityRecord extends WindowToken {
* Returns {@code true} if the fixed orientation, aspect ratio, resizability of the application
* can be ignored.
*/
- static boolean canBeUniversalResizeable(ApplicationInfo appInfo, WindowManagerService wms,
- boolean isLargeScreen, boolean forActivity) {
+ static boolean canBeUniversalResizeable(@NonNull ApplicationInfo appInfo,
+ WindowManagerService wms, boolean isLargeScreen, boolean forActivity) {
if (appInfo.category == ApplicationInfo.CATEGORY_GAME) {
return false;
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index d4f9c0901162..cf111cdbcc6a 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -2154,6 +2154,16 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
}
+ /**
+ * @return ehether the application could be universal resizeable on a large screen,
+ * ignoring any overrides
+ */
+ @Override
+ public boolean canBeUniversalResizeable(@NonNull ApplicationInfo appInfo) {
+ return ActivityRecord.canBeUniversalResizeable(appInfo, mWindowManager,
+ /* isLargeScreen */ true, /* forActivity */ false);
+ }
+
@Override
public void removeAllVisibleRecentTasks() {
mAmInternal.enforceCallingPermission(REMOVE_TASKS, "removeAllVisibleRecentTasks()");
diff --git a/services/core/jni/com_android_server_utils_AnrTimer.cpp b/services/core/jni/com_android_server_utils_AnrTimer.cpp
index 2add5b09f15b..24909ac6150b 100644
--- a/services/core/jni/com_android_server_utils_AnrTimer.cpp
+++ b/services/core/jni/com_android_server_utils_AnrTimer.cpp
@@ -101,6 +101,9 @@ nsecs_t now() {
return systemTime(SYSTEM_TIME_MONOTONIC);
}
+// The current process. This is cached here on startup.
+const pid_t sThisProcess = getpid();
+
// Return true if the process exists and false if we cannot know.
bool processExists(pid_t pid) {
char path[PATH_MAX];
@@ -726,7 +729,7 @@ class AnrTimerService::Timer {
uid(uid),
timeout(timeout),
extend(extend),
- freeze(pid != 0 && freeze),
+ freeze(freeze),
split(trace.earlyTimeout),
action(trace.action),
status(Running),
@@ -1188,8 +1191,11 @@ const char* AnrTimerService::statusString(Status s) {
}
AnrTimerService::timer_id_t AnrTimerService::start(int pid, int uid, nsecs_t timeout) {
+ // Use the freezer only if the pid is not 0 (a nonsense value) and the pid is not self.
+ // Freezing the current process is a fatal error.
+ bool useFreezer = freeze_ && (pid != 0) && (pid != sThisProcess);
AutoMutex _l(lock_);
- Timer t(pid, uid, timeout, extend_, freeze_, tracer_.getConfig(pid));
+ Timer t(pid, uid, timeout, extend_, useFreezer, tracer_.getConfig(pid));
insertLocked(t);
t.start();
counters_.started++;
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java
index 27eada013642..89b48bad2358 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java
@@ -16,8 +16,6 @@
package com.android.server.am;
-import static android.os.PowerWhitelistManager.REASON_NOTIFICATION_SERVICE;
-import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
import static android.os.Process.INVALID_UID;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -29,11 +27,9 @@ import static com.android.server.am.PendingIntentRecord.CANCEL_REASON_OWNER_CANC
import static com.android.server.am.PendingIntentRecord.CANCEL_REASON_OWNER_FORCE_STOPPED;
import static com.android.server.am.PendingIntentRecord.CANCEL_REASON_SUPERSEDED;
import static com.android.server.am.PendingIntentRecord.CANCEL_REASON_USER_STOPPED;
-import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER;
import static com.android.server.am.PendingIntentRecord.cancelReasonToString;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
@@ -43,11 +39,9 @@ import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
-import android.app.BackgroundStartPrivileges;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.pm.IPackageManager;
-import android.os.Binder;
import android.os.Looper;
import android.os.UserHandle;
@@ -185,34 +179,6 @@ public class PendingIntentControllerTest {
}
}
- @Test
- public void testClearAllowBgActivityStartsClearsToken() {
- final PendingIntentRecord pir = createPendingIntentRecord(0);
- Binder token = new Binder();
- pir.setAllowBgActivityStarts(token, FLAG_ACTIVITY_SENDER);
- assertEquals(BackgroundStartPrivileges.allowBackgroundActivityStarts(token),
- pir.getBackgroundStartPrivilegesForActivitySender(token));
- pir.clearAllowBgActivityStarts(token);
- assertEquals(BackgroundStartPrivileges.NONE,
- pir.getBackgroundStartPrivilegesForActivitySender(token));
- }
-
- @Test
- public void testClearAllowBgActivityStartsClearsDuration() {
- final PendingIntentRecord pir = createPendingIntentRecord(0);
- Binder token = new Binder();
- pir.setAllowlistDurationLocked(token, 1000,
- TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, REASON_NOTIFICATION_SERVICE,
- "NotificationManagerService");
- PendingIntentRecord.TempAllowListDuration allowlistDurationLocked =
- pir.getAllowlistDurationLocked(token);
- assertEquals(1000, allowlistDurationLocked.duration);
- pir.clearAllowBgActivityStarts(token);
- PendingIntentRecord.TempAllowListDuration allowlistDurationLockedAfterClear =
- pir.getAllowlistDurationLocked(token);
- assertNull(allowlistDurationLockedAfterClear);
- }
-
private void assertCancelReason(int expectedReason, int actualReason) {
final String errMsg = "Expected: " + cancelReasonToString(expectedReason)
+ "; Actual: " + cancelReasonToString(actualReason);
diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
index 1cf655675a30..793ba7bc89bd 100644
--- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
@@ -144,6 +144,8 @@ public class WallpaperManagerServiceTests {
private static ComponentName sImageWallpaperComponentName;
private static ComponentName sDefaultWallpaperComponent;
+ private static ComponentName sFallbackWallpaperComponentName;
+
private IPackageManager mIpm = AppGlobals.getPackageManager();
@Mock
@@ -195,6 +197,8 @@ public class WallpaperManagerServiceTests {
sContext.getResources().getString(R.string.image_wallpaper_component));
// Mock default wallpaper as image wallpaper if there is no pre-defined default wallpaper.
sDefaultWallpaperComponent = WallpaperManager.getCmfDefaultWallpaperComponent(sContext);
+ sFallbackWallpaperComponentName = ComponentName.unflattenFromString(
+ sContext.getResources().getString(R.string.fallback_wallpaper_component));
if (sDefaultWallpaperComponent == null) {
sDefaultWallpaperComponent = sImageWallpaperComponentName;
@@ -205,6 +209,9 @@ public class WallpaperManagerServiceTests {
}
sContext.addMockService(sImageWallpaperComponentName, sWallpaperService);
+ if (sFallbackWallpaperComponentName != null) {
+ sContext.addMockService(sFallbackWallpaperComponentName, sWallpaperService);
+ }
}
@AfterClass
@@ -216,6 +223,7 @@ public class WallpaperManagerServiceTests {
LocalServices.removeServiceForTest(WindowManagerInternal.class);
sImageWallpaperComponentName = null;
sDefaultWallpaperComponent = null;
+ sFallbackWallpaperComponentName = null;
reset(sContext);
}
@@ -306,6 +314,7 @@ public class WallpaperManagerServiceTests {
* Tests that internal basic data should be correct after boot up.
*/
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WALLPAPER)
public void testDataCorrectAfterBoot() {
mService.switchUser(USER_SYSTEM, null);
@@ -719,7 +728,7 @@ public class WallpaperManagerServiceTests {
final int testDisplayId = 2;
setUpDisplays(List.of(DEFAULT_DISPLAY, testDisplayId));
- // WHEN displayId, 2, is ready.
+ // WHEN display ID, 2, is ready.
WallpaperManagerInternal wallpaperManagerInternal = LocalServices.getService(
WallpaperManagerInternal.class);
wallpaperManagerInternal.onDisplayReady(testDisplayId);
@@ -759,7 +768,7 @@ public class WallpaperManagerServiceTests {
final int testDisplayId = 2;
setUpDisplays(List.of(DEFAULT_DISPLAY, testDisplayId));
- // WHEN displayId, 2, is ready.
+ // WHEN display ID, 2, is ready.
WallpaperManagerInternal wallpaperManagerInternal = LocalServices.getService(
WallpaperManagerInternal.class);
wallpaperManagerInternal.onDisplayReady(testDisplayId);
@@ -791,6 +800,40 @@ public class WallpaperManagerServiceTests {
/* info= */ any(),
/* description= */ any());
}
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WALLPAPER)
+ public void displayAdded_wallpaperIncompatibleForDisplay_shouldAttachFallbackWallpaperService()
+ throws Exception {
+ final int testUserId = USER_SYSTEM;
+ mService.switchUser(testUserId, null);
+ IWallpaperService mockIWallpaperService = mock(IWallpaperService.class);
+ mService.mFallbackWallpaper.connection.mService = mockIWallpaperService;
+ // GIVEN there are two displays: DEFAULT_DISPLAY, 2
+ final int testDisplayId = 2;
+ setUpDisplays(List.of(DEFAULT_DISPLAY, testDisplayId));
+ // GIVEN the wallpaper isn't compatible with display ID, 2
+ mService.removeWallpaperCompatibleDisplayForTest(testDisplayId);
+
+ // WHEN display ID, 2, is ready.
+ WallpaperManagerInternal wallpaperManagerInternal = LocalServices.getService(
+ WallpaperManagerInternal.class);
+ wallpaperManagerInternal.onDisplayReady(testDisplayId);
+
+ // Then there is a connection established for the fallback wallpaper for display ID, 2.
+ verify(mockIWallpaperService).attach(
+ /* connection= */ eq(mService.mFallbackWallpaper.connection),
+ /* windowToken= */ any(),
+ /* windowType= */ anyInt(),
+ /* isPreview= */ anyBoolean(),
+ /* reqWidth= */ anyInt(),
+ /* reqHeight= */ anyInt(),
+ /* padding= */ any(),
+ /* displayId= */ eq(testDisplayId),
+ /* which= */ eq(FLAG_SYSTEM | FLAG_LOCK),
+ /* info= */ any(),
+ /* description= */ any());
+ }
// Verify a secondary display added end
// Verify a secondary display removed started
@@ -810,7 +853,7 @@ public class WallpaperManagerServiceTests {
// GIVEN there are two displays: DEFAULT_DISPLAY, 2
final int testDisplayId = 2;
setUpDisplays(List.of(DEFAULT_DISPLAY, testDisplayId));
- // GIVEN wallpaper connections have been established for displayID, 2.
+ // GIVEN wallpaper connections have been established for display ID, 2.
WallpaperManagerInternal wallpaperManagerInternal = LocalServices.getService(
WallpaperManagerInternal.class);
wallpaperManagerInternal.onDisplayReady(testDisplayId);
@@ -818,11 +861,11 @@ public class WallpaperManagerServiceTests {
WallpaperManagerService.DisplayConnector displayConnector =
wallpaper.connection.getDisplayConnectorOrCreate(testDisplayId);
- // WHEN displayId, 2, is removed.
+ // WHEN display ID, 2, is removed.
DisplayListener displayListener = displayListenerCaptor.getValue();
displayListener.onDisplayRemoved(testDisplayId);
- // Then the wallpaper connection for displayId, 2, is detached.
+ // Then the wallpaper connection for display ID, 2, is detached.
verify(mockIWallpaperService).detach(eq(displayConnector.mToken));
}
@@ -848,27 +891,75 @@ public class WallpaperManagerServiceTests {
// GIVEN there are two displays: DEFAULT_DISPLAY, 2
final int testDisplayId = 2;
setUpDisplays(List.of(DEFAULT_DISPLAY, testDisplayId));
- // GIVEN wallpaper connections have been established for displayID, 2.
+ // GIVEN wallpaper connections have been established for display ID, 2.
WallpaperManagerInternal wallpaperManagerInternal = LocalServices.getService(
WallpaperManagerInternal.class);
wallpaperManagerInternal.onDisplayReady(testDisplayId);
- // Save displayConnectors for displayId 2 before display removal.
+ // Save displayConnectors for display ID, 2, before display removal.
WallpaperManagerService.DisplayConnector systemWallpaperDisplayConnector =
systemWallpaper.connection.getDisplayConnectorOrCreate(testDisplayId);
WallpaperManagerService.DisplayConnector lockWallpaperDisplayConnector =
lockWallpaper.connection.getDisplayConnectorOrCreate(testDisplayId);
- // WHEN displayId, 2, is removed.
+ // WHEN display ID, 2, is removed.
DisplayListener displayListener = displayListenerCaptor.getValue();
displayListener.onDisplayRemoved(testDisplayId);
- // Then the system wallpaper connection for displayId, 2, is detached.
+ // Then the system wallpaper connection for display ID, 2, is detached.
verify(mockIWallpaperService).detach(eq(systemWallpaperDisplayConnector.mToken));
- // Then the lock wallpaper connection for displayId, 2, is detached.
+ // Then the lock wallpaper connection for display ID, 2, is detached.
verify(mockIWallpaperService).detach(eq(lockWallpaperDisplayConnector.mToken));
}
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WALLPAPER)
+ public void displayRemoved_fallbackWallpaper_shouldDetachFallbackWallpaperService()
+ throws Exception {
+ ArgumentCaptor<DisplayListener> displayListenerCaptor = ArgumentCaptor.forClass(
+ DisplayListener.class);
+ verify(mDisplayManager).registerDisplayListener(displayListenerCaptor.capture(), eq(null));
+ final int testUserId = USER_SYSTEM;
+ mService.switchUser(testUserId, null);
+ IWallpaperService mockIWallpaperService = mock(IWallpaperService.class);
+ mService.mFallbackWallpaper.connection.mService = mockIWallpaperService;
+ // GIVEN there are two displays: DEFAULT_DISPLAY, 2
+ final int testDisplayId = 2;
+ setUpDisplays(List.of(DEFAULT_DISPLAY, testDisplayId));
+ // GIVEN display ID, 2, is incompatible with the wallpaper.
+ mService.removeWallpaperCompatibleDisplayForTest(testDisplayId);
+ // GIVEN wallpaper connections have been established for display ID, 2.
+ WallpaperManagerInternal wallpaperManagerInternal = LocalServices.getService(
+ WallpaperManagerInternal.class);
+ wallpaperManagerInternal.onDisplayReady(testDisplayId);
+ // Save fallback wallpaper displayConnector for display ID, 2, before display removal.
+ WallpaperManagerService.DisplayConnector fallbackWallpaperConnector =
+ mService.mFallbackWallpaper.connection.getDisplayConnectorOrCreate(testDisplayId);
+
+ // WHEN displayId, 2, is removed.
+ DisplayListener displayListener = displayListenerCaptor.getValue();
+ displayListener.onDisplayRemoved(testDisplayId);
+
+ // Then the fallback wallpaper connection for display ID, 2, is detached.
+ verify(mockIWallpaperService).detach(eq(fallbackWallpaperConnector.mToken));
+ }
// Verify a secondary display removed ended
+ // Test fallback wallpaper after enabling connected display supports.
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WALLPAPER)
+ public void testFallbackWallpaperForConnectedDisplays() {
+ final WallpaperData fallbackData = mService.mFallbackWallpaper;
+
+ assertWithMessage(
+ "The connected wallpaper component should be fallback wallpaper component from "
+ + "config file.")
+ .that(fallbackData.connection.mWallpaper.getComponent())
+ .isEqualTo(sFallbackWallpaperComponentName);
+ assertWithMessage("Fallback wallpaper should support both lock & system.")
+ .that(fallbackData.mWhich)
+ .isEqualTo(FLAG_LOCK | FLAG_SYSTEM);
+ }
+
// Verify that after continue switch user from userId 0 to lastUserId, the wallpaper data for
// non-current user must not bind to wallpaper service.
private void verifyNoConnectionBeforeLastUser(int lastUserId) {
@@ -893,11 +984,11 @@ public class WallpaperManagerServiceTests {
final WallpaperData lastLockData = mService.mLastLockWallpaper;
assertWithMessage("Last wallpaper must not be null").that(lastLockData).isNotNull();
assertWithMessage("Last wallpaper component must be equals.")
- .that(expectedComponent)
- .isEqualTo(lastLockData.getComponent());
+ .that(lastLockData.getComponent())
+ .isEqualTo(expectedComponent);
assertWithMessage("The user id in last wallpaper should be the last switched user")
- .that(lastUserId)
- .isEqualTo(lastLockData.userId);
+ .that(lastLockData.userId)
+ .isEqualTo(lastUserId);
assertWithMessage("Must exist user data connection on last wallpaper data")
.that(lastLockData.connection)
.isNotNull();
@@ -946,6 +1037,7 @@ public class WallpaperManagerServiceTests {
doReturn(mockDisplay).when(mDisplayManager).getDisplay(eq(displayId));
doReturn(displayId).when(mockDisplay).getDisplayId();
doReturn(true).when(mockDisplay).hasAccess(anyInt());
+ mService.addWallpaperCompatibleDisplayForTest(displayId);
}
doReturn(mockDisplays).when(mDisplayManager).getDisplays();
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 5602fb76e6f5..28e5be505556 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -68,7 +68,6 @@ import android.annotation.UserIdInt;
import android.app.PendingIntent;
import android.app.RemoteAction;
import android.app.admin.DevicePolicyManager;
-import android.app.ecm.EnhancedConfirmationManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -2120,23 +2119,6 @@ public class AccessibilityManagerServiceTest {
}
@Test
- @EnableFlags({android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED,
- android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS})
- public void isAccessibilityTargetAllowed_nonSystemUserId_useEcmWithNonSystemUserId() {
- String fakePackageName = "FAKE_PACKAGE_NAME";
- int uid = 0; // uid is not used in the actual implementation when flags are on
- int userId = mTestableContext.getUserId() + 1234;
- when(mDevicePolicyManager.getPermittedAccessibilityServices(userId)).thenReturn(
- List.of(fakePackageName));
- Context mockUserContext = mock(Context.class);
- mTestableContext.addMockUserContext(userId, mockUserContext);
-
- mA11yms.isAccessibilityTargetAllowed(fakePackageName, uid, userId);
-
- verify(mockUserContext).getSystemService(EnhancedConfirmationManager.class);
- }
-
- @Test
@EnableFlags(com.android.hardware.input.Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES)
public void handleKeyGestureEvent_toggleMagnifier() {
mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY);
diff --git a/services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java b/services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java
index ef9580c54de6..8d3eef4a3168 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java
@@ -38,6 +38,7 @@ import android.media.AudioManager;
import android.media.AudioSystem;
import android.media.IAudioDeviceVolumeDispatcher;
import android.media.VolumeInfo;
+import android.os.IpcDataCache;
import android.os.PermissionEnforcer;
import android.os.RemoteException;
import android.os.test.TestLooper;
@@ -83,6 +84,7 @@ public class AbsoluteVolumeBehaviorTest {
@Before
public void setUp() throws Exception {
+ IpcDataCache.disableForTestMode();
mTestLooper = new TestLooper();
mContext = spy(ApplicationProvider.getApplicationContext());
diff --git a/services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java b/services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java
index 746645a8c585..541dbba67957 100644
--- a/services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java
@@ -28,6 +28,7 @@ import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.media.IDeviceVolumeBehaviorDispatcher;
+import android.os.IpcDataCache;
import android.os.PermissionEnforcer;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
@@ -69,6 +70,7 @@ public class DeviceVolumeBehaviorTest {
@Before
public void setUp() throws Exception {
+ IpcDataCache.disableForTestMode();
mTestLooper = new TestLooper();
mContext = InstrumentationRegistry.getTargetContext();
mAudioSystem = new NoOpAudioSystemAdapter();
diff --git a/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java b/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java
index 6b41c434b80f..0bbae247d8bb 100644
--- a/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java
@@ -80,6 +80,7 @@ import android.media.AudioSystem;
import android.media.IDeviceVolumeBehaviorDispatcher;
import android.media.VolumeInfo;
import android.media.audiopolicy.AudioVolumeGroup;
+import android.os.IpcDataCache;
import android.os.Looper;
import android.os.PermissionEnforcer;
import android.os.test.TestLooper;
@@ -210,6 +211,8 @@ public class VolumeHelperTest {
@Before
public void setUp() throws Exception {
+ IpcDataCache.disableForTestMode();
+
mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
mTestLooper = new TestLooper();
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index 2f3bca031f46..fbb673a194b4 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -1762,7 +1762,8 @@ public final class DataManagerTest {
/* rankingAdjustment= */ 0,
/* isBubble= */ false,
/* proposedImportance= */ 0,
- /* sensitiveContent= */ false
+ /* sensitiveContent= */ false,
+ /* summarization = */ null
);
return true;
}).when(mRankingMap).getRanking(eq(key),
@@ -1806,7 +1807,8 @@ public final class DataManagerTest {
/* rankingAdjustment= */ 0,
/* isBubble= */ false,
/* proposedImportance= */ 0,
- /* sensitiveContent= */ false
+ /* sensitiveContent= */ false,
+ /* summarization = */ null
);
return true;
}).when(mRankingMap).getRanking(eq(CUSTOM_KEY),
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java
index 5c2132f0e0e9..1c0ee09ccc6f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java
@@ -336,7 +336,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
checkRequestPinShortcut(makeResultIntent());
}
- public void testRequestPinShortcut_explicitTargetActivity() {
+ public void disabled_testRequestPinShortcut_explicitTargetActivity() {
setDefaultLauncher(USER_10, LAUNCHER_1);
setDefaultLauncher(USER_11, LAUNCHER_2);
@@ -475,7 +475,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
}
- public void testRequestPinShortcut_dynamicExists() {
+ public void disabled_testRequestPinShortcut_dynamicExists() {
setDefaultLauncher(USER_10, LAUNCHER_1);
final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32);
@@ -590,7 +590,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
});
}
- public void testRequestPinShortcut_dynamicExists_alreadyPinned() {
+ public void disabled_testRequestPinShortcut_dynamicExists_alreadyPinned() {
setDefaultLauncher(USER_10, LAUNCHER_1);
final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32);
@@ -848,7 +848,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
});
}
- public void testRequestPinShortcut_dynamicExists_alreadyPinnedByAnother() {
+ public void disabled_testRequestPinShortcut_dynamicExists_alreadyPinnedByAnother() {
// Initially all launchers have the shortcut permission, until we call setDefaultLauncher().
runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
@@ -1041,7 +1041,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
/**
* When trying to pin an existing shortcut, the new fields shouldn't override existing fields.
*/
- public void testRequestPinShortcut_dynamicExists_titleWontChange() {
+ public void disabled_testRequestPinShortcut_dynamicExists_titleWontChange() {
setDefaultLauncher(USER_10, LAUNCHER_1);
final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32);
@@ -1173,7 +1173,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
* The dynamic shortcut existed, but before accepting(), it's removed. Because the request
* has a partial shortcut, accept() should fail.
*/
- public void testRequestPinShortcut_dynamicExists_thenRemoved_error() {
+ public void disabled_testRequestPinShortcut_dynamicExists_thenRemoved_error() {
setDefaultLauncher(USER_10, LAUNCHER_1);
runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
@@ -1231,7 +1231,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
* The dynamic shortcut existed, but before accepting(), it's removed. Because the request
* has all the mandatory fields, we can go ahead and still publish it.
*/
- public void testRequestPinShortcut_dynamicExists_thenRemoved_okay() {
+ public void disabled_testRequestPinShortcut_dynamicExists_thenRemoved_okay() {
setDefaultLauncher(USER_10, LAUNCHER_1);
runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
@@ -1404,7 +1404,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
* The dynamic shortcut existed, but before accepting(), it's removed. Because the request
* has a partial shortcut, accept() should fail.
*/
- public void testRequestPinShortcut_dynamicExists_thenDisabled_error() {
+ public void disabled_testRequestPinShortcut_dynamicExists_thenDisabled_error() {
setDefaultLauncher(USER_10, LAUNCHER_1);
runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
index 8fad01a7541d..2616ccbe474a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -247,7 +247,8 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {
getRankingAdjustment(i),
isBubble(i),
getProposedImportance(i),
- hasSensitiveContent(i)
+ hasSensitiveContent(i),
+ getSummarization(i)
);
rankings[i] = ranking;
}
@@ -383,6 +384,13 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {
return index % 3 == 0;
}
+ public static String getSummarization(int index) {
+ if ((android.app.Flags.nmSummarizationUi() || android.app.Flags.nmSummarization())) {
+ return "summary " + index;
+ }
+ return null;
+ }
+
private boolean isBubble(int index) {
return index % 4 == 0;
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index edfdb4ffec3a..a8fcadd9dd74 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -51,6 +51,7 @@ import static android.app.NotificationChannel.RECS_ID;
import static android.app.NotificationChannel.SOCIAL_MEDIA_ID;
import static android.app.NotificationChannel.USER_LOCKED_ALLOW_BUBBLE;
import static android.app.NotificationManager.ACTION_AUTOMATIC_ZEN_RULE_STATUS_CHANGED;
+import static android.app.NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED;
import static android.app.NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED;
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ACTIVATED;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
@@ -123,7 +124,9 @@ import static android.service.notification.Flags.FLAG_REDACT_SENSITIVE_NOTIFICAT
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ONGOING;
+import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS;
import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
+import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_LOCKDOWN;
@@ -163,6 +166,7 @@ import static junit.framework.Assert.fail;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyLong;
@@ -360,7 +364,6 @@ import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatcher;
-import org.mockito.ArgumentMatchers;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.Mockito;
@@ -368,6 +371,9 @@ import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
@@ -383,9 +389,6 @@ import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.function.Consumer;
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
-import platform.test.runner.parameterized.Parameters;
-
@SmallTest
@RunWith(ParameterizedAndroidJunit4.class)
@RunWithLooper
@@ -600,7 +603,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Parameters(name = "{0}")
public static List<FlagsParameterization> getParams() {
- return FlagsParameterization.allCombinationsOf();
+ return FlagsParameterization.allCombinationsOf(
+ FLAG_NOTIFICATION_CLASSIFICATION);
}
public NotificationManagerServiceTest(FlagsParameterization flags) {
@@ -10889,6 +10893,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
Bundle signals = new Bundle();
signals.putInt(KEY_IMPORTANCE, IMPORTANCE_LOW);
signals.putInt(KEY_USER_SENTIMENT, USER_SENTIMENT_NEGATIVE);
+ signals.putInt(KEY_TYPE, TYPE_PROMOTION);
Adjustment adjustment = new Adjustment(r.getSbn().getPackageName(), r.getKey(), signals,
"", r.getUser().getIdentifier());
@@ -11413,8 +11418,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
verify(mContext).sendBroadcastAsUser(eqIntent(expected), eq(UserHandle.of(mUserId)));
}
+ private static Intent isIntentWithAction(String wantedAction) {
+ return argThat(
+ intent -> intent != null && wantedAction.equals(intent.getAction())
+ );
+ }
+
private static Intent eqIntent(Intent wanted) {
- return ArgumentMatchers.argThat(
+ return argThat(
new ArgumentMatcher<Intent>() {
@Override
public boolean matches(Intent argument) {
@@ -17491,6 +17502,66 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ @EnableFlags({Flags.FLAG_NM_BINDER_PERF_THROTTLE_EFFECTS_SUPPRESSOR_BROADCAST,
+ Flags.FLAG_NM_BINDER_PERF_REDUCE_ZEN_BROADCASTS})
+ public void requestHintsFromListener_changingEffectsButNotSuppressor_noBroadcast()
+ throws Exception {
+ // Note that NM_BINDER_PERF_REDUCE_ZEN_BROADCASTS is not strictly necessary; however each
+ // path will do slightly different calls so we force one of them to simplify the test.
+ when(mUmInternal.getProfileIds(anyInt(), anyBoolean())).thenReturn(new int[]{mUserId});
+ when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
+ INotificationListener token = mock(INotificationListener.class);
+ mService.isSystemUid = true;
+
+ mBinderService.requestHintsFromListener(token, HINT_HOST_DISABLE_CALL_EFFECTS);
+ mTestableLooper.moveTimeForward(500); // more than ZEN_BROADCAST_DELAY
+ waitForIdle();
+
+ verify(mContext, times(1)).sendBroadcastMultiplePermissions(
+ isIntentWithAction(ACTION_EFFECTS_SUPPRESSOR_CHANGED), any(), any(), any());
+
+ // Same suppressor suppresses something else.
+ mBinderService.requestHintsFromListener(token, HINT_HOST_DISABLE_NOTIFICATION_EFFECTS);
+ mTestableLooper.moveTimeForward(500); // more than ZEN_BROADCAST_DELAY
+ waitForIdle();
+
+ // Still 1 total calls (the previous one).
+ verify(mContext, times(1)).sendBroadcastMultiplePermissions(
+ isIntentWithAction(ACTION_EFFECTS_SUPPRESSOR_CHANGED), any(), any(), any());
+ }
+
+ @Test
+ @EnableFlags({Flags.FLAG_NM_BINDER_PERF_THROTTLE_EFFECTS_SUPPRESSOR_BROADCAST,
+ Flags.FLAG_NM_BINDER_PERF_REDUCE_ZEN_BROADCASTS})
+ public void requestHintsFromListener_changingSuppressor_throttlesBroadcast() throws Exception {
+ // Note that NM_BINDER_PERF_REDUCE_ZEN_BROADCASTS is not strictly necessary; however each
+ // path will do slightly different calls so we force one of them to simplify the test.
+ when(mUmInternal.getProfileIds(anyInt(), anyBoolean())).thenReturn(new int[]{mUserId});
+ when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
+ INotificationListener token = mock(INotificationListener.class);
+ mService.isSystemUid = true;
+
+ // Several updates in quick succession.
+ mBinderService.requestHintsFromListener(token, HINT_HOST_DISABLE_CALL_EFFECTS);
+ mBinderService.clearRequestedListenerHints(token);
+ mBinderService.requestHintsFromListener(token, HINT_HOST_DISABLE_NOTIFICATION_EFFECTS);
+ mBinderService.clearRequestedListenerHints(token);
+ mBinderService.requestHintsFromListener(token, HINT_HOST_DISABLE_CALL_EFFECTS);
+ mBinderService.clearRequestedListenerHints(token);
+ mBinderService.requestHintsFromListener(token, HINT_HOST_DISABLE_NOTIFICATION_EFFECTS);
+
+ // No broadcasts yet!
+ verify(mContext, never()).sendBroadcastMultiplePermissions(any(), any(), any(), any());
+
+ mTestableLooper.moveTimeForward(500); // more than ZEN_BROADCAST_DELAY
+ waitForIdle();
+
+ // Only one broadcast after idle time.
+ verify(mContext, times(1)).sendBroadcastMultiplePermissions(
+ isIntentWithAction(ACTION_EFFECTS_SUPPRESSOR_CHANGED), any(), any(), any());
+ }
+
+ @Test
@EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
public void testApplyAdjustment_keyType_validType() throws Exception {
final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordExtractorDataTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordExtractorDataTest.java
index 9fe0e49c4ab8..09ebc23a1d7b 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordExtractorDataTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordExtractorDataTest.java
@@ -29,12 +29,19 @@ import android.os.UserHandle;
import android.service.notification.Adjustment;
import android.service.notification.StatusBarNotification;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+
import com.android.server.UiServiceTestCase;
+import org.junit.Rule;
import org.junit.Test;
public class NotificationRecordExtractorDataTest extends UiServiceTestCase {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Test
public void testHasDiffs_noDiffs() {
NotificationRecord r = generateRecord();
@@ -57,7 +64,8 @@ public class NotificationRecordExtractorDataTest extends UiServiceTestCase {
r.getRankingScore(),
r.isConversation(),
r.getProposedImportance(),
- r.hasSensitiveContent());
+ r.hasSensitiveContent(),
+ r.getSummarization());
assertFalse(extractorData.hasDiffForRankingLocked(r, 1));
assertFalse(extractorData.hasDiffForLoggingLocked(r, 1));
@@ -85,7 +93,8 @@ public class NotificationRecordExtractorDataTest extends UiServiceTestCase {
r.getRankingScore(),
r.isConversation(),
r.getProposedImportance(),
- r.hasSensitiveContent());
+ r.hasSensitiveContent(),
+ r.getSummarization());
Bundle signals = new Bundle();
signals.putInt(Adjustment.KEY_IMPORTANCE_PROPOSAL, IMPORTANCE_HIGH);
@@ -119,7 +128,8 @@ public class NotificationRecordExtractorDataTest extends UiServiceTestCase {
r.getRankingScore(),
r.isConversation(),
r.getProposedImportance(),
- r.hasSensitiveContent());
+ r.hasSensitiveContent(),
+ r.getSummarization());
Bundle signals = new Bundle();
signals.putString(Adjustment.KEY_GROUP_KEY, "ranker_group");
@@ -154,7 +164,8 @@ public class NotificationRecordExtractorDataTest extends UiServiceTestCase {
r.getRankingScore(),
r.isConversation(),
r.getProposedImportance(),
- r.hasSensitiveContent());
+ r.hasSensitiveContent(),
+ r.getSummarization());
Bundle signals = new Bundle();
signals.putBoolean(Adjustment.KEY_SENSITIVE_CONTENT, true);
@@ -166,6 +177,42 @@ public class NotificationRecordExtractorDataTest extends UiServiceTestCase {
assertTrue(extractorData.hasDiffForLoggingLocked(r, 1));
}
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_NM_SUMMARIZATION)
+ public void testHasDiffs_summarization() {
+ NotificationRecord r = generateRecord();
+
+ NotificationRecordExtractorData extractorData = new NotificationRecordExtractorData(
+ 1,
+ r.getPackageVisibilityOverride(),
+ r.canShowBadge(),
+ r.canBubble(),
+ r.getNotification().isBubbleNotification(),
+ r.getChannel(),
+ r.getGroupKey(),
+ r.getPeopleOverride(),
+ r.getSnoozeCriteria(),
+ r.getUserSentiment(),
+ r.getSuppressedVisualEffects(),
+ r.getSystemGeneratedSmartActions(),
+ r.getSmartReplies(),
+ r.getImportance(),
+ r.getRankingScore(),
+ r.isConversation(),
+ r.getProposedImportance(),
+ r.hasSensitiveContent(),
+ r.getSummarization());
+
+ Bundle signals = new Bundle();
+ signals.putString(Adjustment.KEY_SUMMARIZATION, "SUMMARIZED!");
+ Adjustment adjustment = new Adjustment("pkg", r.getKey(), signals, "", 0);
+ r.addAdjustment(adjustment);
+ r.applyAdjustments();
+
+ assertTrue(extractorData.hasDiffForRankingLocked(r, 1));
+ assertTrue(extractorData.hasDiffForLoggingLocked(r, 1));
+ }
+
private NotificationRecord generateRecord() {
NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW);
final Notification.Builder builder = new Notification.Builder(getContext())
diff --git a/services/tests/wmtests/src/com/android/server/policy/CombinationKeyTests.java b/services/tests/wmtests/src/com/android/server/policy/CombinationKeyTests.java
index 038e1357159b..036e03c60091 100644
--- a/services/tests/wmtests/src/com/android/server/policy/CombinationKeyTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/CombinationKeyTests.java
@@ -23,6 +23,7 @@ import static com.android.server.policy.PhoneWindowManager.POWER_VOLUME_UP_BEHAV
import static com.android.server.policy.PhoneWindowManager.POWER_VOLUME_UP_BEHAVIOR_MUTE;
import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.Presubmit;
import android.view.ViewConfiguration;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -38,6 +39,7 @@ import org.junit.runner.RunWith;
* Build/Install/Run:
* atest WmTests:CombinationKeyTests
*/
+@Presubmit
@MediumTest
@RunWith(AndroidJUnit4.class)
@DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER_MULTI_KEY_GESTURES)
diff --git a/services/tests/wmtests/src/com/android/server/policy/DeferredKeyActionExecutorTests.java b/services/tests/wmtests/src/com/android/server/policy/DeferredKeyActionExecutorTests.java
index ca3787ec4546..bccdd67d33ed 100644
--- a/services/tests/wmtests/src/com/android/server/policy/DeferredKeyActionExecutorTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/DeferredKeyActionExecutorTests.java
@@ -19,6 +19,7 @@ package com.android.server.policy;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import android.platform.test.annotations.Presubmit;
import android.view.KeyEvent;
import org.junit.Before;
@@ -29,6 +30,7 @@ import org.junit.Test;
*
* <p>Build/Install/Run: atest WmTests:DeferredKeyActionExecutorTests
*/
+@Presubmit
public final class DeferredKeyActionExecutorTests {
private DeferredKeyActionExecutor mKeyActionExecutor;
diff --git a/services/tests/wmtests/src/com/android/server/policy/KeyCombinationManagerTests.java b/services/tests/wmtests/src/com/android/server/policy/KeyCombinationManagerTests.java
index 8b5f68a1e974..a912c177c863 100644
--- a/services/tests/wmtests/src/com/android/server/policy/KeyCombinationManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/KeyCombinationManagerTests.java
@@ -29,6 +29,7 @@ import static org.testng.Assert.assertTrue;
import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
+import android.platform.test.annotations.Presubmit;
import android.view.KeyEvent;
import androidx.test.filters.SmallTest;
@@ -45,7 +46,7 @@ import java.util.concurrent.TimeUnit;
* Build/Install/Run:
* atest KeyCombinationManagerTests
*/
-
+@Presubmit
@SmallTest
public class KeyCombinationManagerTests {
private KeyCombinationManager mKeyCombinationManager;
diff --git a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
index 85ef466b2477..16c786b52655 100644
--- a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
@@ -544,17 +544,6 @@ public class KeyGestureEventTests extends ShortcutKeyTestBase {
}
@Test
- public void testKeyGestureSplitscreenFocus() {
- Assert.assertTrue(sendKeyGestureEventComplete(
- KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT));
- mPhoneWindowManager.assertSetSplitscreenFocus(true);
-
- Assert.assertTrue(sendKeyGestureEventComplete(
- KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT));
- mPhoneWindowManager.assertSetSplitscreenFocus(false);
- }
-
- @Test
public void testKeyGestureShortcutHelper() {
Assert.assertTrue(sendKeyGestureEventComplete(
KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER));
diff --git a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java
index 82a5add407f4..d961a6acc9c1 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java
@@ -42,6 +42,7 @@ import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
import android.platform.test.annotations.EnableFlags;
+import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
import android.view.KeyEvent;
import android.view.KeyboardShortcutGroup;
@@ -64,7 +65,7 @@ import java.util.Collections;
* Build/Install/Run:
* atest ModifierShortcutManagerTests
*/
-
+@Presubmit
@SmallTest
@EnableFlags(com.android.hardware.input.Flags.FLAG_MODIFIER_SHORTCUT_MANAGER_REFACTOR)
public class ModifierShortcutManagerTests {
@@ -127,7 +128,7 @@ public class ModifierShortcutManagerTests {
// Total valid shortcuts.
KeyboardShortcutGroup group =
mModifierShortcutManager.getApplicationLaunchKeyboardShortcuts(-1);
- assertEquals(13, group.getItems().size());
+ assertEquals(11, group.getItems().size());
// Total valid shift shortcuts.
assertEquals(3, group.getItems().stream()
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 af3dc1da4dcc..c73ce23fe6b5 100644
--- a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
@@ -48,6 +48,7 @@ import android.app.AppOpsManager;
import android.content.Context;
import android.hardware.input.InputManager;
import android.os.PowerManager;
+import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.filters.SmallTest;
@@ -72,6 +73,7 @@ import org.junit.Test;
* Build/Install/Run:
* atest WmTests:PhoneWindowManagerTests
*/
+@Presubmit
@SmallTest
public class PhoneWindowManagerTests {
diff --git a/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java
index 33ccec3f785a..53e82bad818d 100644
--- a/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java
@@ -28,6 +28,7 @@ import static com.android.server.policy.PhoneWindowManager.SHORT_PRESS_POWER_GO_
import static org.junit.Assert.assertEquals;
import android.platform.test.annotations.EnableFlags;
+import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
import android.view.Display;
@@ -40,6 +41,7 @@ import org.junit.Test;
* Build/Install/Run:
* atest WmTests:PowerKeyGestureTests
*/
+@Presubmit
public class PowerKeyGestureTests extends ShortcutKeyTestBase {
@Before
public void setUp() {
diff --git a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
index 08eb1451bd6f..6c6594220bad 100644
--- a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
@@ -39,6 +39,7 @@ import android.os.Process;
import android.os.SystemClock;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
+import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
import android.view.KeyEvent;
@@ -57,6 +58,7 @@ import java.util.concurrent.TimeUnit;
* Build/Install/Run:
* atest WmTests:SingleKeyGestureTests
*/
+@Presubmit
public class SingleKeyGestureTests {
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
diff --git a/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
index 3ea3235df0f4..833dd5d768e4 100644
--- a/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
@@ -35,6 +35,7 @@ import android.app.ActivityTaskManager.RootTaskInfo;
import android.content.ComponentName;
import android.hardware.input.KeyGestureEvent;
import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
import android.view.Display;
@@ -47,6 +48,7 @@ import org.junit.Test;
* Build/Install/Run:
* atest WmTests:StemKeyGestureTests
*/
+@Presubmit
public class StemKeyGestureTests extends ShortcutKeyTestBase {
private static final String TEST_TARGET_ACTIVITY = "com.android.server.policy/.TestActivity";
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index 4ff3d433632a..f88492477487 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -925,11 +925,6 @@ class TestPhoneWindowManager {
verify(mStatusBarManagerInternal).moveFocusedTaskToStageSplit(anyInt(), eq(leftOrTop));
}
- void assertSetSplitscreenFocus(boolean leftOrTop) {
- mTestLooper.dispatchAll();
- verify(mStatusBarManagerInternal).setSplitscreenFocus(eq(leftOrTop));
- }
-
void assertStatusBarStartAssist() {
mTestLooper.dispatchAll();
verify(mStatusBarManagerInternal).startAssist(any());
diff --git a/services/tests/wmtests/src/com/android/server/policy/WindowWakeUpPolicyTests.java b/services/tests/wmtests/src/com/android/server/policy/WindowWakeUpPolicyTests.java
index 3ca352cfa60d..9e59bced01f1 100644
--- a/services/tests/wmtests/src/com/android/server/policy/WindowWakeUpPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/WindowWakeUpPolicyTests.java
@@ -57,6 +57,7 @@ import android.content.res.Resources;
import android.os.PowerManager;
import android.platform.test.annotations.DisableFlags;
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.Display;
@@ -83,6 +84,7 @@ import java.util.function.BooleanSupplier;
*
* <p>Build/Install/Run: atest WmTests:WindowWakeUpPolicyTests
*/
+@Presubmit
public final class WindowWakeUpPolicyTests {
@Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
@Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
diff --git a/services/usb/OWNERS b/services/usb/OWNERS
index 2dff392d4e34..259261252032 100644
--- a/services/usb/OWNERS
+++ b/services/usb/OWNERS
@@ -1,9 +1,9 @@
-anothermark@google.com
+vmartensson@google.com
+nkapron@google.com
febinthattil@google.com
-aprasath@google.com
+shubhankarm@google.com
badhri@google.com
elaurent@google.com
albertccwang@google.com
jameswei@google.com
howardyen@google.com
-kumarashishg@google.com \ No newline at end of file
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index b32379ae4b1e..4d9df4666016 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -415,4 +415,5 @@ interface ITelecomService {
*/
boolean hasForegroundServiceDelegation(in PhoneAccountHandle phoneAccountHandle,
String callingPackage);
+ void setMetricsTestMode(boolean enabled);
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/RecentTasksUtils.kt
index aa262f9dfd6a..1a5fda7c8324 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/RecentTasksUtils.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.utils
+package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
@@ -24,4 +24,4 @@ object RecentTasksUtils {
"dumpsys activity service SystemUIService WMShell recents clearAll"
)
}
-} \ No newline at end of file
+}
diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
index e0532633d40b..eef4e6f58463 100644
--- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
@@ -467,30 +467,6 @@ class KeyGestureControllerTests {
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
),
TestData(
- "CTRL + ALT + DPAD_LEFT -> Change Splitscreen Focus Left",
- intArrayOf(
- KeyEvent.KEYCODE_CTRL_LEFT,
- KeyEvent.KEYCODE_ALT_LEFT,
- KeyEvent.KEYCODE_DPAD_LEFT
- ),
- KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT,
- intArrayOf(KeyEvent.KEYCODE_DPAD_LEFT),
- KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
- ),
- TestData(
- "CTRL + ALT + DPAD_RIGHT -> Change Splitscreen Focus Right",
- intArrayOf(
- KeyEvent.KEYCODE_CTRL_LEFT,
- KeyEvent.KEYCODE_ALT_LEFT,
- KeyEvent.KEYCODE_DPAD_RIGHT
- ),
- KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT,
- intArrayOf(KeyEvent.KEYCODE_DPAD_RIGHT),
- KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
- ),
- TestData(
"META + / -> Open Shortcut Helper",
intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_SLASH),
KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER,